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