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 | |
37 | static jfieldID IO_fd_fdID; |
38 | |
39 | jfieldID psi_fdID; |
40 | jfieldID psi_addressID; |
41 | jfieldID psi_ipaddressID; |
42 | jfieldID psi_portID; |
43 | jfieldID psi_localportID; |
44 | jfieldID psi_timeoutID; |
45 | jfieldID psi_trafficClassID; |
46 | jfieldID psi_fdLockID; |
47 | jfieldID psi_closePendingID; |
48 | |
49 | /* |
50 | * file descriptor used for dup2 |
51 | */ |
52 | static 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 | */ |
72 | static 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 | */ |
97 | static 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 | */ |
112 | JNIEXPORT void JNICALL |
113 | Java_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 | */ |
148 | static jclass socketExceptionCls; |
149 | |
150 | /* |
151 | * Class: java_net_PlainSocketImpl |
152 | * Method: socketCreate |
153 | * Signature: (ZZ)V */ |
154 | JNIEXPORT void JNICALL |
155 | Java_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 | */ |
223 | JNIEXPORT void JNICALL |
224 | Java_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 | */ |
481 | JNIEXPORT void JNICALL |
482 | Java_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 | */ |
548 | JNIEXPORT void JNICALL |
549 | Java_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 | */ |
583 | JNIEXPORT void JNICALL |
584 | Java_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 | */ |
734 | JNIEXPORT jint JNICALL |
735 | Java_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 | */ |
763 | JNIEXPORT void JNICALL |
764 | Java_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 | */ |
792 | JNIEXPORT void JNICALL |
793 | Java_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 | */ |
820 | JNIEXPORT void JNICALL |
821 | Java_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 | */ |
916 | JNIEXPORT jint JNICALL |
917 | Java_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 | */ |
1007 | JNIEXPORT void JNICALL |
1008 | Java_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 | |