1/*
2 * Copyright (c) 2000, 2012, 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
26#include <errno.h>
27#include <sys/time.h>
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <netinet/in.h>
31#include <netdb.h>
32#include <string.h>
33#include <strings.h>
34#include <stdlib.h>
35#include <ctype.h>
36#ifdef _ALLBSD_SOURCE
37#include <unistd.h> /* gethostname */
38#endif
39
40#include "jvm.h"
41#include "jni_util.h"
42#include "net_util.h"
43#ifndef IPV6_DEFS_H
44#include <netinet/icmp6.h>
45#endif
46
47#include "java_net_Inet4AddressImpl.h"
48#include "JNIHelp.h"
49
50#define NATIVE_METHOD(className, functionName, signature) \
51{ #functionName, signature, (void*)(className ## _ ## functionName) }
52
53/* the initial size of our hostent buffers */
54#ifndef NI_MAXHOST
55#define NI_MAXHOST 1025
56#endif
57
58
59/************************************************************************
60 * Inet6AddressImpl
61 */
62
63static jclass ni_iacls;
64static jclass ni_ia4cls;
65static jclass ni_ia6cls;
66static jmethodID ni_ia4ctrID;
67static jmethodID ni_ia6ctrID;
68static jfieldID ni_ia6ipaddressID;
69static int initialized = 0;
70
71/*
72 * Class:     java_net_Inet6AddressImpl
73 * Method:    getHostByAddr
74 * Signature: (I)Ljava/lang/String;
75 */
76JNIEXPORT jstring JNICALL
77Inet6AddressImpl_getHostByAddr0(JNIEnv *env, jobject this,
78                                             jbyteArray addrArray) {
79
80    jstring ret = NULL;
81
82#ifdef AF_INET6
83    char host[NI_MAXHOST+1];
84    int error = 0;
85    int len = 0;
86    jbyte caddr[16];
87
88    if (NET_addrtransAvailable()) {
89        struct sockaddr_in him4;
90        struct sockaddr_in6 him6;
91        struct sockaddr *sa;
92
93        /*
94         * For IPv4 addresses construct a sockaddr_in structure.
95         */
96        if ((*env)->GetArrayLength(env, addrArray) == 4) {
97            jint addr;
98            (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
99            addr = ((caddr[0]<<24) & 0xff000000);
100            addr |= ((caddr[1] <<16) & 0xff0000);
101            addr |= ((caddr[2] <<8) & 0xff00);
102            addr |= (caddr[3] & 0xff);
103            memset((void *) &him4, 0, sizeof(him4));
104            him4.sin_addr.s_addr = (uint32_t) htonl(addr);
105            him4.sin_family = AF_INET;
106            sa = (struct sockaddr *) &him4;
107            len = sizeof(him4);
108        } else {
109            /*
110             * For IPv6 address construct a sockaddr_in6 structure.
111             */
112            (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr);
113            memset((void *) &him6, 0, sizeof(him6));
114            memcpy((void *)&(him6.sin6_addr), caddr, sizeof(struct in6_addr) );
115            him6.sin6_family = AF_INET6;
116            sa = (struct sockaddr *) &him6 ;
117            len = sizeof(him6) ;
118        }
119
120        error = (*getnameinfo_ptr)(sa, len, host, NI_MAXHOST, NULL, 0,
121                                   NI_NAMEREQD);
122
123        if (!error) {
124            ret = (*env)->NewStringUTF(env, host);
125        }
126    }
127#endif /* AF_INET6 */
128
129    if (ret == NULL) {
130        JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", NULL);
131    }
132
133    return ret;
134}
135
136#define SET_NONBLOCKING(fd) {           \
137        int flags = fcntl(fd, F_GETFL); \
138        flags |= O_NONBLOCK;            \
139        fcntl(fd, F_SETFL, flags);      \
140}
141
142#ifdef AF_INET6
143static jboolean
144ping6(JNIEnv *env, jint fd, struct sockaddr_in6* him, jint timeout,
145      struct sockaddr_in6* netif, jint ttl) {
146    jint size;
147    jint n;
148    socklen_t len;
149    char sendbuf[1500];
150    unsigned char recvbuf[1500];
151    struct icmp6_hdr *icmp6;
152    struct sockaddr_in6 sa_recv;
153    jbyte *caddr, *recv_caddr;
154    jchar pid;
155    jint tmout2, seq = 1;
156    struct timeval tv;
157    size_t plen;
158
159#ifdef __linux__
160    {
161    int csum_offset;
162    /**
163     * For some strange reason, the linux kernel won't calculate the
164     * checksum of ICMPv6 packets unless you set this socket option
165     */
166    csum_offset = 2;
167    setsockopt(fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sizeof(int));
168    }
169#endif
170
171    caddr = (jbyte *)&(him->sin6_addr);
172
173    /* icmp_id is a 16 bit data type, therefore down cast the pid */
174    pid = (jchar)getpid();
175    size = 60*1024;
176    setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
177    if (ttl > 0) {
178      setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
179    }
180    if (netif != NULL) {
181      if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in6)) <0) {
182        NET_ThrowNew(env, errno, "Can't bind socket");
183        untagSocket(env, fd);
184        close(fd);
185        return JNI_FALSE;
186      }
187    }
188    SET_NONBLOCKING(fd);
189
190    do {
191      icmp6 = (struct icmp6_hdr *) sendbuf;
192      icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
193      icmp6->icmp6_code = 0;
194      /* let's tag the ECHO packet with our pid so we can identify it */
195      icmp6->icmp6_id = htons(pid);
196      icmp6->icmp6_seq = htons(seq);
197      seq++;
198      icmp6->icmp6_cksum = 0;
199      gettimeofday(&tv, NULL);
200      memcpy(sendbuf + sizeof(struct icmp6_hdr), &tv, sizeof(tv));
201      plen = sizeof(struct icmp6_hdr) + sizeof(tv);
202      n = sendto(fd, sendbuf, plen, 0, (struct sockaddr*) him, sizeof(struct sockaddr_in6));
203      if (n < 0 && errno != EINPROGRESS) {
204#ifdef __linux__
205        if (errno != EINVAL && errno != EHOSTUNREACH)
206          /*
207           * On some Linuxes, when bound to the loopback interface, sendto
208           * will fail and errno will be set to EINVAL or EHOSTUNREACH.
209           * When that happens, don't throw an exception, just return false.
210           */
211#endif /*__linux__ */
212        NET_ThrowNew(env, errno, "Can't send ICMP packet");
213        untagSocket(env, fd);
214        close(fd);
215        return JNI_FALSE;
216      }
217
218      tmout2 = timeout > 1000 ? 1000 : timeout;
219      do {
220        tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2);
221
222        if (tmout2 >= 0) {
223          len = sizeof(sa_recv);
224          n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*) &sa_recv, &len);
225          icmp6 = (struct icmp6_hdr *) (recvbuf);
226          recv_caddr = (jbyte *)&(sa_recv.sin6_addr);
227          /*
228           * We did receive something, but is it what we were expecting?
229           * I.E.: An ICMP6_ECHO_REPLY packet with the proper PID and
230           *       from the host that we are trying to determine is reachable.
231           */
232          if (n >= 8 && icmp6->icmp6_type == ICMP6_ECHO_REPLY &&
233              (ntohs(icmp6->icmp6_id) == pid)) {
234            if (NET_IsEqual(caddr, recv_caddr)) {
235              untagSocket(env, fd);
236              close(fd);
237              return JNI_TRUE;
238            }
239            if (NET_IsZeroAddr(caddr)) {
240              untagSocket(env, fd);
241              close(fd);
242              return JNI_TRUE;
243            }
244          }
245        }
246      } while (tmout2 > 0);
247      timeout -= 1000;
248    } while (timeout > 0);
249    untagSocket(env, fd);
250    close(fd);
251    return JNI_FALSE;
252}
253#endif /* AF_INET6 */
254
255/*
256 * Class:     java_net_Inet6AddressImpl
257 * Method:    isReachable0
258 * Signature: ([bII[bI)Z
259 */
260JNIEXPORT jboolean JNICALL
261Inet6AddressImpl_isReachable0(JNIEnv *env, jobject this,
262                                           jbyteArray addrArray,
263                                           jint scope,
264                                           jint timeout,
265                                           jbyteArray ifArray,
266                                           jint ttl, jint if_scope) {
267#ifdef AF_INET6
268    jbyte caddr[16];
269    jint fd, sz;
270    struct sockaddr_in6 him6;
271    struct sockaddr_in6 inf6;
272    struct sockaddr_in6* netif = NULL;
273    int len = 0;
274    int connect_rv = -1;
275
276    /*
277     * If IPv6 is not enable, then we can't reach an IPv6 address, can we?
278     */
279    if (!ipv6_available()) {
280      return JNI_FALSE;
281    }
282    /*
283     * If it's an IPv4 address, ICMP won't work with IPv4 mapped address,
284     * therefore, let's delegate to the Inet4Address method.
285     */
286    sz = (*env)->GetArrayLength(env, addrArray);
287    if (sz == 4) {
288      return Inet4AddressImpl_isReachable0(env, this,
289                                                         addrArray,
290                                                         timeout,
291                                                         ifArray, ttl);
292    }
293
294    memset((void *) caddr, 0, 16);
295    memset((void *) &him6, 0, sizeof(him6));
296    (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr);
297    memcpy((void *)&(him6.sin6_addr), caddr, sizeof(struct in6_addr) );
298    him6.sin6_family = AF_INET6;
299
300    // Android-change: Don't try and figure out a default scope ID if one isn't
301    // set. It's only useful for link local addresses anyway, and callers are
302    // expected to call isReachable with a specific NetworkInterface if they
303    // want to query the reachability of an address that's local to that IF.
304    if (scope > 0)
305      him6.sin6_scope_id = scope;
306    len = sizeof(struct sockaddr_in6);
307    /*
308     * If a network interface was specified, let's create the address
309     * for it.
310     */
311    if (!(IS_NULL(ifArray))) {
312      memset((void *) caddr, 0, 16);
313      memset((void *) &inf6, 0, sizeof(inf6));
314      (*env)->GetByteArrayRegion(env, ifArray, 0, 16, caddr);
315      memcpy((void *)&(inf6.sin6_addr), caddr, sizeof(struct in6_addr) );
316      inf6.sin6_family = AF_INET6;
317      inf6.sin6_scope_id = if_scope;
318      netif = &inf6;
319    }
320    /*
321     * If we can create a RAW socket, then when can use the ICMP ECHO_REQUEST
322     * otherwise we'll try a tcp socket to the Echo port (7).
323     * Note that this is empiric, and not connecting could mean it's blocked
324     * or the echo servioe has been disabled.
325     */
326
327    fd = JVM_Socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
328
329    if (fd != -1) { /* Good to go, let's do a ping */
330        tagSocket(env, fd);
331        return ping6(env, fd, &him6, timeout, netif, ttl);
332    }
333
334    /* No good, let's fall back on TCP */
335    fd = JVM_Socket(AF_INET6, SOCK_STREAM, 0);
336    if (fd == JVM_IO_ERR) {
337        /* note: if you run out of fds, you may not be able to load
338         * the exception class, and get a NoClassDefFoundError
339         * instead.
340         */
341        NET_ThrowNew(env, errno, "Can't create socket");
342        return JNI_FALSE;
343    }
344    tagSocket(env, fd);
345
346    if (ttl > 0) {
347      setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
348    }
349
350    /*
351     * A network interface was specified, so let's bind to it.
352     */
353    if (netif != NULL) {
354      if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in6)) <0) {
355        NET_ThrowNew(env, errno, "Can't bind socket");
356        untagSocket(env, fd);
357        close(fd);
358        return JNI_FALSE;
359      }
360    }
361    SET_NONBLOCKING(fd);
362
363    /* no need to use NET_Connect as non-blocking */
364    him6.sin6_port = htons((short) 7); /* Echo port */
365    connect_rv = JVM_Connect(fd, (struct sockaddr *)&him6, len);
366
367    /**
368     * connection established or refused immediately, either way it means
369     * we were able to reach the host!
370     */
371    if (connect_rv == 0 || errno == ECONNREFUSED) {
372        untagSocket(env, fd);
373        close(fd);
374        return JNI_TRUE;
375    } else {
376        int optlen;
377
378        switch (errno) {
379        case ENETUNREACH: /* Network Unreachable */
380        case EAFNOSUPPORT: /* Address Family not supported */
381        case EADDRNOTAVAIL: /* address is not available on  the  remote machine */
382#ifdef __linux__
383        case EINVAL:
384        case EHOSTUNREACH:
385          /*
386           * On some Linuxes, when bound to the loopback interface, connect
387           * will fail and errno will be set to EINVAL or EHOSTUNREACH.
388           * When that happens, don't throw an exception, just return false.
389           */
390#endif /* __linux__ */
391          untagSocket(env, fd);
392          close(fd);
393          return JNI_FALSE;
394        }
395
396        if (errno != EINPROGRESS) {
397            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
398                                         "connect failed");
399            untagSocket(env, fd);
400            close(fd);
401            return JNI_FALSE;
402        }
403
404        timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);
405
406        if (timeout >= 0) {
407          /* has connection been established */
408          optlen = sizeof(connect_rv);
409          if (JVM_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
410                             &optlen) <0) {
411            connect_rv = errno;
412          }
413          if (connect_rv == 0 || ECONNREFUSED) {
414            untagSocket(env, fd);
415            close(fd);
416            return JNI_TRUE;
417          }
418        }
419        untagSocket(env, fd);
420        close(fd);
421        return JNI_FALSE;
422    }
423#else /* AF_INET6 */
424    return JNI_FALSE;
425#endif /* AF_INET6 */
426}
427
428static JNINativeMethod gMethods[] = {
429  NATIVE_METHOD(Inet6AddressImpl, isReachable0, "([BII[BII)Z"),
430  NATIVE_METHOD(Inet6AddressImpl, getHostByAddr0, "([B)Ljava/lang/String;"),
431};
432
433void register_java_net_Inet6AddressImpl(JNIEnv* env) {
434  jniRegisterNativeMethods(env, "java/net/Inet6AddressImpl", gMethods, NELEM(gMethods));
435}
436