1/*
2 * Copyright (C) 2011 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 libcore.io;
18
19import java.io.FileDescriptor;
20import java.io.FileNotFoundException;
21import java.io.IOException;
22import java.net.BindException;
23import java.net.ConnectException;
24import java.net.DatagramPacket;
25import java.net.Inet4Address;
26import java.net.Inet6Address;
27import java.net.InetAddress;
28import java.net.InetSocketAddress;
29import java.net.NetworkInterface;
30import java.net.PortUnreachableException;
31import java.net.SocketAddress;
32import java.net.SocketException;
33import java.net.SocketOptions;
34import java.net.SocketTimeoutException;
35import java.net.UnknownHostException;
36import java.nio.ByteBuffer;
37import java.util.Arrays;
38import static libcore.io.OsConstants.*;
39import libcore.util.MutableInt;
40
41/**
42 * Implements java.io/java.net/java.nio semantics in terms of the underlying POSIX system calls.
43 */
44public final class IoBridge {
45
46    private IoBridge() {
47    }
48
49    public static int available(FileDescriptor fd) throws IOException {
50        try {
51            MutableInt available = new MutableInt(0);
52            Libcore.os.ioctlInt(fd, FIONREAD, available);
53            if (available.value < 0) {
54                // If the fd refers to a regular file, the result is the difference between
55                // the file size and the file position. This may be negative if the position
56                // is past the end of the file. If the fd refers to a special file masquerading
57                // as a regular file, the result may be negative because the special file
58                // may appear to have zero size and yet a previous read call may have
59                // read some amount of data and caused the file position to be advanced.
60                available.value = 0;
61            }
62            return available.value;
63        } catch (ErrnoException errnoException) {
64            if (errnoException.errno == ENOTTY) {
65                // The fd is unwilling to opine about its read buffer.
66                return 0;
67            }
68            throw errnoException.rethrowAsIOException();
69        }
70    }
71
72
73    public static void bind(FileDescriptor fd, InetAddress address, int port) throws SocketException {
74        if (address instanceof Inet6Address && ((Inet6Address) address).getScopeId() == 0) {
75            // Linux won't let you bind a link-local address without a scope id. Find one.
76            NetworkInterface nif = NetworkInterface.getByInetAddress(address);
77            if (nif == null) {
78                throw new SocketException("Can't bind to a link-local address without a scope id: " + address);
79            }
80            try {
81                address = Inet6Address.getByAddress(address.getHostName(), address.getAddress(), nif.getIndex());
82            } catch (UnknownHostException ex) {
83                throw new AssertionError(ex); // Can't happen.
84            }
85        }
86        try {
87            Libcore.os.bind(fd, address, port);
88        } catch (ErrnoException errnoException) {
89            throw new BindException(errnoException.getMessage(), errnoException);
90        }
91    }
92
93
94    /**
95     * Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout
96     * means this method won't throw SocketTimeoutException.
97     */
98    public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
99        try {
100            return IoBridge.connect(fd, inetAddress, port, 0);
101        } catch (SocketTimeoutException ex) {
102            throw new AssertionError(ex); // Can't happen for a connect without a timeout.
103        }
104    }
105
106    /**
107     * Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'.
108     * Use timeoutMs == 0 for a blocking connect with no timeout.
109     */
110    public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
111        try {
112            return connectErrno(fd, inetAddress, port, timeoutMs);
113        } catch (ErrnoException errnoException) {
114            throw new ConnectException(connectDetail(inetAddress, port, timeoutMs, errnoException), errnoException);
115        } catch (SocketException ex) {
116            throw ex; // We don't want to doubly wrap these.
117        } catch (SocketTimeoutException ex) {
118            throw ex; // We don't want to doubly wrap these.
119        } catch (IOException ex) {
120            throw new SocketException(ex);
121        }
122    }
123
124    private static boolean connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws ErrnoException, IOException {
125        // With no timeout, just call connect(2) directly.
126        if (timeoutMs == 0) {
127            Libcore.os.connect(fd, inetAddress, port);
128            return true;
129        }
130
131        // For connect with a timeout, we:
132        //   1. set the socket to non-blocking,
133        //   2. connect(2),
134        //   3. loop using poll(2) to decide whether we're connected, whether we should keep
135        //      waiting, or whether we've seen a permanent failure and should give up,
136        //   4. set the socket back to blocking.
137
138        // 1. set the socket to non-blocking.
139        IoUtils.setBlocking(fd, false);
140
141        // 2. call connect(2) non-blocking.
142        long finishTimeMs = System.currentTimeMillis() + timeoutMs;
143        try {
144            Libcore.os.connect(fd, inetAddress, port);
145            IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
146            return true; // We connected immediately.
147        } catch (ErrnoException errnoException) {
148            if (errnoException.errno != EINPROGRESS) {
149                throw errnoException;
150            }
151            // EINPROGRESS means we should keep trying...
152        }
153
154        // 3. loop using poll(2).
155        int remainingTimeoutMs;
156        do {
157            remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis());
158            if (remainingTimeoutMs <= 0) {
159                throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs, null));
160            }
161        } while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
162        IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
163        return true; // Or we'd have thrown.
164    }
165
166    private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) {
167        String detail = "failed to connect to " + inetAddress + " (port " + port + ")";
168        if (timeoutMs > 0) {
169            detail += " after " + timeoutMs + "ms";
170        }
171        if (cause != null) {
172            detail += ": " + cause.getMessage();
173        }
174        return detail;
175    }
176
177    public static void closeSocket(FileDescriptor fd) throws IOException {
178        if (!fd.valid()) {
179            // Socket.close doesn't throw if you try to close an already-closed socket.
180            return;
181        }
182        int intFd = fd.getInt$();
183        fd.setInt$(-1);
184        FileDescriptor oldFd = new FileDescriptor();
185        oldFd.setInt$(intFd);
186        AsynchronousCloseMonitor.signalBlockedThreads(oldFd);
187        try {
188            Libcore.os.close(oldFd);
189        } catch (ErrnoException errnoException) {
190            // TODO: are there any cases in which we should throw?
191        }
192    }
193
194    public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs, int remainingTimeoutMs) throws IOException {
195        ErrnoException cause;
196        try {
197            StructPollfd[] pollFds = new StructPollfd[] { new StructPollfd() };
198            pollFds[0].fd = fd;
199            pollFds[0].events = (short) POLLOUT;
200            int rc = Libcore.os.poll(pollFds, remainingTimeoutMs);
201            if (rc == 0) {
202                return false; // Timeout.
203            }
204            int connectError = Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_ERROR);
205            if (connectError == 0) {
206                return true; // Success!
207            }
208            throw new ErrnoException("isConnected", connectError); // The connect(2) failed.
209        } catch (ErrnoException errnoException) {
210            if (!fd.valid()) {
211                throw new SocketException("Socket closed");
212            }
213            if (errnoException.errno == EINTR) {
214                return false; // Punt and ask the caller to try again.
215            } else {
216                cause = errnoException;
217            }
218        }
219        String detail = connectDetail(inetAddress, port, timeoutMs, cause);
220        if (cause.errno == ETIMEDOUT) {
221            throw new SocketTimeoutException(detail, cause);
222        }
223        throw new ConnectException(detail, cause);
224    }
225
226    // Socket options used by java.net but not exposed in SocketOptions.
227    public static final int JAVA_MCAST_JOIN_GROUP = 19;
228    public static final int JAVA_MCAST_LEAVE_GROUP = 20;
229    public static final int JAVA_IP_MULTICAST_TTL = 17;
230
231    /**
232     * java.net has its own socket options similar to the underlying Unix ones. We paper over the
233     * differences here.
234     */
235    public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException {
236        try {
237            return getSocketOptionErrno(fd, option);
238        } catch (ErrnoException errnoException) {
239            throw errnoException.rethrowAsSocketException();
240        }
241    }
242
243    private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws ErrnoException, SocketException {
244        switch (option) {
245        case SocketOptions.IP_MULTICAST_IF:
246            // This is IPv4-only.
247            return Libcore.os.getsockoptInAddr(fd, IPPROTO_IP, IP_MULTICAST_IF);
248        case SocketOptions.IP_MULTICAST_IF2:
249            // This is IPv6-only.
250            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF);
251        case SocketOptions.IP_MULTICAST_LOOP:
252            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
253            // it doesn't matter which we return.
254            return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP));
255        case IoBridge.JAVA_IP_MULTICAST_TTL:
256            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
257            // it doesn't matter which we return.
258            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
259        case SocketOptions.IP_TOS:
260            // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
261            // it doesn't matter which we return.
262            return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS);
263        case SocketOptions.SO_BROADCAST:
264            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_BROADCAST));
265        case SocketOptions.SO_KEEPALIVE:
266            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE));
267        case SocketOptions.SO_LINGER:
268            StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
269            if (!linger.isOn()) {
270                return false;
271            }
272            return linger.l_linger;
273        case SocketOptions.SO_OOBINLINE:
274            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE));
275        case SocketOptions.SO_RCVBUF:
276            return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_RCVBUF);
277        case SocketOptions.SO_REUSEADDR:
278            return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR));
279        case SocketOptions.SO_SNDBUF:
280            return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
281        case SocketOptions.SO_TIMEOUT:
282            return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis();
283        case SocketOptions.TCP_NODELAY:
284            return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY));
285        default:
286            throw new SocketException("Unknown socket option: " + option);
287        }
288    }
289
290    private static boolean booleanFromInt(int i) {
291        return (i != 0);
292    }
293
294    private static int booleanToInt(boolean b) {
295        return b ? 1 : 0;
296    }
297
298    /**
299     * java.net has its own socket options similar to the underlying Unix ones. We paper over the
300     * differences here.
301     */
302    public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException {
303        try {
304            setSocketOptionErrno(fd, option, value);
305        } catch (ErrnoException errnoException) {
306            throw errnoException.rethrowAsSocketException();
307        }
308    }
309
310    private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws ErrnoException, SocketException {
311        switch (option) {
312        case SocketOptions.IP_MULTICAST_IF:
313            throw new UnsupportedOperationException("Use IP_MULTICAST_IF2 on Android");
314        case SocketOptions.IP_MULTICAST_IF2:
315            // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int.
316            Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value);
317            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (Integer) value);
318            return;
319        case SocketOptions.IP_MULTICAST_LOOP:
320            // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
321            Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, booleanToInt((Boolean) value));
322            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, booleanToInt((Boolean) value));
323            return;
324        case IoBridge.JAVA_IP_MULTICAST_TTL:
325            // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
326            // IPv4 multicast TTL uses a byte.
327            Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value);
328            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value);
329            return;
330        case SocketOptions.IP_TOS:
331            Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value);
332            Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value);
333            return;
334        case SocketOptions.SO_BROADCAST:
335            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_BROADCAST, booleanToInt((Boolean) value));
336            return;
337        case SocketOptions.SO_KEEPALIVE:
338            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE, booleanToInt((Boolean) value));
339            return;
340        case SocketOptions.SO_LINGER:
341            boolean on = false;
342            int seconds = 0;
343            if (value instanceof Integer) {
344                on = true;
345                seconds = Math.min((Integer) value, 65535);
346            }
347            StructLinger linger = new StructLinger(booleanToInt(on), seconds);
348            Libcore.os.setsockoptLinger(fd, SOL_SOCKET, SO_LINGER, linger);
349            return;
350        case SocketOptions.SO_OOBINLINE:
351            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE, booleanToInt((Boolean) value));
352            return;
353        case SocketOptions.SO_RCVBUF:
354            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, (Integer) value);
355            return;
356        case SocketOptions.SO_REUSEADDR:
357            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR, booleanToInt((Boolean) value));
358            return;
359        case SocketOptions.SO_SNDBUF:
360            Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_SNDBUF, (Integer) value);
361            return;
362        case SocketOptions.SO_TIMEOUT:
363            int millis = (Integer) value;
364            StructTimeval tv = StructTimeval.fromMillis(millis);
365            Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
366            return;
367        case SocketOptions.TCP_NODELAY:
368            Libcore.os.setsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY, booleanToInt((Boolean) value));
369            return;
370        case IoBridge.JAVA_MCAST_JOIN_GROUP:
371        case IoBridge.JAVA_MCAST_LEAVE_GROUP:
372            StructGroupReq groupReq = (StructGroupReq) value;
373            int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6;
374            int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP;
375            Libcore.os.setsockoptGroupReq(fd, level, op, groupReq);
376            return;
377        default:
378            throw new SocketException("Unknown socket option: " + option);
379        }
380    }
381
382    /**
383     * java.io only throws FileNotFoundException when opening files, regardless of what actually
384     * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening
385     * directories: POSIX says read-only is okay, but java.io doesn't even allow that. We also
386     * have an Android-specific hack to alter the default permissions.
387     */
388    public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
389        FileDescriptor fd = null;
390        try {
391            // On Android, we don't want default permissions to allow global access.
392            int mode = ((flags & O_ACCMODE) == O_RDONLY) ? 0 : 0600;
393            fd = Libcore.os.open(path, flags, mode);
394            if (fd.valid()) {
395                // Posix open(2) fails with EISDIR only if you ask for write permission.
396                // Java disallows reading directories too.
397                if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) {
398                    throw new ErrnoException("open", EISDIR);
399                }
400            }
401            return fd;
402        } catch (ErrnoException errnoException) {
403            try {
404                if (fd != null) {
405                    IoUtils.close(fd);
406                }
407            } catch (IOException ignored) {
408            }
409            FileNotFoundException ex = new FileNotFoundException(path + ": " + errnoException.getMessage());
410            ex.initCause(errnoException);
411            throw ex;
412        }
413    }
414
415    /**
416     * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional
417     * Unix practice where you'd read until you got 0 bytes (and any future read would return -1).
418     */
419    public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
420        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
421        if (byteCount == 0) {
422            return 0;
423        }
424        try {
425            int readCount = Libcore.os.read(fd, bytes, byteOffset, byteCount);
426            if (readCount == 0) {
427                return -1;
428            }
429            return readCount;
430        } catch (ErrnoException errnoException) {
431            if (errnoException.errno == EAGAIN) {
432                // We return 0 rather than throw if we try to read from an empty non-blocking pipe.
433                return 0;
434            }
435            throw errnoException.rethrowAsIOException();
436        }
437    }
438
439    /**
440     * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike
441     * Unix it never just writes as many bytes as happens to be convenient.)
442     */
443    public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
444        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
445        if (byteCount == 0) {
446            return;
447        }
448        try {
449            while (byteCount > 0) {
450                int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount);
451                byteCount -= bytesWritten;
452                byteOffset += bytesWritten;
453            }
454        } catch (ErrnoException errnoException) {
455            throw errnoException.rethrowAsIOException();
456        }
457    }
458
459    public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws IOException {
460        boolean isDatagram = (inetAddress != null);
461        if (!isDatagram && byteCount <= 0) {
462            return 0;
463        }
464        int result;
465        try {
466            result = Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
467        } catch (ErrnoException errnoException) {
468            result = maybeThrowAfterSendto(isDatagram, errnoException);
469        }
470        return result;
471    }
472
473    public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws IOException {
474        boolean isDatagram = (inetAddress != null);
475        if (!isDatagram && buffer.remaining() == 0) {
476            return 0;
477        }
478        int result;
479        try {
480            result = Libcore.os.sendto(fd, buffer, flags, inetAddress, port);
481        } catch (ErrnoException errnoException) {
482            result = maybeThrowAfterSendto(isDatagram, errnoException);
483        }
484        return result;
485    }
486
487    private static int maybeThrowAfterSendto(boolean isDatagram, ErrnoException errnoException) throws SocketException {
488        if (isDatagram) {
489            if (errnoException.errno == ECONNRESET || errnoException.errno == ECONNREFUSED) {
490                return 0;
491            }
492        } else {
493            if (errnoException.errno == EAGAIN) {
494                // We were asked to write to a non-blocking socket, but were told
495                // it would block, so report "no bytes written".
496                return 0;
497            }
498        }
499        throw errnoException.rethrowAsSocketException();
500    }
501
502    public static int recvfrom(boolean isRead, FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
503        int result;
504        try {
505            InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null;
506            result = Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
507            result = postRecvfrom(isRead, packet, isConnected, srcAddress, result);
508        } catch (ErrnoException errnoException) {
509            result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
510        }
511        return result;
512    }
513
514    public static int recvfrom(boolean isRead, FileDescriptor fd, ByteBuffer buffer, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
515        int result;
516        try {
517            InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null;
518            result = Libcore.os.recvfrom(fd, buffer, flags, srcAddress);
519            result = postRecvfrom(isRead, packet, isConnected, srcAddress, result);
520        } catch (ErrnoException errnoException) {
521            result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
522        }
523        return result;
524    }
525
526    private static int postRecvfrom(boolean isRead, DatagramPacket packet, boolean isConnected, InetSocketAddress srcAddress, int byteCount) {
527        if (isRead && byteCount == 0) {
528            return -1;
529        }
530        if (packet != null) {
531            packet.setReceivedLength(byteCount);
532            if (!isConnected) {
533                packet.setAddress(srcAddress.getAddress());
534                packet.setPort(srcAddress.getPort());
535            }
536        }
537        return byteCount;
538    }
539
540    private static int maybeThrowAfterRecvfrom(boolean isRead, boolean isConnected, ErrnoException errnoException) throws SocketException, SocketTimeoutException {
541        if (isRead) {
542            if (errnoException.errno == EAGAIN) {
543                return 0;
544            } else {
545                throw errnoException.rethrowAsSocketException();
546            }
547        } else {
548            if (isConnected && errnoException.errno == ECONNREFUSED) {
549                throw new PortUnreachableException("", errnoException);
550            } else if (errnoException.errno == EAGAIN) {
551                throw new SocketTimeoutException(errnoException);
552            } else {
553                throw errnoException.rethrowAsSocketException();
554            }
555        }
556    }
557
558    public static FileDescriptor socket(boolean stream) throws SocketException {
559        FileDescriptor fd;
560        try {
561            fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
562
563            // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults
564            // to 1. The Linux kernel (at least up to 2.6.38) accidentally defaults to 64 (which
565            // would be correct for the *unicast* hop limit).
566            // See http://www.spinics.net/lists/netdev/msg129022.html, though no patch appears to
567            // have been applied as a result of that discussion. If that bug is ever fixed, we can
568            // remove this code. Until then, we manually set the hop limit on IPv6 datagram sockets.
569            // (IPv4 is already correct.)
570            if (!stream) {
571                Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1);
572            }
573
574            return fd;
575        } catch (ErrnoException errnoException) {
576            throw errnoException.rethrowAsSocketException();
577        }
578    }
579
580    public static InetAddress getSocketLocalAddress(FileDescriptor fd) throws SocketException {
581        try {
582            SocketAddress sa = Libcore.os.getsockname(fd);
583            InetSocketAddress isa = (InetSocketAddress) sa;
584            return isa.getAddress();
585        } catch (ErrnoException errnoException) {
586            throw errnoException.rethrowAsSocketException();
587        }
588    }
589
590    public static int getSocketLocalPort(FileDescriptor fd) throws SocketException {
591        try {
592            SocketAddress sa = Libcore.os.getsockname(fd);
593            InetSocketAddress isa = (InetSocketAddress) sa;
594            return isa.getPort();
595        } catch (ErrnoException errnoException) {
596            throw errnoException.rethrowAsSocketException();
597        }
598    }
599}
600