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 java.io.IOException;
20 import java.io.OutputStream;
21 import java.io.InputStream;
22 import java.io.FileDescriptor;
23 import java.net.SocketOptions;
24 
25 import android.system.ErrnoException;
26 import android.system.Os;
27 import android.system.OsConstants;
28 import android.system.StructLinger;
29 import android.system.StructTimeval;
30 import android.util.MutableInt;
31 
32 /**
33  * Socket implementation used for android.net.LocalSocket and
34  * android.net.LocalServerSocket. Supports only AF_LOCAL sockets.
35  */
36 class LocalSocketImpl
37 {
38     private SocketInputStream fis;
39     private SocketOutputStream fos;
40     private Object readMonitor = new Object();
41     private Object writeMonitor = new Object();
42 
43     /** null if closed or not yet created */
44     private FileDescriptor fd;
45     /** whether fd is created internally */
46     private boolean mFdCreatedInternally;
47 
48     // These fields are accessed by native code;
49     /** file descriptor array received during a previous read */
50     FileDescriptor[] inboundFileDescriptors;
51     /** file descriptor array that should be written during next write */
52     FileDescriptor[] outboundFileDescriptors;
53 
54     /**
55      * An input stream for local sockets. Needed because we may
56      * need to read ancillary data.
57      */
58     class SocketInputStream extends InputStream {
59         /** {@inheritDoc} */
60         @Override
available()61         public int available() throws IOException {
62             FileDescriptor myFd = fd;
63             if (myFd == null) throw new IOException("socket closed");
64 
65             MutableInt avail = new MutableInt(0);
66             try {
67                 Os.ioctlInt(myFd, OsConstants.FIONREAD, avail);
68             } catch (ErrnoException e) {
69                 throw e.rethrowAsIOException();
70             }
71             return avail.value;
72         }
73 
74         /** {@inheritDoc} */
75         @Override
close()76         public void close() throws IOException {
77             LocalSocketImpl.this.close();
78         }
79 
80         /** {@inheritDoc} */
81         @Override
read()82         public int read() throws IOException {
83             int ret;
84             synchronized (readMonitor) {
85                 FileDescriptor myFd = fd;
86                 if (myFd == null) throw new IOException("socket closed");
87 
88                 ret = read_native(myFd);
89                 return ret;
90             }
91         }
92 
93         /** {@inheritDoc} */
94         @Override
read(byte[] b)95         public int read(byte[] b) throws IOException {
96             return read(b, 0, b.length);
97         }
98 
99         /** {@inheritDoc} */
100         @Override
read(byte[] b, int off, int len)101         public int read(byte[] b, int off, int len) throws IOException {
102             synchronized (readMonitor) {
103                 FileDescriptor myFd = fd;
104                 if (myFd == null) throw new IOException("socket closed");
105 
106                 if (off < 0 || len < 0 || (off + len) > b.length ) {
107                     throw new ArrayIndexOutOfBoundsException();
108                 }
109 
110                 int ret = readba_native(b, off, len, myFd);
111 
112                 return ret;
113             }
114         }
115     }
116 
117     /**
118      * An output stream for local sockets. Needed because we may
119      * need to read ancillary data.
120      */
121     class SocketOutputStream extends OutputStream {
122         /** {@inheritDoc} */
123         @Override
close()124         public void close() throws IOException {
125             LocalSocketImpl.this.close();
126         }
127 
128         /** {@inheritDoc} */
129         @Override
write(byte[] b)130         public void write (byte[] b) throws IOException {
131             write(b, 0, b.length);
132         }
133 
134         /** {@inheritDoc} */
135         @Override
write(byte[] b, int off, int len)136         public void write (byte[] b, int off, int len) throws IOException {
137             synchronized (writeMonitor) {
138                 FileDescriptor myFd = fd;
139                 if (myFd == null) throw new IOException("socket closed");
140 
141                 if (off < 0 || len < 0 || (off + len) > b.length ) {
142                     throw new ArrayIndexOutOfBoundsException();
143                 }
144                 writeba_native(b, off, len, myFd);
145             }
146         }
147 
148         /** {@inheritDoc} */
149         @Override
write(int b)150         public void write (int b) throws IOException {
151             synchronized (writeMonitor) {
152                 FileDescriptor myFd = fd;
153                 if (myFd == null) throw new IOException("socket closed");
154                 write_native(b, myFd);
155             }
156         }
157 
158         /**
159          * Wait until the data in sending queue is emptied. A polling version
160          * for flush implementation.
161          * @throws IOException
162          *             if an i/o error occurs.
163          */
164         @Override
flush()165         public void flush() throws IOException {
166             FileDescriptor myFd = fd;
167             if (myFd == null) throw new IOException("socket closed");
168 
169             // Loop until the output buffer is empty.
170             MutableInt pending = new MutableInt(0);
171             while (true) {
172                 try {
173                     // See linux/net/unix/af_unix.c
174                     Os.ioctlInt(myFd, OsConstants.TIOCOUTQ, pending);
175                 } catch (ErrnoException e) {
176                     throw e.rethrowAsIOException();
177                 }
178 
179                 if (pending.value <= 0) {
180                     // The output buffer is empty.
181                     break;
182                 }
183 
184                 try {
185                     Thread.sleep(10);
186                 } catch (InterruptedException ie) {
187                     break;
188                 }
189             }
190         }
191     }
192 
read_native(FileDescriptor fd)193     private native int read_native(FileDescriptor fd) throws IOException;
readba_native(byte[] b, int off, int len, FileDescriptor fd)194     private native int readba_native(byte[] b, int off, int len,
195             FileDescriptor fd) throws IOException;
writeba_native(byte[] b, int off, int len, FileDescriptor fd)196     private native void writeba_native(byte[] b, int off, int len,
197             FileDescriptor fd) throws IOException;
write_native(int b, FileDescriptor fd)198     private native void write_native(int b, FileDescriptor fd)
199             throws IOException;
connectLocal(FileDescriptor fd, String name, int namespace)200     private native void connectLocal(FileDescriptor fd, String name,
201             int namespace) throws IOException;
bindLocal(FileDescriptor fd, String name, int namespace)202     private native void bindLocal(FileDescriptor fd, String name, int namespace)
203             throws IOException;
getPeerCredentials_native( FileDescriptor fd)204     private native Credentials getPeerCredentials_native(
205             FileDescriptor fd) throws IOException;
206 
207     /**
208      * Create a new instance.
209      */
LocalSocketImpl()210     /*package*/ LocalSocketImpl()
211     {
212     }
213 
214     /**
215      * Create a new instance from a file descriptor representing
216      * a bound socket. The state of the file descriptor is not checked here
217      *  but the caller can verify socket state by calling listen().
218      *
219      * @param fd non-null; bound file descriptor
220      */
LocalSocketImpl(FileDescriptor fd)221     /*package*/ LocalSocketImpl(FileDescriptor fd) throws IOException
222     {
223         this.fd = fd;
224     }
225 
toString()226     public String toString() {
227         return super.toString() + " fd:" + fd;
228     }
229 
230     /**
231      * Creates a socket in the underlying OS.
232      *
233      * @param sockType either {@link LocalSocket#SOCKET_DGRAM}, {@link LocalSocket#SOCKET_STREAM}
234      * or {@link LocalSocket#SOCKET_SEQPACKET}
235      * @throws IOException
236      */
create(int sockType)237     public void create(int sockType) throws IOException {
238         // no error if socket already created
239         // need this for LocalServerSocket.accept()
240         if (fd == null) {
241             int osType;
242             switch (sockType) {
243                 case LocalSocket.SOCKET_DGRAM:
244                     osType = OsConstants.SOCK_DGRAM;
245                     break;
246                 case LocalSocket.SOCKET_STREAM:
247                     osType = OsConstants.SOCK_STREAM;
248                     break;
249                 case LocalSocket.SOCKET_SEQPACKET:
250                     osType = OsConstants.SOCK_SEQPACKET;
251                     break;
252                 default:
253                     throw new IllegalStateException("unknown sockType");
254             }
255             try {
256                 fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
257                 mFdCreatedInternally = true;
258             } catch (ErrnoException e) {
259                 e.rethrowAsIOException();
260             }
261         }
262     }
263 
264     /**
265      * Closes the socket.
266      *
267      * @throws IOException
268      */
close()269     public void close() throws IOException {
270         synchronized (LocalSocketImpl.this) {
271             if ((fd == null) || (mFdCreatedInternally == false)) {
272                 fd = null;
273                 return;
274             }
275             try {
276                 Os.close(fd);
277             } catch (ErrnoException e) {
278                 e.rethrowAsIOException();
279             }
280             fd = null;
281         }
282     }
283 
284     /** note timeout presently ignored */
connect(LocalSocketAddress address, int timeout)285     protected void connect(LocalSocketAddress address, int timeout)
286                         throws IOException
287     {
288         if (fd == null) {
289             throw new IOException("socket not created");
290         }
291 
292         connectLocal(fd, address.getName(), address.getNamespace().getId());
293     }
294 
295     /**
296      * Binds this socket to an endpoint name. May only be called on an instance
297      * that has not yet been bound.
298      *
299      * @param endpoint endpoint address
300      * @throws IOException
301      */
bind(LocalSocketAddress endpoint)302     public void bind(LocalSocketAddress endpoint) throws IOException
303     {
304         if (fd == null) {
305             throw new IOException("socket not created");
306         }
307 
308         bindLocal(fd, endpoint.getName(), endpoint.getNamespace().getId());
309     }
310 
listen(int backlog)311     protected void listen(int backlog) throws IOException
312     {
313         if (fd == null) {
314             throw new IOException("socket not created");
315         }
316         try {
317             Os.listen(fd, backlog);
318         } catch (ErrnoException e) {
319             throw e.rethrowAsIOException();
320         }
321     }
322 
323     /**
324      * Accepts a new connection to the socket. Blocks until a new
325      * connection arrives.
326      *
327      * @param s a socket that will be used to represent the new connection.
328      * @throws IOException
329      */
accept(LocalSocketImpl s)330     protected void accept(LocalSocketImpl s) throws IOException {
331         if (fd == null) {
332             throw new IOException("socket not created");
333         }
334 
335         try {
336             s.fd = Os.accept(fd, null /* address */);
337             s.mFdCreatedInternally = true;
338         } catch (ErrnoException e) {
339             throw e.rethrowAsIOException();
340         }
341     }
342 
343     /**
344      * Retrieves the input stream for this instance.
345      *
346      * @return input stream
347      * @throws IOException if socket has been closed or cannot be created.
348      */
getInputStream()349     protected InputStream getInputStream() throws IOException
350     {
351         if (fd == null) {
352             throw new IOException("socket not created");
353         }
354 
355         synchronized (this) {
356             if (fis == null) {
357                 fis = new SocketInputStream();
358             }
359 
360             return fis;
361         }
362     }
363 
364     /**
365      * Retrieves the output stream for this instance.
366      *
367      * @return output stream
368      * @throws IOException if socket has been closed or cannot be created.
369      */
getOutputStream()370     protected OutputStream getOutputStream() throws IOException
371     {
372         if (fd == null) {
373             throw new IOException("socket not created");
374         }
375 
376         synchronized (this) {
377             if (fos == null) {
378                 fos = new SocketOutputStream();
379             }
380 
381             return fos;
382         }
383     }
384 
385     /**
386      * Returns the number of bytes available for reading without blocking.
387      *
388      * @return >= 0 count bytes available
389      * @throws IOException
390      */
available()391     protected int available() throws IOException
392     {
393         return getInputStream().available();
394     }
395 
396     /**
397      * Shuts down the input side of the socket.
398      *
399      * @throws IOException
400      */
shutdownInput()401     protected void shutdownInput() throws IOException
402     {
403         if (fd == null) {
404             throw new IOException("socket not created");
405         }
406 
407         try {
408             Os.shutdown(fd, OsConstants.SHUT_RD);
409         } catch (ErrnoException e) {
410             throw e.rethrowAsIOException();
411         }
412     }
413 
414     /**
415      * Shuts down the output side of the socket.
416      *
417      * @throws IOException
418      */
shutdownOutput()419     protected void shutdownOutput() throws IOException
420     {
421         if (fd == null) {
422             throw new IOException("socket not created");
423         }
424 
425         try {
426             Os.shutdown(fd, OsConstants.SHUT_WR);
427         } catch (ErrnoException e) {
428             throw e.rethrowAsIOException();
429         }
430     }
431 
getFileDescriptor()432     protected FileDescriptor getFileDescriptor()
433     {
434         return fd;
435     }
436 
supportsUrgentData()437     protected boolean supportsUrgentData()
438     {
439         return false;
440     }
441 
sendUrgentData(int data)442     protected void sendUrgentData(int data) throws IOException
443     {
444         throw new RuntimeException ("not impled");
445     }
446 
getOption(int optID)447     public Object getOption(int optID) throws IOException
448     {
449         if (fd == null) {
450             throw new IOException("socket not created");
451         }
452 
453         try {
454             Object toReturn;
455             switch (optID) {
456                 case SocketOptions.SO_TIMEOUT:
457                     StructTimeval timeval = Os.getsockoptTimeval(fd, OsConstants.SOL_SOCKET,
458                             OsConstants.SO_SNDTIMEO);
459                     toReturn = (int) timeval.toMillis();
460                     break;
461                 case SocketOptions.SO_RCVBUF:
462                 case SocketOptions.SO_SNDBUF:
463                 case SocketOptions.SO_REUSEADDR:
464                     int osOpt = javaSoToOsOpt(optID);
465                     toReturn = Os.getsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt);
466                     break;
467                 case SocketOptions.SO_LINGER:
468                     StructLinger linger=
469                             Os.getsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER);
470                     if (!linger.isOn()) {
471                         toReturn = -1;
472                     } else {
473                         toReturn = linger.l_linger;
474                     }
475                     break;
476                 case SocketOptions.TCP_NODELAY:
477                     toReturn = Os.getsockoptInt(fd, OsConstants.IPPROTO_TCP,
478                             OsConstants.TCP_NODELAY);
479                     break;
480                 default:
481                     throw new IOException("Unknown option: " + optID);
482             }
483             return toReturn;
484         } catch (ErrnoException e) {
485             throw e.rethrowAsIOException();
486         }
487     }
488 
setOption(int optID, Object value)489     public void setOption(int optID, Object value)
490             throws IOException {
491 
492         if (fd == null) {
493             throw new IOException("socket not created");
494         }
495 
496         /*
497          * Boolean.FALSE is used to disable some options, so it
498          * is important to distinguish between FALSE and unset.
499          * We define it here that -1 is unset, 0 is FALSE, and 1
500          * is TRUE.
501          */
502         int boolValue = -1;
503         int intValue = 0;
504         if (value instanceof Integer) {
505             intValue = (Integer)value;
506         } else if (value instanceof Boolean) {
507             boolValue = ((Boolean) value)? 1 : 0;
508         } else {
509             throw new IOException("bad value: " + value);
510         }
511 
512         try {
513             switch (optID) {
514                 case SocketOptions.SO_LINGER:
515                     StructLinger linger = new StructLinger(boolValue, intValue);
516                     Os.setsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER, linger);
517                     break;
518                 case SocketOptions.SO_TIMEOUT:
519                     /*
520                      * SO_TIMEOUT from the core library gets converted to
521                      * SO_SNDTIMEO, but the option is supposed to set both
522                      * send and receive timeouts. Note: The incoming timeout
523                      * value is in milliseconds.
524                      */
525                     StructTimeval timeval = StructTimeval.fromMillis(intValue);
526                     Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
527                             timeval);
528                     break;
529                 case SocketOptions.SO_RCVBUF:
530                 case SocketOptions.SO_SNDBUF:
531                 case SocketOptions.SO_REUSEADDR:
532                     int osOpt = javaSoToOsOpt(optID);
533                     Os.setsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt, intValue);
534                     break;
535                 case SocketOptions.TCP_NODELAY:
536                     Os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_NODELAY,
537                             intValue);
538                     break;
539                 default:
540                     throw new IOException("Unknown option: " + optID);
541             }
542         } catch (ErrnoException e) {
543             throw e.rethrowAsIOException();
544         }
545     }
546 
547     /**
548      * Enqueues a set of file descriptors to send to the peer. The queue
549      * is one deep. The file descriptors will be sent with the next write
550      * of normal data, and will be delivered in a single ancillary message.
551      * See "man 7 unix" SCM_RIGHTS on a desktop Linux machine.
552      *
553      * @param fds non-null; file descriptors to send.
554      * @throws IOException
555      */
setFileDescriptorsForSend(FileDescriptor[] fds)556     public void setFileDescriptorsForSend(FileDescriptor[] fds) {
557         synchronized(writeMonitor) {
558             outboundFileDescriptors = fds;
559         }
560     }
561 
562     /**
563      * Retrieves a set of file descriptors that a peer has sent through
564      * an ancillary message. This method retrieves the most recent set sent,
565      * and then returns null until a new set arrives.
566      * File descriptors may only be passed along with regular data, so this
567      * method can only return a non-null after a read operation.
568      *
569      * @return null or file descriptor array
570      * @throws IOException
571      */
getAncillaryFileDescriptors()572     public FileDescriptor[] getAncillaryFileDescriptors() throws IOException {
573         synchronized(readMonitor) {
574             FileDescriptor[] result = inboundFileDescriptors;
575 
576             inboundFileDescriptors = null;
577             return result;
578         }
579     }
580 
581     /**
582      * Retrieves the credentials of this socket's peer. Only valid on
583      * connected sockets.
584      *
585      * @return non-null; peer credentials
586      * @throws IOException
587      */
getPeerCredentials()588     public Credentials getPeerCredentials() throws IOException {
589         return getPeerCredentials_native(fd);
590     }
591 
592     /**
593      * Retrieves the socket name from the OS.
594      *
595      * @return non-null; socket name
596      * @throws IOException on failure
597      */
getSockAddress()598     public LocalSocketAddress getSockAddress() throws IOException {
599         // This method has never been implemented.
600         return null;
601     }
602 
603     @Override
finalize()604     protected void finalize() throws IOException {
605         close();
606     }
607 
javaSoToOsOpt(int optID)608     private static int javaSoToOsOpt(int optID) {
609         switch (optID) {
610             case SocketOptions.SO_SNDBUF:
611                 return OsConstants.SO_SNDBUF;
612             case SocketOptions.SO_RCVBUF:
613                 return OsConstants.SO_RCVBUF;
614             case SocketOptions.SO_REUSEADDR:
615                 return OsConstants.SO_REUSEADDR;
616             default:
617                 throw new UnsupportedOperationException("Unknown option: " + optID);
618         }
619     }
620 }
621