1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix.rmi;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.ObjectInput;
23 import java.io.ObjectOutput;
24 import java.rmi.server.RMIClientSocketFactory;
25 import java.rmi.server.RMIServerSocketFactory;
26 import java.rmi.server.RMISocketFactory;
27 import java.util.HashMap;
28 import java.util.Map;
29 import java.util.Objects;
30
31 import org.newsclub.net.unix.AFSocket;
32 import org.newsclub.net.unix.AFSocketAddress;
33 import org.newsclub.net.unix.AFUNIXSocket;
34 import org.newsclub.net.unix.AFUNIXSocketAddress;
35 import org.newsclub.net.unix.AFUNIXSocketCredentials;
36 import org.newsclub.net.unix.HostAndPort;
37
38 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
39
40
41
42
43
44
45 public class AFUNIXRMISocketFactory extends AFRMISocketFactory {
46 private static final long serialVersionUID = 1L;
47
48 static final String DEFAULT_SOCKET_FILE_PREFIX = "";
49 static final String DEFAULT_SOCKET_FILE_SUFFIX = ".rmi";
50
51 private File socketDir;
52 private String socketPrefix;
53 private String socketSuffix;
54
55 private final transient Map<HostAndPort, AFUNIXSocketCredentials> credentials = new HashMap<>();
56
57
58
59
60
61
62 public AFUNIXRMISocketFactory() {
63 super();
64 }
65
66
67
68
69
70
71
72
73
74
75
76
77
78 @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
79 public AFUNIXRMISocketFactory(final AFNaming naming, final File socketDir,
80 final RMIClientSocketFactory defaultClientFactory,
81 final RMIServerSocketFactory defaultServerFactory, final String socketPrefix,
82 final String socketSuffix) {
83 super(naming, defaultClientFactory, defaultServerFactory);
84 Objects.requireNonNull(socketDir);
85 this.socketDir = socketDir;
86 this.socketPrefix = socketPrefix == null ? DEFAULT_SOCKET_FILE_PREFIX : socketPrefix;
87 this.socketSuffix = socketSuffix == null ? DEFAULT_SOCKET_FILE_SUFFIX : socketSuffix;
88 }
89
90
91
92
93
94
95
96
97
98 @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
99 public AFUNIXRMISocketFactory(AFNaming naming, File socketDir,
100 RMIClientSocketFactory defaultClientFactory, RMIServerSocketFactory defaultServerFactory) {
101 this(naming, socketDir, defaultClientFactory, defaultServerFactory, null, null);
102 }
103
104
105
106
107
108
109
110 @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
111 public AFUNIXRMISocketFactory(AFNaming naming, File socketDir) {
112 this(naming, socketDir, DefaultRMIClientSocketFactory.getInstance(),
113 DefaultRMIServerSocketFactory.getInstance());
114 }
115
116 @Override
117 @SuppressFBWarnings("PATH_TRAVERSAL_IN")
118 protected AFNaming readNamingInstance(ObjectInput in) throws IOException {
119 socketDir = new File(in.readUTF());
120 int port = in.readInt();
121 return AFUNIXNaming.getInstance(socketDir, port);
122 }
123
124 @Override
125 protected void writeNamingInstance(ObjectOutput out, AFNaming naming) throws IOException {
126 out.writeUTF(socketDir.getAbsolutePath());
127 out.writeInt(naming.getRegistryPort());
128 }
129
130 @Override
131 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
132 super.readExternal(in);
133
134 socketPrefix = in.readUTF();
135 socketSuffix = in.readUTF();
136 }
137
138 @Override
139 public void writeExternal(ObjectOutput out) throws IOException {
140 super.writeExternal(out);
141
142 out.writeUTF(socketPrefix);
143 out.writeUTF(socketSuffix);
144 }
145
146 @Override
147 public int hashCode() {
148 return socketDir == null ? System.identityHashCode(this) : socketDir.hashCode();
149 }
150
151 @Override
152 public boolean equals(Object other) {
153 if (!(other instanceof AFUNIXRMISocketFactory)) {
154 return false;
155 }
156 AFUNIXRMISocketFactory sf = (AFUNIXRMISocketFactory) other;
157 if (socketDir == null) {
158 return sf == this;
159 } else {
160 return socketDir.equals(sf.socketDir);
161 }
162 }
163
164
165
166
167
168
169 public File getSocketDir() {
170 return socketDir;
171 }
172
173 @SuppressFBWarnings("PATH_TRAVERSAL_IN")
174 File getFile(int port) {
175 if (isPlainFileSocket()) {
176 return getSocketDir();
177 } else {
178 Objects.requireNonNull(socketDir);
179 return new File(socketDir, socketPrefix + port + socketSuffix);
180 }
181 }
182
183 boolean hasSocketFile(int port) {
184 return getFile(port).exists();
185 }
186
187 private boolean isPlainFileSocket() {
188 return (getNaming().getRegistryPort() == RMIPorts.PLAIN_FILE_SOCKET);
189 }
190
191 @Override
192 protected AFUNIXSocketAddress newSocketAddress(int port) throws IOException {
193 return AFUNIXSocketAddress.of(getFile(port), port);
194 }
195
196 @Override
197 protected final AFSocket<?> newConnectedSocket(AFSocketAddress addr) throws IOException {
198 final AFUNIXSocket socket = ((AFUNIXSocketAddress) addr).newConnectedSocket();
199 AFUNIXSocketCredentials creds = socket.getPeerCredentials();
200
201 final HostAndPort hap = new HostAndPort(addr.getHostString(), addr.getPort());
202 synchronized (credentials) {
203 if (credentials.put(hap, creds) != null) {
204
205 }
206 }
207 socket.addCloseable(() -> {
208 synchronized (credentials) {
209 credentials.remove(hap);
210 }
211 });
212 return socket;
213 }
214
215 @Override
216 public String toString() {
217 return super.toString() +
218 "[path=" + socketDir +
219 (isPlainFileSocket() ? "" :
220 ";prefix=" + socketPrefix + ";suffix=" + socketSuffix) + "]";
221 }
222
223 @Override
224 public void close() throws IOException {
225 synchronized (credentials) {
226 credentials.clear();
227 }
228 super.close();
229 }
230
231 AFUNIXSocketCredentials peerCredentialsFor(RemotePeerInfo data) {
232 synchronized (credentials) {
233 return credentials.get(new HostAndPort(data.host, data.port));
234 }
235 }
236
237 @Override
238 boolean hasRegisteredPort(int port) {
239 return hasSocketFile(port);
240 }
241 }