blob: f8f0ba46b6896c027c488a4d5388767cd5121a9e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "NetworkInterface"
#include "JNIHelp.h"
#include "JniConstants.h"
#include "JniException.h"
#include "jni.h"
#include "NetworkUtilities.h"
#include "ScopedFd.h"
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <net/if.h> // Note: Can't appear before <sys/socket.h> on OS X.
#ifdef HAVE_ANDROID_OS
#include "ifaddrs-android.h"
#else
#include <ifaddrs.h>
#endif
// Ensures we always call freeifaddrs(3) to clean up after getifaddrs(3).
class ScopedInterfaceAddresses {
public:
ScopedInterfaceAddresses() : list(NULL) {
}
bool init() {
int rc = getifaddrs(&list);
return (rc != -1);
}
~ScopedInterfaceAddresses() {
freeifaddrs(list);
}
ifaddrs* list;
private:
// Disallow copy and assignment.
ScopedInterfaceAddresses(const ScopedInterfaceAddresses&);
void operator=(const ScopedInterfaceAddresses&);
};
static jobject makeInterfaceAddress(JNIEnv* env, jint interfaceIndex, ifaddrs* ifa) {
jmethodID constructor = env->GetMethodID(JniConstants::interfaceAddressClass, "<init>",
"(ILjava/lang/String;Ljava/net/InetAddress;Ljava/net/InetAddress;)V");
if (constructor == NULL) {
return NULL;
}
jobject javaName = env->NewStringUTF(ifa->ifa_name);
if (javaName == NULL) {
return NULL;
}
sockaddr_storage* addr = reinterpret_cast<sockaddr_storage*>(ifa->ifa_addr);
jobject javaAddress = socketAddressToInetAddress(env, addr);
if (javaAddress == NULL) {
return NULL;
}
sockaddr_storage* mask = reinterpret_cast<sockaddr_storage*>(ifa->ifa_netmask);
jobject javaMask = socketAddressToInetAddress(env, mask);
if (javaMask == NULL) {
return NULL;
}
return env->NewObject(JniConstants::interfaceAddressClass, constructor,
interfaceIndex, javaName, javaAddress, javaMask);
}
static jobjectArray NetworkInterface_getAllInterfaceAddressesImpl(JNIEnv* env, jclass) {
// Get the list of interface addresses.
ScopedInterfaceAddresses addresses;
if (!addresses.init()) {
jniThrowSocketException(env, errno);
return NULL;
}
// Count how many there are.
int interfaceAddressCount = 0;
for (ifaddrs* ifa = addresses.list; ifa != NULL; ifa = ifa->ifa_next) {
++interfaceAddressCount;
}
// Build the InterfaceAddress[]...
jobjectArray result =
env->NewObjectArray(interfaceAddressCount, JniConstants::interfaceAddressClass, NULL);
if (result == NULL) {
return NULL;
}
// And fill it in...
int arrayIndex = 0;
for (ifaddrs* ifa = addresses.list; ifa != NULL; ifa = ifa->ifa_next) {
// We're only interested in IP addresses.
int family = ifa->ifa_addr->sa_family;
if (family != AF_INET && family != AF_INET6) {
continue;
}
// Until we implement Java 6's NetworkInterface.isUp,
// we only want interfaces that are up.
if ((ifa->ifa_flags & IFF_UP) == 0) {
continue;
}
// Find the interface's index, and skip this address if
// the interface has gone away.
int interfaceIndex = if_nametoindex(ifa->ifa_name);
if (interfaceIndex == 0) {
continue;
}
// Make a new InterfaceAddress, and insert it into the array.
jobject element = makeInterfaceAddress(env, interfaceIndex, ifa);
if (element == NULL) {
return NULL;
}
env->SetObjectArrayElement(result, arrayIndex, element);
if (env->ExceptionCheck()) {
return NULL;
}
++arrayIndex;
}
return result;
}
static bool doIoctl(JNIEnv* env, jstring name, int request, ifreq& ifr) {
// Copy the name into the ifreq structure, if there's room...
jsize nameLength = env->GetStringLength(name);
if (nameLength >= IFNAMSIZ) {
errno = ENAMETOOLONG;
jniThrowSocketException(env, errno);
return false;
}
memset(&ifr, 0, sizeof(ifr));
env->GetStringUTFRegion(name, 0, nameLength, ifr.ifr_name);
// ...and do the ioctl.
ScopedFd fd(socket(AF_INET, SOCK_DGRAM, 0));
if (fd.get() == -1) {
jniThrowSocketException(env, errno);
return false;
}
int rc = ioctl(fd.get(), request, &ifr);
if (rc == -1) {
jniThrowSocketException(env, errno);
return false;
}
return true;
}
static jboolean hasFlag(JNIEnv* env, jstring name, int flag) {
ifreq ifr;
doIoctl(env, name, SIOCGIFFLAGS, ifr); // May throw.
return (ifr.ifr_flags & flag) != 0;
}
static jbyteArray NetworkInterface_getHardwareAddressImpl(JNIEnv* env, jclass, jstring name) {
ifreq ifr;
if (!doIoctl(env, name, SIOCGIFHWADDR, ifr)) {
return NULL;
}
jbyte bytes[IFHWADDRLEN];
bool isEmpty = true;
for (int i = 0; i < IFHWADDRLEN; ++i) {
bytes[i] = ifr.ifr_hwaddr.sa_data[i];
if (bytes[i] != 0) {
isEmpty = false;
}
}
if (isEmpty) {
return NULL;
}
jbyteArray result = env->NewByteArray(IFHWADDRLEN);
env->SetByteArrayRegion(result, 0, IFHWADDRLEN, bytes);
return result;
}
static jint NetworkInterface_getMTUImpl(JNIEnv* env, jclass, jstring name) {
ifreq ifr;
doIoctl(env, name, SIOCGIFMTU, ifr); // May throw.
return ifr.ifr_mtu;
}
static jboolean NetworkInterface_isLoopbackImpl(JNIEnv* env, jclass, jstring name) {
return hasFlag(env, name, IFF_LOOPBACK);
}
static jboolean NetworkInterface_isPointToPointImpl(JNIEnv* env, jclass, jstring name) {
return hasFlag(env, name, IFF_POINTOPOINT); // Unix API typo!
}
static jboolean NetworkInterface_isUpImpl(JNIEnv* env, jclass, jstring name) {
return hasFlag(env, name, IFF_UP);
}
static jboolean NetworkInterface_supportsMulticastImpl(JNIEnv* env, jclass, jstring name) {
return hasFlag(env, name, IFF_MULTICAST);
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(NetworkInterface, getAllInterfaceAddressesImpl, "()[Ljava/net/InterfaceAddress;"),
NATIVE_METHOD(NetworkInterface, getHardwareAddressImpl, "(Ljava/lang/String;)[B"),
NATIVE_METHOD(NetworkInterface, getMTUImpl, "(Ljava/lang/String;)I"),
NATIVE_METHOD(NetworkInterface, isLoopbackImpl, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(NetworkInterface, isPointToPointImpl, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(NetworkInterface, isUpImpl, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(NetworkInterface, supportsMulticastImpl, "(Ljava/lang/String;)Z"),
};
int register_java_net_NetworkInterface(JNIEnv* env) {
return jniRegisterNativeMethods(env, "java/net/NetworkInterface", gMethods, NELEM(gMethods));
}