1 /*
2 * Copyright (c) 2007, 2018, 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 "net_util.h"
26
27 #include "java_net_PlainSocketImpl.h"
28 #include "java_net_SocketOptions.h"
29
30 #define SET_BLOCKING 0
31 #define SET_NONBLOCKING 1
32
33 static jclass isa_class; /* java.net.InetSocketAddress */
34 static jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) */
35
36 /*
37 * Class: java_net_PlainSocketImpl
38 * Method: initIDs
39 * Signature: ()V
40 */
Java_java_net_PlainSocketImpl_initIDs(JNIEnv * env,jclass clazz)41 JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_initIDs
42 (JNIEnv *env, jclass clazz) {
43
44 jclass cls = (*env)->FindClass(env, "java/net/InetSocketAddress");
45 CHECK_NULL(cls);
46 isa_class = (*env)->NewGlobalRef(env, cls);
47 CHECK_NULL(isa_class);
48 isa_ctorID = (*env)->GetMethodID(env, cls, "<init>",
49 "(Ljava/net/InetAddress;I)V");
50 CHECK_NULL(isa_ctorID);
51 initInetAddressIDs(env);
52
53 // implement read timeout with select.
54 isRcvTimeoutSupported = JNI_FALSE;
55 }
56
57 /*
58 * Class: java_net_PlainSocketImpl
59 * Method: socket0
60 * Signature: (ZZ)I
61 */
Java_java_net_PlainSocketImpl_socket0(JNIEnv * env,jclass clazz,jboolean stream)62 JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_socket0
63 (JNIEnv *env, jclass clazz, jboolean stream) {
64 int fd, rv, opt = 0;
65 int type = (stream ? SOCK_STREAM : SOCK_DGRAM);
66 int domain = ipv6_available() ? AF_INET6 : AF_INET;
67
68 fd = NET_Socket(domain, type, 0);
69
70 if (fd == INVALID_SOCKET) {
71 NET_ThrowNew(env, WSAGetLastError(), "create");
72 return -1;
73 }
74
75 if (domain == AF_INET6) {
76 rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt,
77 sizeof(opt));
78 if (rv == SOCKET_ERROR) {
79 NET_ThrowNew(env, WSAGetLastError(), "create");
80 closesocket(fd);
81 return -1;
82 }
83 }
84
85 return fd;
86 }
87
88 /*
89 * Class: java_net_PlainSocketImpl
90 * Method: bind0
91 * Signature: (ILjava/net/InetAddress;I)V
92 */
Java_java_net_PlainSocketImpl_bind0(JNIEnv * env,jclass clazz,jint fd,jobject iaObj,jint port,jboolean exclBind)93 JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_bind0
94 (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port,
95 jboolean exclBind)
96 {
97 SOCKETADDRESS sa;
98 int rv, sa_len = 0;
99 jboolean v4MappedAddress = ipv6_available() ? JNI_TRUE : JNI_FALSE;
100
101 if (NET_InetAddressToSockaddr(env, iaObj, port, &sa,
102 &sa_len, v4MappedAddress) != 0) {
103 return;
104 }
105
106 rv = NET_WinBind(fd, &sa, sa_len, exclBind);
107
108 if (rv == SOCKET_ERROR)
109 NET_ThrowNew(env, WSAGetLastError(), "NET_Bind");
110 }
111
112 /*
113 * Class: java_net_PlainSocketImpl
114 * Method: connect0
115 * Signature: (ILjava/net/InetAddress;I)I
116 */
Java_java_net_PlainSocketImpl_connect0(JNIEnv * env,jclass clazz,jint fd,jobject iaObj,jint port)117 JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_connect0
118 (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) {
119 SOCKETADDRESS sa;
120 int rv, sa_len = 0;
121 jboolean v4MappedAddress = ipv6_available() ? JNI_TRUE : JNI_FALSE;
122
123 if (NET_InetAddressToSockaddr(env, iaObj, port, &sa,
124 &sa_len, v4MappedAddress) != 0) {
125 return -1;
126 }
127
128 rv = connect(fd, &sa.sa, sa_len);
129 if (rv == SOCKET_ERROR) {
130 int err = WSAGetLastError();
131 if (err == WSAEWOULDBLOCK) {
132 return java_net_PlainSocketImpl_WOULDBLOCK;
133 } else if (err == WSAEADDRNOTAVAIL) {
134 JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException",
135 "connect: Address is invalid on local machine,"
136 " or port is not valid on remote machine");
137 } else {
138 NET_ThrowNew(env, err, "connect");
139 }
140 // return value not important.
141 }
142 return rv;
143 }
144
145 /*
146 * Class: java_net_PlainSocketImpl
147 * Method: waitForConnect
148 * Signature: (II)V
149 */
Java_java_net_PlainSocketImpl_waitForConnect(JNIEnv * env,jclass clazz,jint fd,jint timeout)150 JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_waitForConnect
151 (JNIEnv *env, jclass clazz, jint fd, jint timeout) {
152 int rv, retry;
153 int optlen = sizeof(rv);
154 fd_set wr, ex;
155 struct timeval t;
156
157 FD_ZERO(&wr);
158 FD_ZERO(&ex);
159 FD_SET(fd, &wr);
160 FD_SET(fd, &ex);
161 t.tv_sec = timeout / 1000;
162 t.tv_usec = (timeout % 1000) * 1000;
163
164 /*
165 * Wait for timeout, connection established or
166 * connection failed.
167 */
168 rv = select(fd+1, 0, &wr, &ex, &t);
169
170 /*
171 * Timeout before connection is established/failed so
172 * we throw exception and shutdown input/output to prevent
173 * socket from being used.
174 * The socket should be closed immediately by the caller.
175 */
176 if (rv == 0) {
177 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
178 "connect timed out");
179 shutdown(fd, SD_BOTH);
180 return;
181 }
182
183 /*
184 * Socket is writable or error occurred. On some Windows editions
185 * the socket will appear writable when the connect fails so we
186 * check for error rather than writable.
187 */
188 if (!FD_ISSET(fd, &ex)) {
189 return; /* connection established */
190 }
191
192 /*
193 * Connection failed. The logic here is designed to work around
194 * bug on Windows NT whereby using getsockopt to obtain the
195 * last error (SO_ERROR) indicates there is no error. The workaround
196 * on NT is to allow winsock to be scheduled and this is done by
197 * yielding and retrying. As yielding is problematic in heavy
198 * load conditions we attempt up to 3 times to get the error reason.
199 */
200 for (retry = 0; retry < 3; retry++) {
201 NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR,
202 (char*)&rv, &optlen);
203 if (rv) {
204 break;
205 }
206 Sleep(0);
207 }
208
209 if (rv == 0) {
210 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
211 "Unable to establish connection");
212 } else if (!ipv6_available() && rv == WSAEADDRNOTAVAIL) {
213 JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException",
214 "connect: Address is invalid on local machine,"
215 " or port is not valid on remote machine");
216 } else {
217 NET_ThrowNew(env, rv, "connect");
218 }
219 }
220
221 /*
222 * Class: java_net_PlainSocketImpl
223 * Method: localPort0
224 * Signature: (I)I
225 */
Java_java_net_PlainSocketImpl_localPort0(JNIEnv * env,jclass clazz,jint fd)226 JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_localPort0
227 (JNIEnv *env, jclass clazz, jint fd) {
228 SOCKETADDRESS sa;
229 int len = sizeof(sa);
230
231 if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) {
232 if (WSAGetLastError() == WSAENOTSOCK) {
233 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
234 "Socket closed");
235 } else {
236 NET_ThrowNew(env, WSAGetLastError(), "getsockname failed");
237 }
238 return -1;
239 }
240 return (int) ntohs((u_short)GET_PORT(&sa));
241 }
242
243 /*
244 * Class: java_net_PlainSocketImpl
245 * Method: localAddress
246 * Signature: (ILjava/net/InetAddressContainer;)V
247 */
Java_java_net_PlainSocketImpl_localAddress(JNIEnv * env,jclass clazz,jint fd,jobject iaContainerObj)248 JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_localAddress
249 (JNIEnv *env, jclass clazz, jint fd, jobject iaContainerObj) {
250 int port;
251 SOCKETADDRESS sa;
252 int len = sizeof(sa);
253 jobject iaObj;
254 jclass iaContainerClass;
255 jfieldID iaFieldID;
256
257 if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) {
258 NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name");
259 return;
260 }
261 iaObj = NET_SockaddrToInetAddress(env, &sa, &port);
262 CHECK_NULL(iaObj);
263
264 iaContainerClass = (*env)->GetObjectClass(env, iaContainerObj);
265 iaFieldID = (*env)->GetFieldID(env, iaContainerClass, "addr", "Ljava/net/InetAddress;");
266 CHECK_NULL(iaFieldID);
267 (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj);
268 }
269
270 /*
271 * Class: java_net_PlainSocketImpl
272 * Method: listen0
273 * Signature: (II)V
274 */
Java_java_net_PlainSocketImpl_listen0(JNIEnv * env,jclass clazz,jint fd,jint backlog)275 JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_listen0
276 (JNIEnv *env, jclass clazz, jint fd, jint backlog) {
277 if (listen(fd, backlog) == SOCKET_ERROR) {
278 NET_ThrowNew(env, WSAGetLastError(), "listen failed");
279 }
280 }
281
282 /*
283 * Class: java_net_PlainSocketImpl
284 * Method: accept0
285 * Signature: (I[Ljava/net/InetSocketAddress;)I
286 */
Java_java_net_PlainSocketImpl_accept0(JNIEnv * env,jclass clazz,jint fd,jobjectArray isaa)287 JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_accept0
288 (JNIEnv *env, jclass clazz, jint fd, jobjectArray isaa) {
289 int newfd, port = 0;
290 jobject isa;
291 jobject ia;
292 SOCKETADDRESS sa;
293 int len = sizeof(sa);
294
295 memset((char *)&sa, 0, len);
296 newfd = accept(fd, &sa.sa, &len);
297
298 if (newfd == INVALID_SOCKET) {
299 NET_ThrowNew(env, WSAGetLastError(), "accept failed");
300 return -1;
301 }
302
303 SetHandleInformation((HANDLE)(UINT_PTR)newfd, HANDLE_FLAG_INHERIT, 0);
304
305 ia = NET_SockaddrToInetAddress(env, &sa, &port);
306 if (ia == NULL){
307 closesocket(newfd);
308 return -1;
309 }
310 isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port);
311 if (isa == NULL) {
312 closesocket(newfd);
313 return -1;
314 }
315 (*env)->SetObjectArrayElement(env, isaa, 0, isa);
316
317 return newfd;
318 }
319
320 /*
321 * Class: java_net_PlainSocketImpl
322 * Method: waitForNewConnection
323 * Signature: (II)V
324 */
Java_java_net_PlainSocketImpl_waitForNewConnection(JNIEnv * env,jclass clazz,jint fd,jint timeout)325 JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_waitForNewConnection
326 (JNIEnv *env, jclass clazz, jint fd, jint timeout) {
327 int rv;
328
329 rv = NET_Timeout(fd, timeout);
330 if (rv == 0) {
331 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
332 "Accept timed out");
333 } else if (rv == -1) {
334 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
335 } else if (rv == -2) {
336 JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
337 "operation interrupted");
338 }
339 }
340
341 /*
342 * Class: java_net_PlainSocketImpl
343 * Method: available0
344 * Signature: (I)I
345 */
Java_java_net_PlainSocketImpl_available0(JNIEnv * env,jclass clazz,jint fd)346 JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_available0
347 (JNIEnv *env, jclass clazz, jint fd) {
348 jint available = -1;
349
350 if ((ioctlsocket(fd, FIONREAD, &available)) == SOCKET_ERROR) {
351 NET_ThrowNew(env, WSAGetLastError(), "socket available");
352 }
353
354 return available;
355 }
356
357 /*
358 * Class: java_net_PlainSocketImpl
359 * Method: close0
360 * Signature: (I)V
361 */
Java_java_net_PlainSocketImpl_close0(JNIEnv * env,jclass clazz,jint fd)362 JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_close0
363 (JNIEnv *env, jclass clazz, jint fd) {
364 NET_SocketClose(fd);
365 }
366
367 /*
368 * Class: java_net_PlainSocketImpl
369 * Method: shutdown0
370 * Signature: (II)V
371 */
Java_java_net_PlainSocketImpl_shutdown0(JNIEnv * env,jclass clazz,jint fd,jint howto)372 JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_shutdown0
373 (JNIEnv *env, jclass clazz, jint fd, jint howto) {
374 shutdown(fd, howto);
375 }
376
377 /*
378 * Class: java_net_PlainSocketImpl
379 * Method: setIntOption
380 * Signature: (III)V
381 */
382 JNIEXPORT void JNICALL
Java_java_net_PlainSocketImpl_setIntOption(JNIEnv * env,jclass clazz,jint fd,jint cmd,jint value)383 Java_java_net_PlainSocketImpl_setIntOption
384 (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value)
385 {
386 int level = 0, opt = 0;
387 struct linger linger = {0, 0};
388 char *parg;
389 int arglen;
390
391 if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
392 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
393 return;
394 }
395
396 if (opt == java_net_SocketOptions_SO_LINGER) {
397 parg = (char *)&linger;
398 arglen = sizeof(linger);
399 if (value >= 0) {
400 linger.l_onoff = 1;
401 linger.l_linger = (unsigned short)value;
402 } else {
403 linger.l_onoff = 0;
404 linger.l_linger = 0;
405 }
406 } else {
407 parg = (char *)&value;
408 arglen = sizeof(value);
409 }
410
411 if (NET_SetSockOpt(fd, level, opt, parg, arglen) < 0) {
412 NET_ThrowNew(env, WSAGetLastError(), "setsockopt");
413 }
414 }
415
416 /*
417 * Class: java_net_PlainSocketImpl
418 * Method: setSoTimeout0
419 * Signature: (II)V
420 */
421 JNIEXPORT void JNICALL
Java_java_net_PlainSocketImpl_setSoTimeout0(JNIEnv * env,jclass clazz,jint fd,jint timeout)422 Java_java_net_PlainSocketImpl_setSoTimeout0
423 (JNIEnv *env, jclass clazz, jint fd, jint timeout)
424 {
425 /*
426 * SO_TIMEOUT is the socket option used to specify the timeout
427 * for ServerSocket.accept and Socket.getInputStream().read.
428 * It does not typically map to a native level socket option.
429 * For Windows we special-case this and use the SOL_SOCKET/SO_RCVTIMEO
430 * socket option to specify a receive timeout on the socket. This
431 * receive timeout is applicable to Socket only and the socket
432 * option should not be set on ServerSocket.
433 */
434
435 /*
436 * SO_RCVTIMEO is only supported on Microsoft's implementation
437 * of Windows Sockets so if WSAENOPROTOOPT returned then
438 * reset flag and timeout will be implemented using
439 * select() -- see SocketInputStream.socketRead.
440 */
441 if (isRcvTimeoutSupported) {
442 /*
443 * Disable SO_RCVTIMEO if timeout is <= 5 second.
444 */
445 if (timeout <= 5000) {
446 timeout = 0;
447 }
448
449 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
450 sizeof(timeout)) < 0) {
451 int err = WSAGetLastError();
452 if (err == WSAENOPROTOOPT) {
453 isRcvTimeoutSupported = JNI_FALSE;
454 } else {
455 NET_ThrowNew(env, err, "setsockopt SO_RCVTIMEO");
456 }
457 }
458 }
459 }
460
461 /*
462 * Class: java_net_PlainSocketImpl
463 * Method: getIntOption
464 * Signature: (II)I
465 */
Java_java_net_PlainSocketImpl_getIntOption(JNIEnv * env,jclass clazz,jint fd,jint cmd)466 JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_getIntOption
467 (JNIEnv *env, jclass clazz, jint fd, jint cmd)
468 {
469 int level = 0, opt = 0;
470 int result = 0;
471 struct linger linger = {0, 0};
472 char *arg;
473 int arglen;
474
475 if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
476 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
477 return -1;
478 }
479
480 if (opt == java_net_SocketOptions_SO_LINGER) {
481 arg = (char *)&linger;
482 arglen = sizeof(linger);
483 } else {
484 arg = (char *)&result;
485 arglen = sizeof(result);
486 }
487
488 if (NET_GetSockOpt(fd, level, opt, arg, &arglen) < 0) {
489 NET_ThrowNew(env, WSAGetLastError(), "getsockopt");
490 return -1;
491 }
492
493 if (opt == java_net_SocketOptions_SO_LINGER)
494 return linger.l_onoff ? linger.l_linger : -1;
495 else
496 return result;
497 }
498
499 /*
500 * Class: java_net_PlainSocketImpl
501 * Method: sendOOB
502 * Signature: (II)V
503 */
Java_java_net_PlainSocketImpl_sendOOB(JNIEnv * env,jclass clazz,jint fd,jint data)504 JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_sendOOB
505 (JNIEnv *env, jclass clazz, jint fd, jint data) {
506 jint n;
507 unsigned char d = (unsigned char) data & 0xff;
508
509 n = send(fd, (char *)&data, 1, MSG_OOB);
510 if (n == SOCKET_ERROR) {
511 NET_ThrowNew(env, WSAGetLastError(), "send");
512 }
513 }
514
515 /*
516 * Class: java_net_PlainSocketImpl
517 * Method: configureBlocking
518 * Signature: (IZ)V
519 */
Java_java_net_PlainSocketImpl_configureBlocking(JNIEnv * env,jclass clazz,jint fd,jboolean blocking)520 JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_configureBlocking
521 (JNIEnv *env, jclass clazz, jint fd, jboolean blocking) {
522 u_long arg;
523 int result;
524
525 if (blocking == JNI_TRUE) {
526 arg = SET_BLOCKING; // 0
527 } else {
528 arg = SET_NONBLOCKING; // 1
529 }
530
531 result = ioctlsocket(fd, FIONBIO, &arg);
532 if (result == SOCKET_ERROR) {
533 NET_ThrowNew(env, WSAGetLastError(), "configureBlocking");
534 }
535 }
536