1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.system;
18 
19 import java.net.SocketAddress;
20 import java.nio.charset.StandardCharsets;
21 import java.util.Arrays;
22 
23 /**
24  * A UNIX-domain (AF_UNIX / AF_LOCAL) socket address.
25  *
26  * @hide
27  */
28 @libcore.api.CorePlatformApi
29 public final class UnixSocketAddress extends SocketAddress {
30 
31     private static final int NAMED_PATH_LENGTH = OsConstants.UNIX_PATH_MAX;
32     private static final byte[] UNNAMED_PATH = new byte[0];
33 
34     // See unix(7): Three types of UnixSocketAddress:
35     // 1) pathname: 0 < sun_path.length <= NAMED_PATH_LENGTH, sun_path[0] != 0.
36     // 2) unnamed: sun_path = [].
37     // 3) abstract: 0 < sun_path.length <= NAMED_PATH_LENGTH, sun_path[0] == 0.
38     // Note that the array referenced by this field can be modified from JNI (libcore_io_Linux.cpp).
39     private final byte[] sun_path;
40 
41     /** This constructor is also used from JNI. */
UnixSocketAddress(byte[] sun_path)42     private UnixSocketAddress(byte[] sun_path) {
43         if (sun_path == null) {
44             throw new IllegalArgumentException("sun_path must not be null");
45         }
46         if (sun_path.length > NAMED_PATH_LENGTH) {
47             throw new IllegalArgumentException("sun_path exceeds the maximum length");
48         }
49 
50         if (sun_path.length == 0) {
51             this.sun_path = UNNAMED_PATH;
52         } else {
53             this.sun_path = new byte[sun_path.length];
54             System.arraycopy(sun_path, 0, this.sun_path, 0, sun_path.length);
55         }
56     }
57 
58     /**
59      * Creates a named, abstract AF_UNIX socket address.
60      *
61      * @throws NullPointerException if {@code name} is null
62      * @throws IllegalArgumentException if {@code name} is invalid, e.g. too long
63      */
createAbstract(String name)64     public static UnixSocketAddress createAbstract(String name) {
65         byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
66         // Abstract sockets have a path that starts with (byte) 0.
67         byte[] path = new byte[nameBytes.length + 1];
68         System.arraycopy(nameBytes, 0, path, 1, nameBytes.length);
69         return new UnixSocketAddress(path);
70     }
71 
72     /**
73      * Creates a named, filesystem AF_UNIX socket address.
74      *
75      * @throws NullPointerException if {@code name} is null
76      * @throws IllegalArgumentException if {@code name} is invalid, e.g. too long
77      */
78     @libcore.api.CorePlatformApi
createFileSystem(String pathName)79     public static UnixSocketAddress createFileSystem(String pathName) {
80         byte[] pathNameBytes = pathName.getBytes(StandardCharsets.UTF_8);
81         // File system sockets have a path that ends with (byte) 0.
82         byte[] path = new byte[pathNameBytes.length + 1];
83         System.arraycopy(pathNameBytes, 0, path, 0, pathNameBytes.length);
84         return new UnixSocketAddress(path);
85     }
86 
87     /**
88      * Creates an unnamed, filesystem AF_UNIX socket address.
89      */
createUnnamed()90     public static UnixSocketAddress createUnnamed() {
91         return new UnixSocketAddress(UNNAMED_PATH);
92     }
93 
94     /** Used for testing. */
getSunPath()95     public byte[] getSunPath() {
96         if (sun_path.length == 0) {
97             return sun_path;
98         }
99         byte[] sunPathCopy = new byte[sun_path.length];
100         System.arraycopy(sun_path, 0, sunPathCopy, 0, sun_path.length);
101         return sunPathCopy;
102     }
103 
104     @Override
equals(Object o)105     public boolean equals(Object o) {
106         if (this == o) {
107             return true;
108         }
109         if (o == null || getClass() != o.getClass()) {
110             return false;
111         }
112 
113         UnixSocketAddress that = (UnixSocketAddress) o;
114         return Arrays.equals(sun_path, that.sun_path);
115     }
116 
117     @Override
hashCode()118     public int hashCode() {
119         return Arrays.hashCode(sun_path);
120     }
121 
122     @Override
toString()123     public String toString() {
124         return "UnixSocketAddress[" +
125                 "sun_path=" + Arrays.toString(sun_path) +
126                 ']';
127     }
128 }
129