Home | History | Annotate | Download | only in native
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "InetAddress"
     18 
     19 #define LOG_DNS 0
     20 
     21 #include "JNIHelp.h"
     22 #include "JniConstants.h"
     23 #include "LocalArray.h"
     24 #include "NetworkUtilities.h"
     25 #include "ScopedLocalRef.h"
     26 #include "ScopedUtfChars.h"
     27 #include "jni.h"
     28 #include "utils/Log.h"
     29 
     30 #include <stdio.h>
     31 #include <string.h>
     32 #include <netdb.h>
     33 #include <errno.h>
     34 
     35 #include <netinet/in.h>
     36 #include <arpa/inet.h>
     37 #include <sys/socket.h>
     38 
     39 static jstring InetAddress_gethostname(JNIEnv* env, jclass)
     40 {
     41     char name[256];
     42     int r = gethostname(name, 256);
     43     if (r == 0) {
     44         return env->NewStringUTF(name);
     45     } else {
     46         return NULL;
     47     }
     48 }
     49 
     50 #if LOG_DNS
     51 static void logIpString(addrinfo* ai, const char* name)
     52 {
     53     char ipString[INET6_ADDRSTRLEN];
     54     int result = getnameinfo(ai->ai_addr, ai->ai_addrlen, ipString,
     55                              sizeof(ipString), NULL, 0, NI_NUMERICHOST);
     56     if (result == 0) {
     57         LOGD("%s: %s (family %d, proto %d)", name, ipString, ai->ai_family,
     58              ai->ai_protocol);
     59     } else {
     60         LOGE("%s: getnameinfo: %s", name, gai_strerror(result));
     61     }
     62 }
     63 #else
     64 static inline void logIpString(addrinfo*, const char*)
     65 {
     66 }
     67 #endif
     68 
     69 static jobjectArray InetAddress_getaddrinfo(JNIEnv* env, jclass, jstring javaName) {
     70     ScopedUtfChars name(env, javaName);
     71     if (name.c_str() == NULL) {
     72         return NULL;
     73     }
     74 
     75     addrinfo hints;
     76     memset(&hints, 0, sizeof(hints));
     77     hints.ai_family = AF_UNSPEC;
     78     hints.ai_flags = AI_ADDRCONFIG;
     79     /*
     80      * If we don't specify a socket type, every address will appear twice, once
     81      * for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family
     82      * anyway, just pick one.
     83      */
     84     hints.ai_socktype = SOCK_STREAM;
     85 
     86     addrinfo* addressList = NULL;
     87     jobjectArray addressArray = NULL;
     88     int result = getaddrinfo(name.c_str(), NULL, &hints, &addressList);
     89     if (result == 0 && addressList) {
     90         // Count results so we know how to size the output array.
     91         int addressCount = 0;
     92         for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) {
     93             if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
     94                 addressCount++;
     95             }
     96         }
     97 
     98         // Prepare output array.
     99         addressArray = env->NewObjectArray(addressCount, JniConstants::byteArrayClass, NULL);
    100         if (addressArray == NULL) {
    101             // Appropriate exception will be thrown.
    102             LOGE("getaddrinfo: could not allocate array of size %i", addressCount);
    103             freeaddrinfo(addressList);
    104             return NULL;
    105         }
    106 
    107         // Examine returned addresses one by one, save them in the output array.
    108         int index = 0;
    109         for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) {
    110             sockaddr* address = ai->ai_addr;
    111             size_t addressLength = 0;
    112             void* rawAddress;
    113 
    114             switch (ai->ai_family) {
    115                 // Find the raw address length and start pointer.
    116                 case AF_INET6:
    117                     addressLength = 16;
    118                     rawAddress = &reinterpret_cast<sockaddr_in6*>(address)->sin6_addr.s6_addr;
    119                     logIpString(ai, name.c_str());
    120                     break;
    121                 case AF_INET:
    122                     addressLength = 4;
    123                     rawAddress = &reinterpret_cast<sockaddr_in*>(address)->sin_addr.s_addr;
    124                     logIpString(ai, name.c_str());
    125                     break;
    126                 default:
    127                     // Unknown address family. Skip this address.
    128                     LOGE("getaddrinfo: Unknown address family %d", ai->ai_family);
    129                     continue;
    130             }
    131 
    132             // Convert each IP address into a Java byte array.
    133             ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(addressLength));
    134             if (byteArray.get() == NULL) {
    135                 // Out of memory error will be thrown on return.
    136                 LOGE("getaddrinfo: Can't allocate %d-byte array", addressLength);
    137                 addressArray = NULL;
    138                 break;
    139             }
    140             env->SetByteArrayRegion(byteArray.get(),
    141                     0, addressLength, reinterpret_cast<jbyte*>(rawAddress));
    142             env->SetObjectArrayElement(addressArray, index, byteArray.get());
    143             index++;
    144         }
    145     } else if (result == EAI_SYSTEM && errno == EACCES) {
    146         /* No permission to use network */
    147         jniThrowException(env, "java/lang/SecurityException",
    148             "Permission denied (maybe missing INTERNET permission)");
    149     } else {
    150         jniThrowException(env, "java/net/UnknownHostException", gai_strerror(result));
    151     }
    152 
    153     if (addressList) {
    154         freeaddrinfo(addressList);
    155     }
    156 
    157     return addressArray;
    158 }
    159 
    160 /**
    161  * Looks up the name corresponding to an IP address.
    162  *
    163  * @param javaAddress: a byte array containing the raw IP address bytes. Must be
    164  *         4 or 16 bytes long.
    165  * @return the hostname.
    166  * @throws UnknownHostException: the IP address has no associated hostname.
    167  */
    168 static jstring InetAddress_getnameinfo(JNIEnv* env, jclass,
    169                                          jbyteArray javaAddress)
    170 {
    171     if (javaAddress == NULL) {
    172         jniThrowNullPointerException(env, NULL);
    173         return NULL;
    174     }
    175 
    176     // Convert the raw address bytes into a socket address structure.
    177     sockaddr_storage ss;
    178     memset(&ss, 0, sizeof(ss));
    179 
    180     size_t socklen;
    181     const size_t addressLength = env->GetArrayLength(javaAddress);
    182     if (addressLength == 4) {
    183         sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss);
    184         sin->sin_family = AF_INET;
    185         socklen = sizeof(sockaddr_in);
    186         jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr);
    187         env->GetByteArrayRegion(javaAddress, 0, 4, dst);
    188     } else if (addressLength == 16) {
    189         sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(&ss);
    190         sin6->sin6_family = AF_INET6;
    191         socklen = sizeof(sockaddr_in6);
    192         jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr);
    193         env->GetByteArrayRegion(javaAddress, 0, 16, dst);
    194     } else {
    195         // The caller already throws an exception in this case. Don't worry
    196         // about it here.
    197         return NULL;
    198     }
    199 
    200     // Look up the host name from the IP address.
    201     char name[NI_MAXHOST];
    202     int ret = getnameinfo(reinterpret_cast<sockaddr*>(&ss), socklen,
    203                           name, sizeof(name), NULL, 0, NI_NAMEREQD);
    204     if (ret != 0) {
    205         jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret));
    206         return NULL;
    207     }
    208 
    209     return env->NewStringUTF(name);
    210 }
    211 
    212 /**
    213  * Convert a Java string representing an IP address to a Java byte array.
    214  * The formats accepted are:
    215  * - IPv4:
    216  *   - 1.2.3.4
    217  *   - 1.2.4
    218  *   - 1.4
    219  *   - 4
    220  * - IPv6
    221  *   - Compressed form (2001:db8::1)
    222  *   - Uncompressed form (2001:db8:0:0:0:0:0:1)
    223  *   - IPv4-compatible (::192.0.2.0)
    224  *   - With an embedded IPv4 address (2001:db8::192.0.2.0).
    225  * IPv6 addresses may appear in square brackets.
    226  *
    227  * @param addressByteArray the byte array to convert.
    228  * @return a string with the textual representation of the address.
    229  * @throws UnknownHostException the IP address was invalid.
    230  */
    231 static jbyteArray InetAddress_ipStringToByteArray(JNIEnv* env, jobject, jstring javaString) {
    232     // Convert the String to UTF-8 bytes.
    233     ScopedUtfChars chars(env, javaString);
    234     if (chars.c_str() == NULL) {
    235         return NULL;
    236     }
    237     size_t byteCount = chars.size();
    238     LocalArray<INET6_ADDRSTRLEN> bytes(byteCount + 1);
    239     char* ipString = &bytes[0];
    240     strcpy(ipString, chars.c_str());
    241 
    242     // Accept IPv6 addresses (only) in square brackets for compatibility.
    243     if (ipString[0] == '[' && ipString[byteCount - 1] == ']' && strchr(ipString, ':') != NULL) {
    244         memmove(ipString, ipString + 1, byteCount - 2);
    245         ipString[byteCount - 2] = '\0';
    246     }
    247 
    248     jbyteArray result = NULL;
    249     addrinfo hints;
    250     memset(&hints, 0, sizeof(hints));
    251     hints.ai_flags = AI_NUMERICHOST;
    252 
    253     sockaddr_storage ss;
    254     memset(&ss, 0, sizeof(ss));
    255 
    256     addrinfo* res = NULL;
    257     int ret = getaddrinfo(ipString, NULL, &hints, &res);
    258     if (ret == 0 && res) {
    259         // Convert IPv4-mapped addresses to IPv4 addresses.
    260         // The RI states "Java will never return an IPv4-mapped address".
    261         sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(res->ai_addr);
    262         if (res->ai_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
    263             sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss);
    264             sin->sin_family = AF_INET;
    265             sin->sin_port = sin6->sin6_port;
    266             memcpy(&sin->sin_addr.s_addr, &sin6->sin6_addr.s6_addr[12], 4);
    267             result = socketAddressToByteArray(env, &ss);
    268         } else {
    269             result = socketAddressToByteArray(env, reinterpret_cast<sockaddr_storage*>(res->ai_addr));
    270         }
    271     } else {
    272         // For backwards compatibility, deal with address formats that
    273         // getaddrinfo does not support. For example, 1.2.3, 1.3, and even 3 are
    274         // valid IPv4 addresses according to the Java API. If getaddrinfo fails,
    275         // try to use inet_aton.
    276         sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss);
    277         if (inet_aton(ipString, &sin->sin_addr)) {
    278             sin->sin_family = AF_INET;
    279             sin->sin_port = 0;
    280             result = socketAddressToByteArray(env, &ss);
    281         }
    282     }
    283 
    284     if (res) {
    285         freeaddrinfo(res);
    286     }
    287 
    288     if (!result) {
    289         env->ExceptionClear();
    290         jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret));
    291     }
    292 
    293     return result;
    294 }
    295 
    296 static jstring InetAddress_byteArrayToIpString(JNIEnv* env, jobject, jbyteArray byteArray) {
    297     if (byteArray == NULL) {
    298         jniThrowNullPointerException(env, NULL);
    299         return NULL;
    300     }
    301     sockaddr_storage ss;
    302     if (!byteArrayToSocketAddress(env, NULL, byteArray, 0, &ss)) {
    303         return NULL;
    304     }
    305     // TODO: getnameinfo seems to want its length parameter to be exactly
    306     // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an
    307     // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and
    308     // then remove this hack.
    309     int sa_size;
    310     if (ss.ss_family == AF_INET) {
    311         sa_size = sizeof(sockaddr_in);
    312     } else if (ss.ss_family == AF_INET6) {
    313         sa_size = sizeof(sockaddr_in6);
    314     } else {
    315         // byteArrayToSocketAddress already threw.
    316         return NULL;
    317     }
    318     char ipString[INET6_ADDRSTRLEN];
    319     int rc = getnameinfo(reinterpret_cast<sockaddr*>(&ss), sa_size,
    320             ipString, sizeof(ipString), NULL, 0, NI_NUMERICHOST);
    321     if (rc != 0) {
    322         jniThrowException(env, "java/net/UnknownHostException", gai_strerror(rc));
    323         return NULL;
    324     }
    325     return env->NewStringUTF(ipString);
    326 }
    327 
    328 static JNINativeMethod gMethods[] = {
    329     NATIVE_METHOD(InetAddress, byteArrayToIpString, "([B)Ljava/lang/String;"),
    330     NATIVE_METHOD(InetAddress, getaddrinfo, "(Ljava/lang/String;)[[B"),
    331     NATIVE_METHOD(InetAddress, gethostname, "()Ljava/lang/String;"),
    332     NATIVE_METHOD(InetAddress, getnameinfo, "([B)Ljava/lang/String;"),
    333     NATIVE_METHOD(InetAddress, ipStringToByteArray, "(Ljava/lang/String;)[B"),
    334 };
    335 int register_java_net_InetAddress(JNIEnv* env) {
    336     return jniRegisterNativeMethods(env, "java/net/InetAddress", gMethods, NELEM(gMethods));
    337 }
    338