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