1 /*
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 
17 package android.net;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.system.ErrnoException;
21 import android.system.Int32Ref;
22 import android.system.Os;
23 import android.system.OsConstants;
24 import android.system.StructLinger;
25 import android.system.StructTimeval;
26 
27 import java.io.FileDescriptor;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.net.SocketOptions;
32 
33 /**
34  * Socket implementation used for android.net.LocalSocket and
35  * android.net.LocalServerSocket. Supports only AF_LOCAL sockets.
36  */
37 class 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;
46     /** whether fd is created internally */
47     private boolean mFdCreatedInternally;
48 
49     // These fields are accessed by native code;
50     /** file descriptor array received during a previous read */
51     @UnsupportedAppUsage
52     FileDescriptor[] inboundFileDescriptors;
53     /** file descriptor array that should be written during next write */
54     @UnsupportedAppUsage
55     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
available()64         public int available() throws IOException {
65             FileDescriptor myFd = fd;
66             if (myFd == null) throw new IOException("socket closed");
67 
68             Int32Ref avail = new Int32Ref(0);
69             try {
70                 Os.ioctlInt(myFd, OsConstants.FIONREAD, avail);
71             } catch (ErrnoException e) {
72                 throw e.rethrowAsIOException();
73             }
74             return avail.value;
75         }
76 
77         /** {@inheritDoc} */
78         @Override
close()79         public void close() throws IOException {
80             LocalSocketImpl.this.close();
81         }
82 
83         /** {@inheritDoc} */
84         @Override
read()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
read(byte[] b)98         public int read(byte[] b) throws IOException {
99             return read(b, 0, b.length);
100         }
101 
102         /** {@inheritDoc} */
103         @Override
read(byte[] b, int off, int len)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
close()127         public void close() throws IOException {
128             LocalSocketImpl.this.close();
129         }
130 
131         /** {@inheritDoc} */
132         @Override
write(byte[] b)133         public void write (byte[] b) throws IOException {
134             write(b, 0, b.length);
135         }
136 
137         /** {@inheritDoc} */
138         @Override
write(byte[] b, int off, int len)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
write(int b)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 
read_native(FileDescriptor fd)162     private native int read_native(FileDescriptor fd) throws IOException;
readba_native(byte[] b, int off, int len, FileDescriptor fd)163     private native int readba_native(byte[] b, int off, int len,
164             FileDescriptor fd) throws IOException;
writeba_native(byte[] b, int off, int len, FileDescriptor fd)165     private native void writeba_native(byte[] b, int off, int len,
166             FileDescriptor fd) throws IOException;
write_native(int b, FileDescriptor fd)167     private native void write_native(int b, FileDescriptor fd)
168             throws IOException;
connectLocal(FileDescriptor fd, String name, int namespace)169     private native void connectLocal(FileDescriptor fd, String name,
170             int namespace) throws IOException;
bindLocal(FileDescriptor fd, String name, int namespace)171     private native void bindLocal(FileDescriptor fd, String name, int namespace)
172             throws IOException;
getPeerCredentials_native( FileDescriptor fd)173     private native Credentials getPeerCredentials_native(
174             FileDescriptor fd) throws IOException;
175 
176     /**
177      * Create a new instance.
178      */
179     @UnsupportedAppUsage
LocalSocketImpl()180     /*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      */
LocalSocketImpl(FileDescriptor fd)191     /*package*/ LocalSocketImpl(FileDescriptor fd)
192     {
193         this.fd = fd;
194     }
195 
toString()196     public String toString() {
197         return super.toString() + " fd:" + fd;
198     }
199 
200     /**
201      * Creates a socket in the underlying OS.
202      *
203      * @param sockType either {@link LocalSocket#SOCKET_DGRAM}, {@link LocalSocket#SOCKET_STREAM}
204      * or {@link LocalSocket#SOCKET_SEQPACKET}
205      * @throws IOException
206      */
create(int sockType)207     public void create(int sockType) throws IOException {
208         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();
231         }
232     }
233 
234     /**
235      * Closes the socket.
236      *
237      * @throws IOException
238      */
close()239     public void close() throws IOException {
240         synchronized (LocalSocketImpl.this) {
241             if ((fd == null) || (mFdCreatedInternally == false)) {
242                 fd = null;
243                 return;
244             }
245             try {
246                 Os.close(fd);
247             } catch (ErrnoException e) {
248                 e.rethrowAsIOException();
249             }
250             fd = null;
251         }
252     }
253 
254     /** note timeout presently ignored */
connect(LocalSocketAddress address, int timeout)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      */
bind(LocalSocketAddress endpoint)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 
listen(int backlog)281     protected void listen(int backlog) throws IOException
282     {
283         if (fd == null) {
284             throw new IOException("socket not created");
285         }
286         try {
287             Os.listen(fd, backlog);
288         } catch (ErrnoException e) {
289             throw e.rethrowAsIOException();
290         }
291     }
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      */
accept(LocalSocketImpl s)300     protected void accept(LocalSocketImpl s) throws IOException {
301         if (fd == null) {
302             throw new IOException("socket not created");
303         }
304 
305         try {
306             s.fd = Os.accept(fd, null /* address */);
307             s.mFdCreatedInternally = true;
308         } catch (ErrnoException e) {
309             throw e.rethrowAsIOException();
310         }
311     }
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      */
getInputStream()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      */
getOutputStream()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      */
available()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      */
shutdownInput()371     protected void shutdownInput() throws IOException
372     {
373         if (fd == null) {
374             throw new IOException("socket not created");
375         }
376 
377         try {
378             Os.shutdown(fd, OsConstants.SHUT_RD);
379         } catch (ErrnoException e) {
380             throw e.rethrowAsIOException();
381         }
382     }
383 
384     /**
385      * Shuts down the output side of the socket.
386      *
387      * @throws IOException
388      */
shutdownOutput()389     protected void shutdownOutput() throws IOException
390     {
391         if (fd == null) {
392             throw new IOException("socket not created");
393         }
394 
395         try {
396             Os.shutdown(fd, OsConstants.SHUT_WR);
397         } catch (ErrnoException e) {
398             throw e.rethrowAsIOException();
399         }
400     }
401 
getFileDescriptor()402     protected FileDescriptor getFileDescriptor()
403     {
404         return fd;
405     }
406 
supportsUrgentData()407     protected boolean supportsUrgentData()
408     {
409         return false;
410     }
411 
sendUrgentData(int data)412     protected void sendUrgentData(int data) throws IOException
413     {
414         throw new RuntimeException ("not impled");
415     }
416 
getOption(int optID)417     public Object getOption(int optID) throws IOException
418     {
419         if (fd == null) {
420             throw new IOException("socket not created");
421         }
422 
423         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();
456         }
457     }
458 
setOption(int optID, Object value)459     public void setOption(int optID, Object value)
460             throws IOException {
461 
462         if (fd == null) {
463             throw new IOException("socket not created");
464         }
465 
466         /*
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;
474         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 
482         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:
489                     // The option must set both send and receive timeouts.
490                     // Note: The incoming timeout value is in milliseconds.
491                     StructTimeval timeval = StructTimeval.fromMillis(intValue);
492                     Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
493                             timeval);
494                     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         }
513     }
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      */
setFileDescriptorsForSend(FileDescriptor[] fds)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      */
getAncillaryFileDescriptors()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      */
getPeerCredentials()556     public Credentials getPeerCredentials() throws IOException {
557         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      */
getSockAddress()566     public LocalSocketAddress getSockAddress() throws IOException {
567         // This method has never been implemented.
568         return null;
569     }
570 
571     @Override
finalize()572     protected void finalize() throws IOException {
573         close();
574     }
575 
javaSoToOsOpt(int optID)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     }
588 }
589