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