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