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