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