blob: e80e3a6f93ec8accc42451d26271397209b039ff [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
17package android.net;
18
Artur Satayev26958002019-12-10 17:47:52 +000019import android.compat.annotation.UnsupportedAppUsage;
Elliott Hughes34385d32014-04-28 11:11:32 -070020import android.system.ErrnoException;
Tobias Thierer99a1d452017-10-30 20:21:38 +000021import android.system.Int32Ref;
Elliott Hughes34385d32014-04-28 11:11:32 -070022import android.system.Os;
23import android.system.OsConstants;
Neil Fullerc80af6d2015-07-03 10:59:17 +010024import android.system.StructLinger;
25import android.system.StructTimeval;
Tobias Thierer99a1d452017-10-30 20:21:38 +000026
27import java.io.FileDescriptor;
28import java.io.IOException;
29import java.io.InputStream;
30import java.io.OutputStream;
31import java.net.SocketOptions;
Mike Lockwoode7d309a2013-07-16 11:36:22 -070032
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033/**
34 * Socket implementation used for android.net.LocalSocket and
35 * android.net.LocalServerSocket. Supports only AF_LOCAL sockets.
36 */
37class LocalSocketImpl
38{
39 private SocketInputStream fis;
40 private SocketOutputStream fos;
41 private Object readMonitor = new Object();
42 private Object writeMonitor = new Object();
43
44 /** null if closed or not yet created */
45 private FileDescriptor fd;
Zhihai Xu118c8512013-07-25 13:44:50 -070046 /** whether fd is created internally */
47 private boolean mFdCreatedInternally;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49 // These fields are accessed by native code;
50 /** file descriptor array received during a previous read */
Mathew Inwoodfa3a7462018-08-08 14:52:47 +010051 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 FileDescriptor[] inboundFileDescriptors;
53 /** file descriptor array that should be written during next write */
Mathew Inwoodfa3a7462018-08-08 14:52:47 +010054 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 FileDescriptor[] outboundFileDescriptors;
56
57 /**
58 * An input stream for local sockets. Needed because we may
59 * need to read ancillary data.
60 */
61 class SocketInputStream extends InputStream {
62 /** {@inheritDoc} */
63 @Override
64 public int available() throws IOException {
Ian McKellar54d50082014-04-11 15:41:00 -070065 FileDescriptor myFd = fd;
66 if (myFd == null) throw new IOException("socket closed");
67
Tobias Thierer99a1d452017-10-30 20:21:38 +000068 Int32Ref avail = new Int32Ref(0);
Neil Fullerd2df87e2015-07-03 15:06:23 +010069 try {
70 Os.ioctlInt(myFd, OsConstants.FIONREAD, avail);
71 } catch (ErrnoException e) {
72 throw e.rethrowAsIOException();
73 }
74 return avail.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 }
76
77 /** {@inheritDoc} */
78 @Override
79 public void close() throws IOException {
80 LocalSocketImpl.this.close();
81 }
82
83 /** {@inheritDoc} */
84 @Override
85 public int read() throws IOException {
86 int ret;
87 synchronized (readMonitor) {
88 FileDescriptor myFd = fd;
89 if (myFd == null) throw new IOException("socket closed");
90
91 ret = read_native(myFd);
92 return ret;
93 }
94 }
95
96 /** {@inheritDoc} */
97 @Override
98 public int read(byte[] b) throws IOException {
99 return read(b, 0, b.length);
100 }
101
102 /** {@inheritDoc} */
103 @Override
104 public int read(byte[] b, int off, int len) throws IOException {
105 synchronized (readMonitor) {
106 FileDescriptor myFd = fd;
107 if (myFd == null) throw new IOException("socket closed");
108
109 if (off < 0 || len < 0 || (off + len) > b.length ) {
110 throw new ArrayIndexOutOfBoundsException();
111 }
112
113 int ret = readba_native(b, off, len, myFd);
114
115 return ret;
116 }
117 }
118 }
119
120 /**
121 * An output stream for local sockets. Needed because we may
122 * need to read ancillary data.
123 */
124 class SocketOutputStream extends OutputStream {
125 /** {@inheritDoc} */
126 @Override
127 public void close() throws IOException {
128 LocalSocketImpl.this.close();
129 }
130
131 /** {@inheritDoc} */
132 @Override
133 public void write (byte[] b) throws IOException {
134 write(b, 0, b.length);
135 }
136
137 /** {@inheritDoc} */
138 @Override
139 public void write (byte[] b, int off, int len) throws IOException {
140 synchronized (writeMonitor) {
141 FileDescriptor myFd = fd;
142 if (myFd == null) throw new IOException("socket closed");
143
144 if (off < 0 || len < 0 || (off + len) > b.length ) {
145 throw new ArrayIndexOutOfBoundsException();
146 }
147 writeba_native(b, off, len, myFd);
148 }
149 }
150
151 /** {@inheritDoc} */
152 @Override
153 public void write (int b) throws IOException {
154 synchronized (writeMonitor) {
155 FileDescriptor myFd = fd;
156 if (myFd == null) throw new IOException("socket closed");
157 write_native(b, myFd);
158 }
159 }
160 }
161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 private native int read_native(FileDescriptor fd) throws IOException;
163 private native int readba_native(byte[] b, int off, int len,
164 FileDescriptor fd) throws IOException;
165 private native void writeba_native(byte[] b, int off, int len,
166 FileDescriptor fd) throws IOException;
167 private native void write_native(int b, FileDescriptor fd)
168 throws IOException;
169 private native void connectLocal(FileDescriptor fd, String name,
170 int namespace) throws IOException;
171 private native void bindLocal(FileDescriptor fd, String name, int namespace)
172 throws IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 private native Credentials getPeerCredentials_native(
174 FileDescriptor fd) throws IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175
176 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 * Create a new instance.
178 */
Mathew Inwoodfa3a7462018-08-08 14:52:47 +0100179 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 /*package*/ LocalSocketImpl()
181 {
182 }
183
184 /**
185 * Create a new instance from a file descriptor representing
186 * a bound socket. The state of the file descriptor is not checked here
187 * but the caller can verify socket state by calling listen().
188 *
189 * @param fd non-null; bound file descriptor
190 */
Neil Fuller7fd72462017-01-06 11:29:15 +0000191 /*package*/ LocalSocketImpl(FileDescriptor fd)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 {
193 this.fd = fd;
194 }
195
196 public String toString() {
197 return super.toString() + " fd:" + fd;
198 }
199
200 /**
201 * Creates a socket in the underlying OS.
202 *
Mike Lockwoode7d309a2013-07-16 11:36:22 -0700203 * @param sockType either {@link LocalSocket#SOCKET_DGRAM}, {@link LocalSocket#SOCKET_STREAM}
204 * or {@link LocalSocket#SOCKET_SEQPACKET}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 * @throws IOException
206 */
Neil Fullerc80af6d2015-07-03 10:59:17 +0100207 public void create(int sockType) throws IOException {
Neil Fullerb08c7bc2017-01-04 10:07:25 +0000208 if (fd != null) {
209 throw new IOException("LocalSocketImpl already has an fd");
210 }
211
212 int osType;
213 switch (sockType) {
214 case LocalSocket.SOCKET_DGRAM:
215 osType = OsConstants.SOCK_DGRAM;
216 break;
217 case LocalSocket.SOCKET_STREAM:
218 osType = OsConstants.SOCK_STREAM;
219 break;
220 case LocalSocket.SOCKET_SEQPACKET:
221 osType = OsConstants.SOCK_SEQPACKET;
222 break;
223 default:
224 throw new IllegalStateException("unknown sockType");
225 }
226 try {
227 fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
228 mFdCreatedInternally = true;
229 } catch (ErrnoException e) {
230 e.rethrowAsIOException();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 }
232 }
233
234 /**
235 * Closes the socket.
236 *
237 * @throws IOException
238 */
239 public void close() throws IOException {
240 synchronized (LocalSocketImpl.this) {
Zhihai Xu118c8512013-07-25 13:44:50 -0700241 if ((fd == null) || (mFdCreatedInternally == false)) {
242 fd = null;
243 return;
244 }
Mike Lockwoode7d309a2013-07-16 11:36:22 -0700245 try {
Elliott Hughes34385d32014-04-28 11:11:32 -0700246 Os.close(fd);
Mike Lockwoode7d309a2013-07-16 11:36:22 -0700247 } catch (ErrnoException e) {
248 e.rethrowAsIOException();
249 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 fd = null;
251 }
252 }
253
254 /** note timeout presently ignored */
255 protected void connect(LocalSocketAddress address, int timeout)
256 throws IOException
257 {
258 if (fd == null) {
259 throw new IOException("socket not created");
260 }
261
262 connectLocal(fd, address.getName(), address.getNamespace().getId());
263 }
264
265 /**
266 * Binds this socket to an endpoint name. May only be called on an instance
267 * that has not yet been bound.
268 *
269 * @param endpoint endpoint address
270 * @throws IOException
271 */
272 public void bind(LocalSocketAddress endpoint) throws IOException
273 {
274 if (fd == null) {
275 throw new IOException("socket not created");
276 }
277
278 bindLocal(fd, endpoint.getName(), endpoint.getNamespace().getId());
279 }
280
281 protected void listen(int backlog) throws IOException
282 {
283 if (fd == null) {
284 throw new IOException("socket not created");
285 }
Neil Fuller4fa438e2015-07-03 17:58:00 +0100286 try {
287 Os.listen(fd, backlog);
288 } catch (ErrnoException e) {
289 throw e.rethrowAsIOException();
290 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 }
292
293 /**
294 * Accepts a new connection to the socket. Blocks until a new
295 * connection arrives.
296 *
297 * @param s a socket that will be used to represent the new connection.
298 * @throws IOException
299 */
Neil Fullerc1eaeb92015-07-08 11:33:54 +0100300 protected void accept(LocalSocketImpl s) throws IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 if (fd == null) {
302 throw new IOException("socket not created");
303 }
304
Neil Fullerc1eaeb92015-07-08 11:33:54 +0100305 try {
306 s.fd = Os.accept(fd, null /* address */);
307 s.mFdCreatedInternally = true;
308 } catch (ErrnoException e) {
309 throw e.rethrowAsIOException();
310 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 }
312
313 /**
314 * Retrieves the input stream for this instance.
315 *
316 * @return input stream
317 * @throws IOException if socket has been closed or cannot be created.
318 */
319 protected InputStream getInputStream() throws IOException
320 {
321 if (fd == null) {
322 throw new IOException("socket not created");
323 }
324
325 synchronized (this) {
326 if (fis == null) {
327 fis = new SocketInputStream();
328 }
329
330 return fis;
331 }
332 }
333
334 /**
335 * Retrieves the output stream for this instance.
336 *
337 * @return output stream
338 * @throws IOException if socket has been closed or cannot be created.
339 */
340 protected OutputStream getOutputStream() throws IOException
341 {
342 if (fd == null) {
343 throw new IOException("socket not created");
344 }
345
346 synchronized (this) {
347 if (fos == null) {
348 fos = new SocketOutputStream();
349 }
350
351 return fos;
352 }
353 }
354
355 /**
356 * Returns the number of bytes available for reading without blocking.
357 *
358 * @return >= 0 count bytes available
359 * @throws IOException
360 */
361 protected int available() throws IOException
362 {
363 return getInputStream().available();
364 }
365
366 /**
367 * Shuts down the input side of the socket.
368 *
369 * @throws IOException
370 */
371 protected void shutdownInput() throws IOException
372 {
373 if (fd == null) {
374 throw new IOException("socket not created");
375 }
376
Neil Fullera7f7c242015-07-08 12:57:15 +0100377 try {
378 Os.shutdown(fd, OsConstants.SHUT_RD);
379 } catch (ErrnoException e) {
380 throw e.rethrowAsIOException();
381 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 }
383
384 /**
385 * Shuts down the output side of the socket.
386 *
387 * @throws IOException
388 */
389 protected void shutdownOutput() throws IOException
390 {
391 if (fd == null) {
392 throw new IOException("socket not created");
393 }
394
Neil Fullera7f7c242015-07-08 12:57:15 +0100395 try {
396 Os.shutdown(fd, OsConstants.SHUT_WR);
397 } catch (ErrnoException e) {
398 throw e.rethrowAsIOException();
399 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 }
401
402 protected FileDescriptor getFileDescriptor()
403 {
404 return fd;
405 }
406
407 protected boolean supportsUrgentData()
408 {
409 return false;
410 }
411
412 protected void sendUrgentData(int data) throws IOException
413 {
414 throw new RuntimeException ("not impled");
415 }
416
417 public Object getOption(int optID) throws IOException
418 {
419 if (fd == null) {
420 throw new IOException("socket not created");
421 }
422
Neil Fullerc80af6d2015-07-03 10:59:17 +0100423 try {
424 Object toReturn;
425 switch (optID) {
426 case SocketOptions.SO_TIMEOUT:
427 StructTimeval timeval = Os.getsockoptTimeval(fd, OsConstants.SOL_SOCKET,
428 OsConstants.SO_SNDTIMEO);
429 toReturn = (int) timeval.toMillis();
430 break;
431 case SocketOptions.SO_RCVBUF:
432 case SocketOptions.SO_SNDBUF:
433 case SocketOptions.SO_REUSEADDR:
434 int osOpt = javaSoToOsOpt(optID);
435 toReturn = Os.getsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt);
436 break;
437 case SocketOptions.SO_LINGER:
438 StructLinger linger=
439 Os.getsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER);
440 if (!linger.isOn()) {
441 toReturn = -1;
442 } else {
443 toReturn = linger.l_linger;
444 }
445 break;
446 case SocketOptions.TCP_NODELAY:
447 toReturn = Os.getsockoptInt(fd, OsConstants.IPPROTO_TCP,
448 OsConstants.TCP_NODELAY);
449 break;
450 default:
451 throw new IOException("Unknown option: " + optID);
452 }
453 return toReturn;
454 } catch (ErrnoException e) {
455 throw e.rethrowAsIOException();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 }
457 }
458
459 public void setOption(int optID, Object value)
460 throws IOException {
Neil Fullerc80af6d2015-07-03 10:59:17 +0100461
462 if (fd == null) {
463 throw new IOException("socket not created");
464 }
465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 /*
467 * Boolean.FALSE is used to disable some options, so it
468 * is important to distinguish between FALSE and unset.
469 * We define it here that -1 is unset, 0 is FALSE, and 1
470 * is TRUE.
471 */
472 int boolValue = -1;
473 int intValue = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 if (value instanceof Integer) {
475 intValue = (Integer)value;
476 } else if (value instanceof Boolean) {
477 boolValue = ((Boolean) value)? 1 : 0;
478 } else {
479 throw new IOException("bad value: " + value);
480 }
481
Neil Fullerc80af6d2015-07-03 10:59:17 +0100482 try {
483 switch (optID) {
484 case SocketOptions.SO_LINGER:
485 StructLinger linger = new StructLinger(boolValue, intValue);
486 Os.setsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER, linger);
487 break;
488 case SocketOptions.SO_TIMEOUT:
Neil Fullera8280a52016-08-31 11:32:17 +0100489 // The option must set both send and receive timeouts.
490 // Note: The incoming timeout value is in milliseconds.
Neil Fullerc80af6d2015-07-03 10:59:17 +0100491 StructTimeval timeval = StructTimeval.fromMillis(intValue);
Neil Fullera8280a52016-08-31 11:32:17 +0100492 Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
493 timeval);
Neil Fullerc80af6d2015-07-03 10:59:17 +0100494 Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
495 timeval);
496 break;
497 case SocketOptions.SO_RCVBUF:
498 case SocketOptions.SO_SNDBUF:
499 case SocketOptions.SO_REUSEADDR:
500 int osOpt = javaSoToOsOpt(optID);
501 Os.setsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt, intValue);
502 break;
503 case SocketOptions.TCP_NODELAY:
504 Os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_NODELAY,
505 intValue);
506 break;
507 default:
508 throw new IOException("Unknown option: " + optID);
509 }
510 } catch (ErrnoException e) {
511 throw e.rethrowAsIOException();
512 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 }
514
515 /**
516 * Enqueues a set of file descriptors to send to the peer. The queue
517 * is one deep. The file descriptors will be sent with the next write
518 * of normal data, and will be delivered in a single ancillary message.
519 * See "man 7 unix" SCM_RIGHTS on a desktop Linux machine.
520 *
521 * @param fds non-null; file descriptors to send.
522 * @throws IOException
523 */
524 public void setFileDescriptorsForSend(FileDescriptor[] fds) {
525 synchronized(writeMonitor) {
526 outboundFileDescriptors = fds;
527 }
528 }
529
530 /**
531 * Retrieves a set of file descriptors that a peer has sent through
532 * an ancillary message. This method retrieves the most recent set sent,
533 * and then returns null until a new set arrives.
534 * File descriptors may only be passed along with regular data, so this
535 * method can only return a non-null after a read operation.
536 *
537 * @return null or file descriptor array
538 * @throws IOException
539 */
540 public FileDescriptor[] getAncillaryFileDescriptors() throws IOException {
541 synchronized(readMonitor) {
542 FileDescriptor[] result = inboundFileDescriptors;
543
544 inboundFileDescriptors = null;
545 return result;
546 }
547 }
548
549 /**
550 * Retrieves the credentials of this socket's peer. Only valid on
551 * connected sockets.
552 *
553 * @return non-null; peer credentials
554 * @throws IOException
555 */
Neil Fullerc80af6d2015-07-03 10:59:17 +0100556 public Credentials getPeerCredentials() throws IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 return getPeerCredentials_native(fd);
558 }
559
560 /**
561 * Retrieves the socket name from the OS.
562 *
563 * @return non-null; socket name
564 * @throws IOException on failure
565 */
Neil Fullerc80af6d2015-07-03 10:59:17 +0100566 public LocalSocketAddress getSockAddress() throws IOException {
567 // This method has never been implemented.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 }
570
571 @Override
572 protected void finalize() throws IOException {
573 close();
574 }
Neil Fullerc80af6d2015-07-03 10:59:17 +0100575
576 private static int javaSoToOsOpt(int optID) {
577 switch (optID) {
578 case SocketOptions.SO_SNDBUF:
579 return OsConstants.SO_SNDBUF;
580 case SocketOptions.SO_RCVBUF:
581 return OsConstants.SO_RCVBUF;
582 case SocketOptions.SO_REUSEADDR:
583 return OsConstants.SO_REUSEADDR;
584 default:
585 throw new UnsupportedOperationException("Unknown option: " + optID);
586 }
587 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588}