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