PlainSocketImpl.c revision 11099:678faa7d1a6a
1/*
2 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include <errno.h>
27#include <string.h>
28#include <sys/types.h>
29#include <sys/socket.h>
30#if defined(__linux__)
31#include <sys/poll.h>
32#endif
33#include <netinet/tcp.h>        /* Defines TCP_NODELAY, needed for 2.6 */
34#include <netinet/in.h>
35#ifdef __linux__
36#include <netinet/ip.h>
37#endif
38#include <netdb.h>
39#include <stdlib.h>
40
41#ifdef __solaris__
42#include <fcntl.h>
43#endif
44#ifdef __linux__
45#include <unistd.h>
46#include <sys/sysctl.h>
47#endif
48
49#include "jvm.h"
50#include "jni_util.h"
51#include "net_util.h"
52
53#include "java_net_SocketOptions.h"
54#include "java_net_PlainSocketImpl.h"
55
56/************************************************************************
57 * PlainSocketImpl
58 */
59
60static jfieldID IO_fd_fdID;
61
62jfieldID psi_fdID;
63jfieldID psi_addressID;
64jfieldID psi_ipaddressID;
65jfieldID psi_portID;
66jfieldID psi_localportID;
67jfieldID psi_timeoutID;
68jfieldID psi_trafficClassID;
69jfieldID psi_serverSocketID;
70jfieldID psi_fdLockID;
71jfieldID psi_closePendingID;
72
73extern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him);
74
75/*
76 * file descriptor used for dup2
77 */
78static int marker_fd = -1;
79
80
81#define SET_NONBLOCKING(fd) {           \
82        int flags = fcntl(fd, F_GETFL); \
83        flags |= O_NONBLOCK;            \
84        fcntl(fd, F_SETFL, flags);      \
85}
86
87#define SET_BLOCKING(fd) {              \
88        int flags = fcntl(fd, F_GETFL); \
89        flags &= ~O_NONBLOCK;           \
90        fcntl(fd, F_SETFL, flags);      \
91}
92
93/*
94 * Create the marker file descriptor by establishing a loopback connection
95 * which we shutdown but do not close the fd. The result is an fd that
96 * can be used for read/write.
97 */
98static int getMarkerFD()
99{
100    int sv[2];
101
102#ifdef AF_UNIX
103    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
104        return -1;
105    }
106#else
107    return -1;
108#endif
109
110    /*
111     * Finally shutdown sv[0] (any reads to this fd will get
112     * EOF; any writes will get an error).
113     */
114    shutdown(sv[0], 2);
115    close(sv[1]);
116
117    return sv[0];
118}
119
120/*
121 * Return the file descriptor given a PlainSocketImpl
122 */
123static int getFD(JNIEnv *env, jobject this) {
124    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
125    CHECK_NULL_RETURN(fdObj, -1);
126    return (*env)->GetIntField(env, fdObj, IO_fd_fdID);
127}
128
129/*
130 * The initroto function is called whenever PlainSocketImpl is
131 * loaded, to cache field IDs for efficiency. This is called every time
132 * the Java class is loaded.
133 *
134 * Class:     java_net_PlainSocketImpl
135 * Method:    initProto
136 * Signature: ()V
137 */
138JNIEXPORT void JNICALL
139Java_java_net_PlainSocketImpl_initProto(JNIEnv *env, jclass cls) {
140    psi_fdID = (*env)->GetFieldID(env, cls , "fd",
141                                  "Ljava/io/FileDescriptor;");
142    CHECK_NULL(psi_fdID);
143    psi_addressID = (*env)->GetFieldID(env, cls, "address",
144                                          "Ljava/net/InetAddress;");
145    CHECK_NULL(psi_addressID);
146    psi_portID = (*env)->GetFieldID(env, cls, "port", "I");
147    CHECK_NULL(psi_portID);
148    psi_localportID = (*env)->GetFieldID(env, cls, "localport", "I");
149    CHECK_NULL(psi_localportID);
150    psi_timeoutID = (*env)->GetFieldID(env, cls, "timeout", "I");
151    CHECK_NULL(psi_timeoutID);
152    psi_trafficClassID = (*env)->GetFieldID(env, cls, "trafficClass", "I");
153    CHECK_NULL(psi_trafficClassID);
154    psi_serverSocketID = (*env)->GetFieldID(env, cls, "serverSocket",
155                        "Ljava/net/ServerSocket;");
156    CHECK_NULL(psi_serverSocketID);
157    psi_fdLockID = (*env)->GetFieldID(env, cls, "fdLock",
158                                      "Ljava/lang/Object;");
159    CHECK_NULL(psi_fdLockID);
160    psi_closePendingID = (*env)->GetFieldID(env, cls, "closePending", "Z");
161    CHECK_NULL(psi_closePendingID);
162    IO_fd_fdID = NET_GetFileDescriptorID(env);
163    CHECK_NULL(IO_fd_fdID);
164
165    initInetAddressIDs(env);
166    JNU_CHECK_EXCEPTION(env);
167
168    /* Create the marker fd used for dup2 */
169    marker_fd = getMarkerFD();
170}
171
172/* a global reference to the java.net.SocketException class. In
173 * socketCreate, we ensure that this is initialized. This is to
174 * prevent the problem where socketCreate runs out of file
175 * descriptors, and is then unable to load the exception class.
176 */
177static jclass socketExceptionCls;
178
179/*
180 * Class:     java_net_PlainSocketImpl
181 * Method:    socketCreate
182 * Signature: (Z)V */
183JNIEXPORT void JNICALL
184Java_java_net_PlainSocketImpl_socketCreate(JNIEnv *env, jobject this,
185                                           jboolean stream) {
186    jobject fdObj, ssObj;
187    int fd;
188    int type = (stream ? SOCK_STREAM : SOCK_DGRAM);
189#ifdef AF_INET6
190    int domain = ipv6_available() ? AF_INET6 : AF_INET;
191#else
192    int domain = AF_INET;
193#endif
194
195    if (socketExceptionCls == NULL) {
196        jclass c = (*env)->FindClass(env, "java/net/SocketException");
197        CHECK_NULL(c);
198        socketExceptionCls = (jclass)(*env)->NewGlobalRef(env, c);
199        CHECK_NULL(socketExceptionCls);
200    }
201    fdObj = (*env)->GetObjectField(env, this, psi_fdID);
202
203    if (fdObj == NULL) {
204        (*env)->ThrowNew(env, socketExceptionCls, "null fd object");
205        return;
206    }
207
208    if ((fd = socket(domain, type, 0)) == -1) {
209        /* note: if you run out of fds, you may not be able to load
210         * the exception class, and get a NoClassDefFoundError
211         * instead.
212         */
213        NET_ThrowNew(env, errno, "can't create socket");
214        return;
215    }
216
217#ifdef AF_INET6
218    /* Disable IPV6_V6ONLY to ensure dual-socket support */
219    if (domain == AF_INET6) {
220        int arg = 0;
221        if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
222                       sizeof(int)) < 0) {
223            NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6");
224            close(fd);
225            return;
226        }
227    }
228#endif /* AF_INET6 */
229
230    /*
231     * If this is a server socket then enable SO_REUSEADDR
232     * automatically and set to non blocking.
233     */
234    ssObj = (*env)->GetObjectField(env, this, psi_serverSocketID);
235    if (ssObj != NULL) {
236        int arg = 1;
237        SET_NONBLOCKING(fd);
238        if (NET_SetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg,
239                       sizeof(arg)) < 0) {
240            NET_ThrowNew(env, errno, "cannot set SO_REUSEADDR");
241            close(fd);
242            return;
243        }
244    }
245
246    (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
247}
248
249/*
250 * inetAddress is the address object passed to the socket connect
251 * call.
252 *
253 * Class:     java_net_PlainSocketImpl
254 * Method:    socketConnect
255 * Signature: (Ljava/net/InetAddress;I)V
256 */
257JNIEXPORT void JNICALL
258Java_java_net_PlainSocketImpl_socketConnect(JNIEnv *env, jobject this,
259                                            jobject iaObj, jint port,
260                                            jint timeout)
261{
262    jint localport = (*env)->GetIntField(env, this, psi_localportID);
263    int len = 0;
264
265    /* fdObj is the FileDescriptor field on this */
266    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
267
268    jclass clazz = (*env)->GetObjectClass(env, this);
269
270    jobject fdLock;
271
272    jint trafficClass = (*env)->GetIntField(env, this, psi_trafficClassID);
273
274    /* fd is an int field on iaObj */
275    jint fd;
276
277    SOCKADDR him;
278    /* The result of the connection */
279    int connect_rv = -1;
280
281    if (IS_NULL(fdObj)) {
282        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
283        return;
284    } else {
285        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
286    }
287    if (IS_NULL(iaObj)) {
288        JNU_ThrowNullPointerException(env, "inet address argument null.");
289        return;
290    }
291
292    /* connect */
293    if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&him, &len, JNI_TRUE) != 0) {
294      return;
295    }
296    setDefaultScopeID(env, (struct sockaddr *)&him);
297
298#ifdef AF_INET6
299    if (trafficClass != 0 && ipv6_available()) {
300        NET_SetTrafficClass((struct sockaddr *)&him, trafficClass);
301    }
302#endif /* AF_INET6 */
303    if (timeout <= 0) {
304        connect_rv = NET_Connect(fd, (struct sockaddr *)&him, len);
305#ifdef __solaris__
306        if (connect_rv == -1 && errno == EINPROGRESS ) {
307
308            /* This can happen if a blocking connect is interrupted by a signal.
309             * See 6343810.
310             */
311            while (1) {
312                struct pollfd pfd;
313                pfd.fd = fd;
314                pfd.events = POLLOUT;
315
316                connect_rv = NET_Poll(&pfd, 1, -1);
317
318                if (connect_rv == -1) {
319                    if (errno == EINTR) {
320                        continue;
321                    } else {
322                        break;
323                    }
324                }
325                if (connect_rv > 0) {
326                    socklen_t optlen;
327                    /* has connection been established */
328                    optlen = sizeof(connect_rv);
329                    if (getsockopt(fd, SOL_SOCKET, SO_ERROR,
330                                   (void*)&connect_rv, &optlen) <0) {
331                        connect_rv = errno;
332                    }
333
334                    if (connect_rv != 0) {
335                        /* restore errno */
336                        errno = connect_rv;
337                        connect_rv = -1;
338                    }
339                    break;
340                }
341            }
342        }
343#endif
344    } else {
345        /*
346         * A timeout was specified. We put the socket into non-blocking
347         * mode, connect, and then wait for the connection to be
348         * established, fail, or timeout.
349         */
350        SET_NONBLOCKING(fd);
351
352        /* no need to use NET_Connect as non-blocking */
353        connect_rv = connect(fd, (struct sockaddr *)&him, len);
354
355        /* connection not established immediately */
356        if (connect_rv != 0) {
357            socklen_t optlen;
358            jlong prevTime = JVM_CurrentTimeMillis(env, 0);
359
360            if (errno != EINPROGRESS) {
361                NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
362                             "connect failed");
363                SET_BLOCKING(fd);
364                return;
365            }
366
367            /*
368             * Wait for the connection to be established or a
369             * timeout occurs. poll needs to handle EINTR in
370             * case lwp sig handler redirects any process signals to
371             * this thread.
372             */
373            while (1) {
374                jlong newTime;
375                struct pollfd pfd;
376                pfd.fd = fd;
377                pfd.events = POLLOUT;
378
379                errno = 0;
380                connect_rv = NET_Poll(&pfd, 1, timeout);
381
382                if (connect_rv >= 0) {
383                    break;
384                }
385                if (errno != EINTR) {
386                    break;
387                }
388
389                /*
390                 * The poll was interrupted so adjust timeout and
391                 * restart
392                 */
393                newTime = JVM_CurrentTimeMillis(env, 0);
394                timeout -= (newTime - prevTime);
395                if (timeout <= 0) {
396                    connect_rv = 0;
397                    break;
398                }
399                prevTime = newTime;
400
401            } /* while */
402
403            if (connect_rv == 0) {
404                JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
405                            "connect timed out");
406
407                /*
408                 * Timeout out but connection may still be established.
409                 * At the high level it should be closed immediately but
410                 * just in case we make the socket blocking again and
411                 * shutdown input & output.
412                 */
413                SET_BLOCKING(fd);
414                shutdown(fd, 2);
415                return;
416            }
417
418            /* has connection been established */
419            optlen = sizeof(connect_rv);
420            if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
421                           &optlen) <0) {
422                connect_rv = errno;
423            }
424        }
425
426        /* make socket blocking again */
427        SET_BLOCKING(fd);
428
429        /* restore errno */
430        if (connect_rv != 0) {
431            errno = connect_rv;
432            connect_rv = -1;
433        }
434    }
435
436    /* report the appropriate exception */
437    if (connect_rv < 0) {
438
439#ifdef __linux__
440        /*
441         * Linux/GNU distribution setup /etc/hosts so that
442         * InetAddress.getLocalHost gets back the loopback address
443         * rather than the host address. Thus a socket can be
444         * bound to the loopback address and the connect will
445         * fail with EADDRNOTAVAIL. In addition the Linux kernel
446         * returns the wrong error in this case - it returns EINVAL
447         * instead of EADDRNOTAVAIL. We handle this here so that
448         * a more descriptive exception text is used.
449         */
450        if (connect_rv == -1 && errno == EINVAL) {
451            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
452                "Invalid argument or cannot assign requested address");
453            return;
454        }
455#endif
456#if defined(EPROTO)
457        if (errno == EPROTO) {
458            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ProtocolException",
459                           "Protocol error");
460            return;
461        }
462#endif
463        if (errno == ECONNREFUSED) {
464            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
465                           "Connection refused");
466        } else if (errno == ETIMEDOUT) {
467            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
468                           "Connection timed out");
469        } else if (errno == EHOSTUNREACH) {
470            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "NoRouteToHostException",
471                           "Host unreachable");
472        } else if (errno == EADDRNOTAVAIL) {
473            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "NoRouteToHostException",
474                             "Address not available");
475        } else if ((errno == EISCONN) || (errno == EBADF)) {
476            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
477                            "Socket closed");
478        } else {
479            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "connect failed");
480        }
481        return;
482    }
483
484    (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
485
486    /* set the remote peer address and port */
487    (*env)->SetObjectField(env, this, psi_addressID, iaObj);
488    (*env)->SetIntField(env, this, psi_portID, port);
489
490    /*
491     * we need to initialize the local port field if bind was called
492     * previously to the connect (by the client) then localport field
493     * will already be initialized
494     */
495    if (localport == 0) {
496        /* Now that we're a connected socket, let's extract the port number
497         * that the system chose for us and store it in the Socket object.
498         */
499        socklen_t slen = SOCKADDR_LEN;
500        if (getsockname(fd, (struct sockaddr *)&him, &slen) == -1) {
501            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
502                           "Error getting socket name");
503        } else {
504            localport = NET_GetPortFromSockaddr((struct sockaddr *)&him);
505            (*env)->SetIntField(env, this, psi_localportID, localport);
506        }
507    }
508}
509
510/*
511 * Class:     java_net_PlainSocketImpl
512 * Method:    socketBind
513 * Signature: (Ljava/net/InetAddress;I)V
514 */
515JNIEXPORT void JNICALL
516Java_java_net_PlainSocketImpl_socketBind(JNIEnv *env, jobject this,
517                                         jobject iaObj, jint localport) {
518
519    /* fdObj is the FileDescriptor field on this */
520    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
521    /* fd is an int field on fdObj */
522    int fd;
523    int len;
524    SOCKADDR him;
525
526    if (IS_NULL(fdObj)) {
527        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
528                        "Socket closed");
529        return;
530    } else {
531        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
532    }
533    if (IS_NULL(iaObj)) {
534        JNU_ThrowNullPointerException(env, "iaObj is null.");
535        return;
536    }
537
538    /* bind */
539    if (NET_InetAddressToSockaddr(env, iaObj, localport, (struct sockaddr *)&him, &len, JNI_TRUE) != 0) {
540      return;
541    }
542    setDefaultScopeID(env, (struct sockaddr *)&him);
543
544    if (NET_Bind(fd, (struct sockaddr *)&him, len) < 0) {
545        if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
546            errno == EPERM || errno == EACCES) {
547            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "BindException",
548                           "Bind failed");
549        } else {
550            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
551                           "Bind failed");
552        }
553        return;
554    }
555
556    /* set the address */
557    (*env)->SetObjectField(env, this, psi_addressID, iaObj);
558
559    /* initialize the local port */
560    if (localport == 0) {
561        socklen_t slen = sizeof(him);
562        /* Now that we're a connected socket, let's extract the port number
563         * that the system chose for us and store it in the Socket object.
564         */
565        if (getsockname(fd, (struct sockaddr *)&him, &slen) == -1) {
566            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
567                           "Error getting socket name");
568            return;
569        }
570        localport = NET_GetPortFromSockaddr((struct sockaddr *)&him);
571        (*env)->SetIntField(env, this, psi_localportID, localport);
572    } else {
573        (*env)->SetIntField(env, this, psi_localportID, localport);
574    }
575}
576
577/*
578 * Class:     java_net_PlainSocketImpl
579 * Method:    socketListen
580 * Signature: (I)V
581 */
582JNIEXPORT void JNICALL
583Java_java_net_PlainSocketImpl_socketListen (JNIEnv *env, jobject this,
584                                            jint count)
585{
586    /* this FileDescriptor fd field */
587    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
588    /* fdObj's int fd field */
589    int fd;
590
591    if (IS_NULL(fdObj)) {
592        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
593                        "Socket closed");
594        return;
595    } else {
596        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
597    }
598
599    /*
600     * Workaround for bugid 4101691 in Solaris 2.6. See 4106600.
601     * If listen backlog is Integer.MAX_VALUE then subtract 1.
602     */
603    if (count == 0x7fffffff)
604        count -= 1;
605
606    if (listen(fd, count) == -1) {
607        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
608                       "Listen failed");
609    }
610}
611
612/*
613 * Class:     java_net_PlainSocketImpl
614 * Method:    socketAccept
615 * Signature: (Ljava/net/SocketImpl;)V
616 */
617JNIEXPORT void JNICALL
618Java_java_net_PlainSocketImpl_socketAccept(JNIEnv *env, jobject this,
619                                           jobject socket)
620{
621    /* fields on this */
622    int port;
623    jint timeout = (*env)->GetIntField(env, this, psi_timeoutID);
624    jlong prevTime = 0;
625    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
626
627    /* the FileDescriptor field on socket */
628    jobject socketFdObj;
629    /* the InetAddress field on socket */
630    jobject socketAddressObj;
631
632    /* the ServerSocket fd int field on fdObj */
633    jint fd;
634
635    /* accepted fd */
636    jint newfd;
637
638    SOCKADDR him;
639    socklen_t slen = SOCKADDR_LEN;
640
641    if (IS_NULL(fdObj)) {
642        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
643                        "Socket closed");
644        return;
645    } else {
646        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
647    }
648    if (IS_NULL(socket)) {
649        JNU_ThrowNullPointerException(env, "socket is null");
650        return;
651    }
652
653    /*
654     * accept connection but ignore ECONNABORTED indicating that
655     * connection was eagerly accepted by the OS but was reset
656     * before accept() was called.
657     *
658     * If accept timeout in place and timeout is adjusted with
659     * each ECONNABORTED or EWOULDBLOCK to ensure that semantics
660     * of timeout are preserved.
661     */
662    for (;;) {
663        int ret;
664
665        /* first usage pick up current time */
666        if (prevTime == 0 && timeout > 0) {
667            prevTime = JVM_CurrentTimeMillis(env, 0);
668        }
669
670        /* passing a timeout of 0 to poll will return immediately,
671           but in the case of ServerSocket 0 means infinite. */
672        if (timeout <= 0) {
673            ret = NET_Timeout(fd, -1);
674        } else {
675            ret = NET_Timeout(fd, timeout);
676        }
677        if (ret == 0) {
678            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
679                            "Accept timed out");
680            return;
681        } else if (ret == -1) {
682            if (errno == EBADF) {
683               JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
684            } else if (errno == ENOMEM) {
685               JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
686            } else {
687               NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Accept failed");
688            }
689            return;
690        }
691
692        newfd = NET_Accept(fd, (struct sockaddr *)&him, &slen);
693
694        /* connection accepted */
695        if (newfd >= 0) {
696            SET_BLOCKING(newfd);
697            break;
698        }
699
700        /* non (ECONNABORTED or EWOULDBLOCK) error */
701        if (!(errno == ECONNABORTED || errno == EWOULDBLOCK)) {
702            break;
703        }
704
705        /* ECONNABORTED or EWOULDBLOCK error so adjust timeout if there is one. */
706        if (timeout) {
707            jlong currTime = JVM_CurrentTimeMillis(env, 0);
708            timeout -= (currTime - prevTime);
709
710            if (timeout <= 0) {
711                JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
712                                "Accept timed out");
713                return;
714            }
715            prevTime = currTime;
716        }
717    }
718
719    if (newfd < 0) {
720        if (newfd == -2) {
721            JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
722                            "operation interrupted");
723        } else {
724            if (errno == EINVAL) {
725                errno = EBADF;
726            }
727            if (errno == EBADF) {
728                JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
729            } else {
730                NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Accept failed");
731            }
732        }
733        return;
734    }
735
736    /*
737     * fill up the remote peer port and address in the new socket structure.
738     */
739    socketAddressObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port);
740    if (socketAddressObj == NULL) {
741        /* should be pending exception */
742        close(newfd);
743        return;
744    }
745
746    /*
747     * Populate SocketImpl.fd.fd
748     */
749    socketFdObj = (*env)->GetObjectField(env, socket, psi_fdID);
750    (*env)->SetIntField(env, socketFdObj, IO_fd_fdID, newfd);
751
752    (*env)->SetObjectField(env, socket, psi_addressID, socketAddressObj);
753    (*env)->SetIntField(env, socket, psi_portID, port);
754    /* also fill up the local port information */
755     port = (*env)->GetIntField(env, this, psi_localportID);
756    (*env)->SetIntField(env, socket, psi_localportID, port);
757}
758
759
760/*
761 * Class:     java_net_PlainSocketImpl
762 * Method:    socketAvailable
763 * Signature: ()I
764 */
765JNIEXPORT jint JNICALL
766Java_java_net_PlainSocketImpl_socketAvailable(JNIEnv *env, jobject this) {
767
768    jint ret = -1;
769    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
770    jint fd;
771
772    if (IS_NULL(fdObj)) {
773        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
774                        "Socket closed");
775        return -1;
776    } else {
777        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
778    }
779    /* NET_SocketAvailable returns 0 for failure, 1 for success */
780    if (NET_SocketAvailable(fd, &ret) == 0){
781        if (errno == ECONNRESET) {
782            JNU_ThrowByName(env, "sun/net/ConnectionResetException", "");
783        } else {
784            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
785                                         "ioctl FIONREAD failed");
786        }
787    }
788    return ret;
789}
790
791/*
792 * Class:     java_net_PlainSocketImpl
793 * Method:    socketClose0
794 * Signature: (Z)V
795 */
796JNIEXPORT void JNICALL
797Java_java_net_PlainSocketImpl_socketClose0(JNIEnv *env, jobject this,
798                                          jboolean useDeferredClose) {
799
800    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
801    jint fd;
802
803    if (IS_NULL(fdObj)) {
804        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
805                        "socket already closed");
806        return;
807    } else {
808        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
809    }
810    if (fd != -1) {
811        if (useDeferredClose && marker_fd >= 0) {
812            NET_Dup2(marker_fd, fd);
813        } else {
814            (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);
815            NET_SocketClose(fd);
816        }
817    }
818}
819
820/*
821 * Class:     java_net_PlainSocketImpl
822 * Method:    socketShutdown
823 * Signature: (I)V
824 */
825JNIEXPORT void JNICALL
826Java_java_net_PlainSocketImpl_socketShutdown(JNIEnv *env, jobject this,
827                                             jint howto)
828{
829
830    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
831    jint fd;
832
833    /*
834     * WARNING: THIS NEEDS LOCKING. ALSO: SHOULD WE CHECK for fd being
835     * -1 already?
836     */
837    if (IS_NULL(fdObj)) {
838        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
839                        "socket already closed");
840        return;
841    } else {
842        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
843    }
844    shutdown(fd, howto);
845}
846
847
848/*
849 * Class:     java_net_PlainSocketImpl
850 * Method:    socketSetOption
851 * Signature: (IZLjava/lang/Object;)V
852 */
853JNIEXPORT void JNICALL
854Java_java_net_PlainSocketImpl_socketSetOption(JNIEnv *env, jobject this,
855                                              jint cmd, jboolean on,
856                                              jobject value) {
857    int fd;
858    int level, optname, optlen;
859    union {
860        int i;
861        struct linger ling;
862    } optval;
863
864    /*
865     * Check that socket hasn't been closed
866     */
867    fd = getFD(env, this);
868    if (fd < 0) {
869        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
870                        "Socket closed");
871        return;
872    }
873
874    /*
875     * SO_TIMEOUT is a NOOP on Solaris/Linux
876     */
877    if (cmd == java_net_SocketOptions_SO_TIMEOUT) {
878        return;
879    }
880
881    /*
882     * Map the Java level socket option to the platform specific
883     * level and option name.
884     */
885    if (NET_MapSocketOption(cmd, &level, &optname)) {
886        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
887        return;
888    }
889
890    switch (cmd) {
891        case java_net_SocketOptions_SO_SNDBUF :
892        case java_net_SocketOptions_SO_RCVBUF :
893        case java_net_SocketOptions_SO_LINGER :
894        case java_net_SocketOptions_IP_TOS :
895            {
896                jclass cls;
897                jfieldID fid;
898
899                cls = (*env)->FindClass(env, "java/lang/Integer");
900                CHECK_NULL(cls);
901                fid = (*env)->GetFieldID(env, cls, "value", "I");
902                CHECK_NULL(fid);
903
904                if (cmd == java_net_SocketOptions_SO_LINGER) {
905                    if (on) {
906                        optval.ling.l_onoff = 1;
907                        optval.ling.l_linger = (*env)->GetIntField(env, value, fid);
908                    } else {
909                        optval.ling.l_onoff = 0;
910                        optval.ling.l_linger = 0;
911                    }
912                    optlen = sizeof(optval.ling);
913                } else {
914                    optval.i = (*env)->GetIntField(env, value, fid);
915                    optlen = sizeof(optval.i);
916                }
917
918                break;
919            }
920
921        /* Boolean -> int */
922        default :
923            optval.i = (on ? 1 : 0);
924            optlen = sizeof(optval.i);
925
926    }
927
928    if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {
929#if defined(__solaris__) || defined(_AIX)
930        if (errno == EINVAL) {
931            // On Solaris setsockopt will set errno to EINVAL if the socket
932            // is closed. The default error message is then confusing
933            char fullMsg[128];
934            jio_snprintf(fullMsg, sizeof(fullMsg), "Invalid option or socket reset by remote peer");
935            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", fullMsg);
936            return;
937        }
938#endif /* __solaris__ */
939        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
940                                      "Error setting socket option");
941    }
942}
943
944/*
945 * Class:     java_net_PlainSocketImpl
946 * Method:    socketGetOption
947 * Signature: (I)I
948 */
949JNIEXPORT jint JNICALL
950Java_java_net_PlainSocketImpl_socketGetOption(JNIEnv *env, jobject this,
951                                              jint cmd, jobject iaContainerObj) {
952
953    int fd;
954    int level, optname, optlen;
955    union {
956        int i;
957        struct linger ling;
958    } optval;
959
960    /*
961     * Check that socket hasn't been closed
962     */
963    fd = getFD(env, this);
964    if (fd < 0) {
965        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
966                        "Socket closed");
967        return -1;
968    }
969
970    /*
971     * SO_BINDADDR isn't a socket option
972     */
973    if (cmd == java_net_SocketOptions_SO_BINDADDR) {
974        SOCKADDR him;
975        socklen_t len = 0;
976        int port;
977        jobject iaObj;
978        jclass iaCntrClass;
979        jfieldID iaFieldID;
980
981        len = SOCKADDR_LEN;
982
983        if (getsockname(fd, (struct sockaddr *)&him, &len) < 0) {
984            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
985                             "Error getting socket name");
986            return -1;
987        }
988        iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port);
989        CHECK_NULL_RETURN(iaObj, -1);
990
991        iaCntrClass = (*env)->GetObjectClass(env, iaContainerObj);
992        iaFieldID = (*env)->GetFieldID(env, iaCntrClass, "addr", "Ljava/net/InetAddress;");
993        CHECK_NULL_RETURN(iaFieldID, -1);
994        (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj);
995        return 0; /* notice change from before */
996    }
997
998    /*
999     * Map the Java level socket option to the platform specific
1000     * level and option name.
1001     */
1002    if (NET_MapSocketOption(cmd, &level, &optname)) {
1003        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
1004        return -1;
1005    }
1006
1007    /*
1008     * Args are int except for SO_LINGER
1009     */
1010    if (cmd == java_net_SocketOptions_SO_LINGER) {
1011        optlen = sizeof(optval.ling);
1012    } else {
1013        optlen = sizeof(optval.i);
1014    }
1015
1016    if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
1017        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1018                                      "Error getting socket option");
1019        return -1;
1020    }
1021
1022    switch (cmd) {
1023        case java_net_SocketOptions_SO_LINGER:
1024            return (optval.ling.l_onoff ? optval.ling.l_linger: -1);
1025
1026        case java_net_SocketOptions_SO_SNDBUF:
1027        case java_net_SocketOptions_SO_RCVBUF:
1028        case java_net_SocketOptions_IP_TOS:
1029            return optval.i;
1030
1031        default :
1032            return (optval.i == 0) ? -1 : 1;
1033    }
1034}
1035
1036
1037/*
1038 * Class:     java_net_PlainSocketImpl
1039 * Method:    socketSendUrgentData
1040 * Signature: (B)V
1041 */
1042JNIEXPORT void JNICALL
1043Java_java_net_PlainSocketImpl_socketSendUrgentData(JNIEnv *env, jobject this,
1044                                             jint data) {
1045    /* The fd field */
1046    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
1047    int n, fd;
1048    unsigned char d = data & 0xFF;
1049
1050    if (IS_NULL(fdObj)) {
1051        JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
1052        return;
1053    } else {
1054        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1055        /* Bug 4086704 - If the Socket associated with this file descriptor
1056         * was closed (sysCloseFD), the file descriptor is set to -1.
1057         */
1058        if (fd == -1) {
1059            JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
1060            return;
1061        }
1062
1063    }
1064    n = NET_Send(fd, (char *)&d, 1, MSG_OOB);
1065    if (n == -1) {
1066        NET_ThrowByNameWithLastError(env, "java/io/IOException", "Write failed");
1067    }
1068}
1069