0N/A/*
3658N/A * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A#include <errno.h>
1392N/A#include <sys/time.h>
0N/A#include <sys/types.h>
0N/A#include <sys/socket.h>
0N/A#include <netinet/in_systm.h>
0N/A#include <netinet/in.h>
0N/A#include <netinet/ip.h>
0N/A#include <netinet/ip_icmp.h>
0N/A#include <netdb.h>
0N/A#include <string.h>
0N/A#include <stdlib.h>
0N/A#include <ctype.h>
0N/A
4632N/A#ifdef _ALLBSD_SOURCE
4632N/A#include <unistd.h>
4632N/A#include <sys/param.h>
4632N/A#endif
4632N/A
0N/A#include "jvm.h"
0N/A#include "jni_util.h"
0N/A#include "net_util.h"
0N/A
0N/A#include "java_net_Inet4AddressImpl.h"
0N/A
4632N/A#if defined(__GLIBC__) || (defined(__FreeBSD__) && (__FreeBSD_version >= 601104))
4632N/A#define HAS_GLIBC_GETHOSTBY_R 1
4632N/A#endif
4632N/A
4632N/A#if defined(_ALLBSD_SOURCE) && !defined(HAS_GLIBC_GETHOSTBY_R)
4632N/A/* Use getaddrinfo(3), which is thread safe */
4632N/A/************************************************************************
4632N/A * Inet4AddressImpl
4632N/A */
4632N/A
4632N/A/*
4632N/A * Class: java_net_Inet4AddressImpl
4632N/A * Method: getLocalHostName
4632N/A * Signature: ()Ljava/lang/String;
4632N/A */
4632N/AJNIEXPORT jstring JNICALL
4632N/AJava_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {
4632N/A char hostname[NI_MAXHOST+1];
4632N/A
4632N/A hostname[0] = '\0';
4632N/A if (JVM_GetHostName(hostname, NI_MAXHOST)) {
4632N/A /* Something went wrong, maybe networking is not setup? */
4632N/A strcpy(hostname, "localhost");
4632N/A } else {
4632N/A struct addrinfo hints, *res;
4632N/A int error;
4632N/A
4632N/A memset(&hints, 0, sizeof(hints));
4632N/A hints.ai_flags = AI_CANONNAME;
4632N/A hints.ai_family = AF_UNSPEC;
4632N/A
4632N/A error = getaddrinfo(hostname, NULL, &hints, &res);
4632N/A
4632N/A if (error == 0) {
4632N/A /* host is known to name service */
4632N/A error = getnameinfo(res->ai_addr,
4632N/A res->ai_addrlen,
4632N/A hostname,
4632N/A NI_MAXHOST,
4632N/A NULL,
4632N/A 0,
4632N/A NI_NAMEREQD);
4632N/A
4632N/A /* if getnameinfo fails hostname is still the value
4632N/A from gethostname */
4632N/A
4632N/A freeaddrinfo(res);
4632N/A }
4632N/A }
4632N/A return (*env)->NewStringUTF(env, hostname);
4632N/A}
4632N/A
4632N/Astatic jclass ni_iacls;
4632N/Astatic jclass ni_ia4cls;
4632N/Astatic jmethodID ni_ia4ctrID;
4632N/Astatic int initialized = 0;
4632N/A
4632N/A/*
4632N/A * Find an internet address for a given hostname. Note that this
4632N/A * code only works for addresses of type INET. The translation
4632N/A * of %d.%d.%d.%d to an address (int) occurs in java now, so the
4632N/A * String "host" shouldn't *ever* be a %d.%d.%d.%d string
4632N/A *
4632N/A * Class: java_net_Inet4AddressImpl
4632N/A * Method: lookupAllHostAddr
4632N/A * Signature: (Ljava/lang/String;)[[B
4632N/A */
4632N/A
4632N/AJNIEXPORT jobjectArray JNICALL
4632N/AJava_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
4632N/A jstring host) {
4632N/A const char *hostname;
4632N/A jobject name;
4632N/A jobjectArray ret = 0;
4632N/A int retLen = 0;
4632N/A
4632N/A int error=0;
4632N/A struct addrinfo hints, *res, *resNew = NULL;
4632N/A
4632N/A if (!initialized) {
4632N/A ni_iacls = (*env)->FindClass(env, "java/net/InetAddress");
4632N/A ni_iacls = (*env)->NewGlobalRef(env, ni_iacls);
4632N/A ni_ia4cls = (*env)->FindClass(env, "java/net/Inet4Address");
4632N/A ni_ia4cls = (*env)->NewGlobalRef(env, ni_ia4cls);
4632N/A ni_ia4ctrID = (*env)->GetMethodID(env, ni_ia4cls, "<init>", "()V");
4632N/A initialized = 1;
4632N/A }
4632N/A
4632N/A if (IS_NULL(host)) {
4632N/A JNU_ThrowNullPointerException(env, "host is null");
4632N/A return 0;
4632N/A }
4632N/A hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
4632N/A CHECK_NULL_RETURN(hostname, NULL);
4632N/A
4632N/A memset(&hints, 0, sizeof(hints));
4632N/A hints.ai_flags = AI_CANONNAME;
4632N/A hints.ai_family = AF_INET;
4632N/A
4632N/A /*
4632N/A * Workaround for Solaris bug 4160367 - if a hostname contains a
4632N/A * white space then 0.0.0.0 is returned
4632N/A */
4632N/A if (isspace((unsigned char)hostname[0])) {
4632N/A JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException",
4632N/A (char *)hostname);
4632N/A JNU_ReleaseStringPlatformChars(env, host, hostname);
4632N/A return NULL;
4632N/A }
4632N/A
4632N/A error = getaddrinfo(hostname, NULL, &hints, &res);
4632N/A
4632N/A if (error) {
4632N/A /* report error */
4632N/A JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException",
4632N/A (char *)hostname);
4632N/A JNU_ReleaseStringPlatformChars(env, host, hostname);
4632N/A return NULL;
4632N/A } else {
4632N/A int i = 0;
4632N/A struct addrinfo *itr, *last = NULL, *iterator = res;
4632N/A while (iterator != NULL) {
4632N/A int skip = 0;
4632N/A itr = resNew;
4632N/A
4632N/A while (itr != NULL) {
4632N/A struct sockaddr_in *addr1, *addr2;
4632N/A
4632N/A addr1 = (struct sockaddr_in *)iterator->ai_addr;
4632N/A addr2 = (struct sockaddr_in *)itr->ai_addr;
4632N/A if (addr1->sin_addr.s_addr ==
4632N/A addr2->sin_addr.s_addr) {
4632N/A skip = 1;
4632N/A break;
4632N/A }
4632N/A
4632N/A itr = itr->ai_next;
4632N/A }
4632N/A
4632N/A if (!skip) {
4632N/A struct addrinfo *next
4632N/A = (struct addrinfo*) malloc(sizeof(struct addrinfo));
4632N/A if (!next) {
5476N/A JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
4632N/A ret = NULL;
4632N/A goto cleanupAndReturn;
4632N/A }
4632N/A memcpy(next, iterator, sizeof(struct addrinfo));
4632N/A next->ai_next = NULL;
4632N/A if (resNew == NULL) {
4632N/A resNew = next;
4632N/A } else {
4632N/A last->ai_next = next;
4632N/A }
4632N/A last = next;
4632N/A i++;
4632N/A }
4632N/A iterator = iterator->ai_next;
4632N/A }
4632N/A
4632N/A retLen = i;
4632N/A iterator = resNew;
4632N/A i = 0;
4632N/A
4632N/A name = (*env)->NewStringUTF(env, hostname);
4632N/A if (IS_NULL(name)) {
4632N/A goto cleanupAndReturn;
4632N/A }
4632N/A
4632N/A ret = (*env)->NewObjectArray(env, retLen, ni_iacls, NULL);
4632N/A if (IS_NULL(ret)) {
4632N/A /* we may have memory to free at the end of this */
4632N/A goto cleanupAndReturn;
4632N/A }
4632N/A
4632N/A while (iterator != NULL) {
4632N/A /* We need 4 bytes to store ipv4 address; */
4632N/A int len = 4;
4632N/A
4632N/A jobject iaObj = (*env)->NewObject(env, ni_ia4cls, ni_ia4ctrID);
4632N/A if (IS_NULL(iaObj)) {
4632N/A /* we may have memory to free at the end of this */
4632N/A ret = NULL;
4632N/A goto cleanupAndReturn;
4632N/A }
5888N/A setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in*)(iterator->ai_addr))->sin_addr.s_addr));
5888N/A setInetAddress_hostName(env, iaObj, name);
4632N/A (*env)->SetObjectArrayElement(env, ret, retLen - i -1, iaObj);
4632N/A i++;
4632N/A iterator = iterator->ai_next;
4632N/A }
4632N/A }
4632N/A
4632N/AcleanupAndReturn:
4632N/A {
4632N/A struct addrinfo *iterator, *tmp;
4632N/A iterator = resNew;
4632N/A while (iterator != NULL) {
4632N/A tmp = iterator;
4632N/A iterator = iterator->ai_next;
4632N/A free(tmp);
4632N/A }
4632N/A JNU_ReleaseStringPlatformChars(env, host, hostname);
4632N/A }
4632N/A
4632N/A freeaddrinfo(res);
4632N/A
4632N/A return ret;
4632N/A
4632N/A}
4632N/A
4632N/A/*
4632N/A * Class: java_net_Inet4AddressImpl
4632N/A * Method: getHostByAddr
4632N/A * Signature: (I)Ljava/lang/String;
4632N/A */
4632N/AJNIEXPORT jstring JNICALL
4632N/AJava_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this,
4632N/A jbyteArray addrArray) {
4632N/A jstring ret = NULL;
4632N/A
4632N/A char host[NI_MAXHOST+1];
4632N/A jfieldID fid;
4632N/A int error = 0;
4632N/A jint family;
4632N/A struct sockaddr *him ;
4632N/A int len = 0;
4632N/A jbyte caddr[4];
4632N/A jint addr;
4632N/A
4632N/A struct sockaddr_in him4;
4632N/A struct sockaddr *sa;
4632N/A
4632N/A /*
4632N/A * For IPv4 addresses construct a sockaddr_in structure.
4632N/A */
4632N/A (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
4632N/A addr = ((caddr[0]<<24) & 0xff000000);
4632N/A addr |= ((caddr[1] <<16) & 0xff0000);
4632N/A addr |= ((caddr[2] <<8) & 0xff00);
4632N/A addr |= (caddr[3] & 0xff);
4632N/A memset((char *) &him4, 0, sizeof(him4));
4632N/A him4.sin_addr.s_addr = (uint32_t) htonl(addr);
4632N/A him4.sin_family = AF_INET;
4632N/A sa = (struct sockaddr *) &him4;
4632N/A len = sizeof(him4);
4632N/A
4632N/A error = getnameinfo(sa, len, host, NI_MAXHOST, NULL, 0,
4632N/A NI_NAMEREQD);
4632N/A
4632N/A if (!error) {
4632N/A ret = (*env)->NewStringUTF(env, host);
4632N/A }
4632N/A
4632N/A if (ret == NULL) {
4632N/A JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", NULL);
4632N/A }
4632N/A
4632N/A return ret;
4632N/A
4632N/A}
4632N/A
4632N/A#else /* defined(_ALLBSD_SOURCE) && !defined(HAS_GLIBC_GETHOSTBY_R) */
4632N/A
0N/A/* the initial size of our hostent buffers */
0N/A#define HENT_BUF_SIZE 1024
0N/A#define BIG_HENT_BUF_SIZE 10240 /* a jumbo-sized one */
0N/A
0N/A/************************************************************************
0N/A * Inet4AddressImpl
0N/A */
0N/A
0N/A/*
0N/A * Class: java_net_Inet4AddressImpl
0N/A * Method: getLocalHostName
0N/A * Signature: ()Ljava/lang/String;
0N/A */
0N/AJNIEXPORT jstring JNICALL
0N/AJava_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {
0N/A char hostname[MAXHOSTNAMELEN+1];
0N/A
0N/A hostname[0] = '\0';
3745N/A if (JVM_GetHostName(hostname, sizeof(hostname))) {
0N/A /* Something went wrong, maybe networking is not setup? */
0N/A strcpy(hostname, "localhost");
0N/A } else {
0N/A#ifdef __linux__
0N/A /* On Linux gethostname() says "host.domain.sun.com". On
0N/A * Solaris gethostname() says "host", so extra work is needed.
0N/A */
0N/A#else
0N/A /* Solaris doesn't want to give us a fully qualified domain name.
0N/A * We do a reverse lookup to try and get one. This works
0N/A * if DNS occurs before NIS in /etc/resolv.conf, but fails
0N/A * if NIS comes first (it still gets only a partial name).
0N/A * We use thread-safe system calls.
0N/A */
0N/A#endif /* __linux__ */
0N/A struct hostent res, res2, *hp;
3658N/A // these buffers must be pointer-aligned so they are declared
3658N/A // with pointer type
3658N/A char *buf[HENT_BUF_SIZE/(sizeof (char *))];
3658N/A char *buf2[HENT_BUF_SIZE/(sizeof (char *))];
0N/A int h_error=0;
0N/A
3745N/A // ensure null-terminated
3745N/A hostname[MAXHOSTNAMELEN] = '\0';
3745N/A
4632N/A#ifdef HAS_GLIBC_GETHOSTBY_R
3658N/A gethostbyname_r(hostname, &res, (char*)buf, sizeof(buf), &hp, &h_error);
0N/A#else
3658N/A hp = gethostbyname_r(hostname, &res, (char*)buf, sizeof(buf), &h_error);
0N/A#endif
0N/A if (hp) {
4632N/A#ifdef HAS_GLIBC_GETHOSTBY_R
0N/A gethostbyaddr_r(hp->h_addr, hp->h_length, AF_INET,
3658N/A &res2, (char*)buf2, sizeof(buf2), &hp, &h_error);
0N/A#else
0N/A hp = gethostbyaddr_r(hp->h_addr, hp->h_length, AF_INET,
3658N/A &res2, (char*)buf2, sizeof(buf2), &h_error);
0N/A#endif
0N/A if (hp) {
0N/A /*
0N/A * If gethostbyaddr_r() found a fully qualified host name,
0N/A * returns that name. Otherwise, returns the hostname
0N/A * found by gethostname().
0N/A */
0N/A char *p = hp->h_name;
0N/A if ((strlen(hp->h_name) > strlen(hostname))
0N/A && (strncmp(hostname, hp->h_name, strlen(hostname)) == 0)
0N/A && (*(p + strlen(hostname)) == '.'))
0N/A strcpy(hostname, hp->h_name);
0N/A }
0N/A }
0N/A }
0N/A return (*env)->NewStringUTF(env, hostname);
0N/A}
0N/A
0N/Astatic jclass ni_iacls;
0N/Astatic jclass ni_ia4cls;
0N/Astatic jmethodID ni_ia4ctrID;
0N/Astatic int initialized = 0;
0N/A
0N/A/*
0N/A * Find an internet address for a given hostname. Note that this
0N/A * code only works for addresses of type INET. The translation
0N/A * of %d.%d.%d.%d to an address (int) occurs in java now, so the
0N/A * String "host" shouldn't *ever* be a %d.%d.%d.%d string
0N/A *
0N/A * Class: java_net_Inet4AddressImpl
0N/A * Method: lookupAllHostAddr
0N/A * Signature: (Ljava/lang/String;)[[B
0N/A */
0N/A
0N/AJNIEXPORT jobjectArray JNICALL
0N/AJava_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
0N/A jstring host) {
0N/A const char *hostname;
0N/A jobjectArray ret = 0;
0N/A struct hostent res, *hp = 0;
3658N/A // this buffer must be pointer-aligned so is declared
3658N/A // with pointer type
3658N/A char *buf[HENT_BUF_SIZE/(sizeof (char *))];
0N/A
0N/A /* temporary buffer, on the off chance we need to expand */
0N/A char *tmp = NULL;
0N/A int h_error=0;
0N/A
0N/A if (!initialized) {
0N/A ni_iacls = (*env)->FindClass(env, "java/net/InetAddress");
0N/A ni_iacls = (*env)->NewGlobalRef(env, ni_iacls);
0N/A ni_ia4cls = (*env)->FindClass(env, "java/net/Inet4Address");
0N/A ni_ia4cls = (*env)->NewGlobalRef(env, ni_ia4cls);
0N/A ni_ia4ctrID = (*env)->GetMethodID(env, ni_ia4cls, "<init>", "()V");
0N/A initialized = 1;
0N/A }
0N/A
0N/A if (IS_NULL(host)) {
0N/A JNU_ThrowNullPointerException(env, "host is null");
0N/A return 0;
0N/A }
0N/A hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
0N/A CHECK_NULL_RETURN(hostname, NULL);
0N/A
892N/A#ifdef __solaris__
0N/A /*
0N/A * Workaround for Solaris bug 4160367 - if a hostname contains a
0N/A * white space then 0.0.0.0 is returned
0N/A */
892N/A if (isspace((unsigned char)hostname[0])) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException",
0N/A (char *)hostname);
0N/A JNU_ReleaseStringPlatformChars(env, host, hostname);
0N/A return NULL;
0N/A }
892N/A#endif
0N/A
0N/A /* Try once, with our static buffer. */
4632N/A#ifdef HAS_GLIBC_GETHOSTBY_R
3658N/A gethostbyname_r(hostname, &res, (char*)buf, sizeof(buf), &hp, &h_error);
0N/A#else
3658N/A hp = gethostbyname_r(hostname, &res, (char*)buf, sizeof(buf), &h_error);
0N/A#endif
0N/A
0N/A /* With the re-entrant system calls, it's possible that the buffer
0N/A * we pass to it is not large enough to hold an exceptionally
0N/A * large DNS entry. This is signaled by errno->ERANGE. We try once
0N/A * more, with a very big size.
0N/A */
0N/A if (hp == NULL && errno == ERANGE) {
0N/A if ((tmp = (char*)malloc(BIG_HENT_BUF_SIZE))) {
4632N/A#ifdef HAS_GLIBC_GETHOSTBY_R
0N/A gethostbyname_r(hostname, &res, tmp, BIG_HENT_BUF_SIZE,
0N/A &hp, &h_error);
0N/A#else
0N/A hp = gethostbyname_r(hostname, &res, tmp, BIG_HENT_BUF_SIZE,
0N/A &h_error);
0N/A#endif
0N/A }
0N/A }
0N/A if (hp != NULL) {
0N/A struct in_addr **addrp = (struct in_addr **) hp->h_addr_list;
0N/A int i = 0;
0N/A
0N/A while (*addrp != (struct in_addr *) 0) {
0N/A i++;
0N/A addrp++;
0N/A }
0N/A
0N/A ret = (*env)->NewObjectArray(env, i, ni_iacls, NULL);
0N/A if (IS_NULL(ret)) {
0N/A /* we may have memory to free at the end of this */
0N/A goto cleanupAndReturn;
0N/A }
0N/A addrp = (struct in_addr **) hp->h_addr_list;
0N/A i = 0;
0N/A while (*addrp) {
0N/A jobject iaObj = (*env)->NewObject(env, ni_ia4cls, ni_ia4ctrID);
0N/A if (IS_NULL(iaObj)) {
0N/A ret = NULL;
0N/A goto cleanupAndReturn;
0N/A }
5888N/A setInetAddress_addr(env, iaObj, ntohl((*addrp)->s_addr));
5888N/A setInetAddress_hostName(env, iaObj, host);
0N/A (*env)->SetObjectArrayElement(env, ret, i, iaObj);
0N/A addrp++;
0N/A i++;
0N/A }
0N/A } else {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException",
0N/A (char *)hostname);
0N/A ret = NULL;
0N/A }
0N/A
0N/AcleanupAndReturn:
0N/A JNU_ReleaseStringPlatformChars(env, host, hostname);
0N/A if (tmp != NULL) {
0N/A free(tmp);
0N/A }
0N/A return ret;
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_Inet4AddressImpl
0N/A * Method: getHostByAddr
0N/A * Signature: (I)Ljava/lang/String;
0N/A */
0N/AJNIEXPORT jstring JNICALL
0N/AJava_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this,
0N/A jbyteArray addrArray) {
0N/A jstring ret = NULL;
0N/A jint addr;
0N/A struct hostent hent, *hp = 0;
3658N/A // this buffer must be pointer-aligned so is declared
3658N/A // with pointer type
3658N/A char *buf[HENT_BUF_SIZE/(sizeof (char *))];
0N/A int h_error = 0;
0N/A char *tmp = NULL;
0N/A
0N/A /*
0N/A * We are careful here to use the reentrant version of
0N/A * gethostbyname because at the Java level this routine is not
0N/A * protected by any synchronization.
0N/A *
0N/A * Still keeping the reentrant platform dependent calls temporarily
0N/A * We should probably conform to one interface later.
0N/A *
0N/A */
0N/A jbyte caddr[4];
0N/A (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
0N/A addr = ((caddr[0]<<24) & 0xff000000);
0N/A addr |= ((caddr[1] <<16) & 0xff0000);
0N/A addr |= ((caddr[2] <<8) & 0xff00);
0N/A addr |= (caddr[3] & 0xff);
0N/A addr = htonl(addr);
4632N/A#ifdef HAS_GLIBC_GETHOSTBY_R
0N/A gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, &hent,
3658N/A (char*)buf, sizeof(buf), &hp, &h_error);
0N/A#else
0N/A hp = gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, &hent,
3658N/A (char*)buf, sizeof(buf), &h_error);
0N/A#endif
0N/A /* With the re-entrant system calls, it's possible that the buffer
0N/A * we pass to it is not large enough to hold an exceptionally
0N/A * large DNS entry. This is signaled by errno->ERANGE. We try once
0N/A * more, with a very big size.
0N/A */
0N/A if (hp == NULL && errno == ERANGE) {
0N/A if ((tmp = (char*)malloc(BIG_HENT_BUF_SIZE))) {
4632N/A#ifdef HAS_GLIBC_GETHOSTBY_R
0N/A gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET,
0N/A &hent, tmp, BIG_HENT_BUF_SIZE, &hp, &h_error);
0N/A#else
0N/A hp = gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET,
0N/A &hent, tmp, BIG_HENT_BUF_SIZE, &h_error);
0N/A#endif
0N/A } else {
0N/A JNU_ThrowOutOfMemoryError(env, "getHostByAddr");
0N/A }
0N/A }
0N/A if (hp == NULL) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", NULL);
0N/A } else {
0N/A ret = (*env)->NewStringUTF(env, hp->h_name);
0N/A }
0N/A if (tmp) {
0N/A free(tmp);
0N/A }
0N/A return ret;
0N/A}
0N/A
4632N/A#endif /* _ALLBSD_SOURCE */
4632N/A
0N/A#define SET_NONBLOCKING(fd) { \
0N/A int flags = fcntl(fd, F_GETFL); \
0N/A flags |= O_NONBLOCK; \
0N/A fcntl(fd, F_SETFL, flags); \
0N/A}
0N/A
0N/A/**
0N/A * ping implementation.
0N/A * Send a ICMP_ECHO_REQUEST packet every second until either the timeout
0N/A * expires or a answer is received.
0N/A * Returns true is an ECHO_REPLY is received, otherwise, false.
0N/A */
0N/Astatic jboolean
0N/Aping4(JNIEnv *env, jint fd, struct sockaddr_in* him, jint timeout,
0N/A struct sockaddr_in* netif, jint ttl) {
0N/A jint size;
892N/A jint n, hlen1, icmplen;
892N/A socklen_t len;
0N/A char sendbuf[1500];
0N/A char recvbuf[1500];
0N/A struct icmp *icmp;
0N/A struct ip *ip;
0N/A struct sockaddr_in sa_recv;
0N/A jchar pid;
0N/A jint tmout2, seq = 1;
0N/A struct timeval tv;
0N/A size_t plen;
0N/A
0N/A /* icmp_id is a 16 bit data type, therefore down cast the pid */
0N/A pid = (jchar)getpid();
0N/A size = 60*1024;
0N/A setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
0N/A /*
0N/A * sets the ttl (max number of hops)
0N/A */
0N/A if (ttl > 0) {
0N/A setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
0N/A }
0N/A /*
0N/A * a specific interface was specified, so let's bind the socket
0N/A * to that interface to ensure the requests are sent only through it.
0N/A */
0N/A if (netif != NULL) {
0N/A if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
0N/A NET_ThrowNew(env, errno, "Can't bind socket");
0N/A close(fd);
0N/A return JNI_FALSE;
0N/A }
0N/A }
0N/A /*
0N/A * Make the socket non blocking so we can use select
0N/A */
0N/A SET_NONBLOCKING(fd);
0N/A do {
0N/A /*
0N/A * create the ICMP request
0N/A */
0N/A icmp = (struct icmp *) sendbuf;
0N/A icmp->icmp_type = ICMP_ECHO;
0N/A icmp->icmp_code = 0;
0N/A icmp->icmp_id = htons(pid);
0N/A icmp->icmp_seq = htons(seq);
0N/A seq++;
0N/A gettimeofday(&tv, NULL);
0N/A memcpy(icmp->icmp_data, &tv, sizeof(tv));
0N/A plen = ICMP_ADVLENMIN + sizeof(tv);
0N/A icmp->icmp_cksum = 0;
0N/A icmp->icmp_cksum = in_cksum((u_short *)icmp, plen);
0N/A /*
0N/A * send it
0N/A */
0N/A n = sendto(fd, sendbuf, plen, 0, (struct sockaddr *)him,
0N/A sizeof(struct sockaddr));
0N/A if (n < 0 && errno != EINPROGRESS ) {
2983N/A#ifdef __linux__
5201N/A if (errno != EINVAL && errno != EHOSTUNREACH)
2983N/A /*
2983N/A * On some Linuxes, when bound to the loopback interface, sendto
5201N/A * will fail and errno will be set to EINVAL or EHOSTUNREACH.
5201N/A * When that happens, don't throw an exception, just return false.
2983N/A */
2983N/A#endif /*__linux__ */
2983N/A NET_ThrowNew(env, errno, "Can't send ICMP packet");
0N/A close(fd);
0N/A return JNI_FALSE;
0N/A }
0N/A
0N/A tmout2 = timeout > 1000 ? 1000 : timeout;
0N/A do {
0N/A tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2);
0N/A if (tmout2 >= 0) {
0N/A len = sizeof(sa_recv);
0N/A n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&sa_recv, &len);
0N/A ip = (struct ip*) recvbuf;
0N/A hlen1 = (ip->ip_hl) << 2;
0N/A icmp = (struct icmp *) (recvbuf + hlen1);
0N/A icmplen = n - hlen1;
0N/A /*
0N/A * We did receive something, but is it what we were expecting?
0N/A * I.E.: A ICMP_ECHOREPLY packet with the proper PID.
0N/A */
5518N/A if (icmplen >= 8 && icmp->icmp_type == ICMP_ECHOREPLY
5518N/A && (ntohs(icmp->icmp_id) == pid)) {
5518N/A if ((him->sin_addr.s_addr == sa_recv.sin_addr.s_addr)) {
5518N/A close(fd);
5518N/A return JNI_TRUE;
5518N/A }
5518N/A
5518N/A if (him->sin_addr.s_addr == 0) {
5518N/A close(fd);
5518N/A return JNI_TRUE;
5518N/A }
5518N/A }
5518N/A
0N/A }
0N/A } while (tmout2 > 0);
0N/A timeout -= 1000;
0N/A } while (timeout >0);
0N/A close(fd);
0N/A return JNI_FALSE;
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_Inet4AddressImpl
0N/A * Method: isReachable0
0N/A * Signature: ([bI[bI)Z
0N/A */
0N/AJNIEXPORT jboolean JNICALL
0N/AJava_java_net_Inet4AddressImpl_isReachable0(JNIEnv *env, jobject this,
0N/A jbyteArray addrArray,
0N/A jint timeout,
0N/A jbyteArray ifArray,
0N/A jint ttl) {
0N/A jint addr;
0N/A jbyte caddr[4];
0N/A jint fd;
0N/A struct sockaddr_in him;
0N/A struct sockaddr_in* netif = NULL;
0N/A struct sockaddr_in inf;
0N/A int len = 0;
0N/A int connect_rv = -1;
0N/A int sz;
0N/A
0N/A memset((char *) caddr, 0, sizeof(caddr));
0N/A memset((char *) &him, 0, sizeof(him));
4638N/A memset((char *) &inf, 0, sizeof(inf));
0N/A sz = (*env)->GetArrayLength(env, addrArray);
0N/A if (sz != 4) {
0N/A return JNI_FALSE;
0N/A }
0N/A (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
0N/A addr = ((caddr[0]<<24) & 0xff000000);
0N/A addr |= ((caddr[1] <<16) & 0xff0000);
0N/A addr |= ((caddr[2] <<8) & 0xff00);
0N/A addr |= (caddr[3] & 0xff);
0N/A addr = htonl(addr);
0N/A him.sin_addr.s_addr = addr;
0N/A him.sin_family = AF_INET;
0N/A len = sizeof(him);
0N/A /*
0N/A * If a network interface was specified, let's create the address
0N/A * for it.
0N/A */
0N/A if (!(IS_NULL(ifArray))) {
0N/A memset((char *) caddr, 0, sizeof(caddr));
0N/A (*env)->GetByteArrayRegion(env, ifArray, 0, 4, caddr);
0N/A addr = ((caddr[0]<<24) & 0xff000000);
0N/A addr |= ((caddr[1] <<16) & 0xff0000);
0N/A addr |= ((caddr[2] <<8) & 0xff00);
0N/A addr |= (caddr[3] & 0xff);
0N/A addr = htonl(addr);
0N/A inf.sin_addr.s_addr = addr;
0N/A inf.sin_family = AF_INET;
0N/A inf.sin_port = 0;
0N/A netif = &inf;
0N/A }
0N/A
0N/A /*
0N/A * Let's try to create a RAW socket to send ICMP packets
0N/A * This usually requires "root" privileges, so it's likely to fail.
0N/A */
0N/A fd = JVM_Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
0N/A if (fd != -1) {
0N/A /*
0N/A * It didn't fail, so we can use ICMP_ECHO requests.
0N/A */
0N/A return ping4(env, fd, &him, timeout, netif, ttl);
0N/A }
0N/A
0N/A /*
0N/A * Can't create a raw socket, so let's try a TCP socket
0N/A */
0N/A fd = JVM_Socket(AF_INET, SOCK_STREAM, 0);
0N/A if (fd == JVM_IO_ERR) {
0N/A /* note: if you run out of fds, you may not be able to load
0N/A * the exception class, and get a NoClassDefFoundError
0N/A * instead.
0N/A */
0N/A NET_ThrowNew(env, errno, "Can't create socket");
0N/A return JNI_FALSE;
0N/A }
0N/A if (ttl > 0) {
0N/A setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
0N/A }
0N/A
0N/A /*
0N/A * A network interface was specified, so let's bind to it.
0N/A */
0N/A if (netif != NULL) {
0N/A if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
0N/A NET_ThrowNew(env, errno, "Can't bind socket");
0N/A close(fd);
0N/A return JNI_FALSE;
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Make the socket non blocking so we can use select/poll.
0N/A */
0N/A SET_NONBLOCKING(fd);
0N/A
0N/A /* no need to use NET_Connect as non-blocking */
0N/A him.sin_port = htons(7); /* Echo */
0N/A connect_rv = JVM_Connect(fd, (struct sockaddr *)&him, len);
0N/A
0N/A /**
0N/A * connection established or refused immediately, either way it means
0N/A * we were able to reach the host!
0N/A */
0N/A if (connect_rv == 0 || errno == ECONNREFUSED) {
0N/A close(fd);
0N/A return JNI_TRUE;
0N/A } else {
0N/A int optlen;
0N/A
0N/A switch (errno) {
0N/A case ENETUNREACH: /* Network Unreachable */
0N/A case EAFNOSUPPORT: /* Address Family not supported */
0N/A case EADDRNOTAVAIL: /* address is not available on the remote machine */
0N/A#ifdef __linux__
0N/A case EINVAL:
5201N/A case EHOSTUNREACH:
0N/A /*
0N/A * On some Linuxes, when bound to the loopback interface, connect
5201N/A * will fail and errno will be set to EINVAL or EHOSTUNREACH.
5201N/A * When that happens, don't throw an exception, just return false.
0N/A */
0N/A#endif /* __linux__ */
0N/A close(fd);
0N/A return JNI_FALSE;
0N/A }
0N/A
0N/A if (errno != EINPROGRESS) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
0N/A "connect failed");
0N/A close(fd);
0N/A return JNI_FALSE;
0N/A }
0N/A
0N/A timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);
0N/A if (timeout >= 0) {
0N/A /* has connection been established? */
0N/A optlen = sizeof(connect_rv);
0N/A if (JVM_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
0N/A &optlen) <0) {
0N/A connect_rv = errno;
0N/A }
0N/A if (connect_rv == 0 || connect_rv == ECONNREFUSED) {
0N/A close(fd);
0N/A return JNI_TRUE;
0N/A }
0N/A }
0N/A close(fd);
0N/A return JNI_FALSE;
0N/A }
0N/A}