• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 #define LOG_TAG "NetworkInterface"
19 
20 #include "JNIHelp.h"
21 #include "JniConstants.h"
22 #include "JniException.h"
23 #include "jni.h"
24 #include "NetworkUtilities.h"
25 #include "ScopedFd.h"
26 
27 #include <errno.h>
28 #include <netinet/in.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/ioctl.h>
33 #include <sys/socket.h>
34 #include <unistd.h>
35 
36 #include <net/if.h> // Note: Can't appear before <sys/socket.h> on OS X.
37 
38 #ifdef HAVE_ANDROID_OS
39 #include "ifaddrs-android.h"
40 #else
41 #include <ifaddrs.h>
42 #endif
43 
44 // Ensures we always call freeifaddrs(3) to clean up after getifaddrs(3).
45 class ScopedInterfaceAddresses {
46 public:
ScopedInterfaceAddresses()47     ScopedInterfaceAddresses() : list(NULL) {
48     }
49 
init()50     bool init() {
51         int rc = getifaddrs(&list);
52         return (rc != -1);
53     }
54 
~ScopedInterfaceAddresses()55     ~ScopedInterfaceAddresses() {
56         freeifaddrs(list);
57     }
58 
59     ifaddrs* list;
60 
61 private:
62     // Disallow copy and assignment.
63     ScopedInterfaceAddresses(const ScopedInterfaceAddresses&);
64     void operator=(const ScopedInterfaceAddresses&);
65 };
66 
makeInterfaceAddress(JNIEnv * env,jint interfaceIndex,ifaddrs * ifa)67 static jobject makeInterfaceAddress(JNIEnv* env, jint interfaceIndex, ifaddrs* ifa) {
68     jmethodID constructor = env->GetMethodID(JniConstants::interfaceAddressClass, "<init>",
69             "(ILjava/lang/String;Ljava/net/InetAddress;Ljava/net/InetAddress;)V");
70     if (constructor == NULL) {
71         return NULL;
72     }
73     jobject javaName = env->NewStringUTF(ifa->ifa_name);
74     if (javaName == NULL) {
75         return NULL;
76     }
77     sockaddr_storage* addr = reinterpret_cast<sockaddr_storage*>(ifa->ifa_addr);
78     jobject javaAddress = socketAddressToInetAddress(env, addr);
79     if (javaAddress == NULL) {
80         return NULL;
81     }
82     sockaddr_storage* mask = reinterpret_cast<sockaddr_storage*>(ifa->ifa_netmask);
83     jobject javaMask = socketAddressToInetAddress(env, mask);
84     if (javaMask == NULL) {
85         return NULL;
86     }
87     return env->NewObject(JniConstants::interfaceAddressClass, constructor,
88             interfaceIndex, javaName, javaAddress, javaMask);
89 }
90 
NetworkInterface_getAllInterfaceAddressesImpl(JNIEnv * env,jclass)91 static jobjectArray NetworkInterface_getAllInterfaceAddressesImpl(JNIEnv* env, jclass) {
92     // Get the list of interface addresses.
93     ScopedInterfaceAddresses addresses;
94     if (!addresses.init()) {
95         jniThrowSocketException(env, errno);
96         return NULL;
97     }
98 
99     // Count how many there are.
100     int interfaceAddressCount = 0;
101     for (ifaddrs* ifa = addresses.list; ifa != NULL; ifa = ifa->ifa_next) {
102         ++interfaceAddressCount;
103     }
104 
105     // Build the InterfaceAddress[]...
106     jobjectArray result =
107             env->NewObjectArray(interfaceAddressCount, JniConstants::interfaceAddressClass, NULL);
108     if (result == NULL) {
109         return NULL;
110     }
111 
112     // And fill it in...
113     int arrayIndex = 0;
114     for (ifaddrs* ifa = addresses.list; ifa != NULL; ifa = ifa->ifa_next) {
115         // We're only interested in IP addresses.
116         int family = ifa->ifa_addr->sa_family;
117         if (family != AF_INET && family != AF_INET6) {
118             continue;
119         }
120         // Until we implement Java 6's NetworkInterface.isUp,
121         // we only want interfaces that are up.
122         if ((ifa->ifa_flags & IFF_UP) == 0) {
123             continue;
124         }
125         // Find the interface's index, and skip this address if
126         // the interface has gone away.
127         int interfaceIndex = if_nametoindex(ifa->ifa_name);
128         if (interfaceIndex == 0) {
129             continue;
130         }
131         // Make a new InterfaceAddress, and insert it into the array.
132         jobject element = makeInterfaceAddress(env, interfaceIndex, ifa);
133         if (element == NULL) {
134             return NULL;
135         }
136         env->SetObjectArrayElement(result, arrayIndex, element);
137         if (env->ExceptionCheck()) {
138             return NULL;
139         }
140         ++arrayIndex;
141     }
142     return result;
143 }
144 
doIoctl(JNIEnv * env,jstring name,int request,ifreq & ifr)145 static bool doIoctl(JNIEnv* env, jstring name, int request, ifreq& ifr) {
146     // Copy the name into the ifreq structure, if there's room...
147     jsize nameLength = env->GetStringLength(name);
148     if (nameLength >= IFNAMSIZ) {
149         errno = ENAMETOOLONG;
150         jniThrowSocketException(env, errno);
151         return false;
152     }
153     memset(&ifr, 0, sizeof(ifr));
154     env->GetStringUTFRegion(name, 0, nameLength, ifr.ifr_name);
155 
156     // ...and do the ioctl.
157     ScopedFd fd(socket(AF_INET, SOCK_DGRAM, 0));
158     if (fd.get() == -1) {
159         jniThrowSocketException(env, errno);
160         return false;
161     }
162     int rc = ioctl(fd.get(), request, &ifr);
163     if (rc == -1) {
164         jniThrowSocketException(env, errno);
165         return false;
166     }
167     return true;
168 }
169 
hasFlag(JNIEnv * env,jstring name,int flag)170 static jboolean hasFlag(JNIEnv* env, jstring name, int flag) {
171     ifreq ifr;
172     doIoctl(env, name, SIOCGIFFLAGS, ifr); // May throw.
173     return (ifr.ifr_flags & flag) != 0;
174 }
175 
NetworkInterface_getHardwareAddressImpl(JNIEnv * env,jclass,jstring name)176 static jbyteArray NetworkInterface_getHardwareAddressImpl(JNIEnv* env, jclass, jstring name) {
177     ifreq ifr;
178     if (!doIoctl(env, name, SIOCGIFHWADDR, ifr)) {
179         return NULL;
180     }
181     jbyte bytes[IFHWADDRLEN];
182     bool isEmpty = true;
183     for (int i = 0; i < IFHWADDRLEN; ++i) {
184         bytes[i] = ifr.ifr_hwaddr.sa_data[i];
185         if (bytes[i] != 0) {
186             isEmpty = false;
187         }
188     }
189     if (isEmpty) {
190         return NULL;
191     }
192     jbyteArray result = env->NewByteArray(IFHWADDRLEN);
193     env->SetByteArrayRegion(result, 0, IFHWADDRLEN, bytes);
194     return result;
195 }
196 
NetworkInterface_getMTUImpl(JNIEnv * env,jclass,jstring name)197 static jint NetworkInterface_getMTUImpl(JNIEnv* env, jclass, jstring name) {
198     ifreq ifr;
199     doIoctl(env, name, SIOCGIFMTU, ifr); // May throw.
200     return ifr.ifr_mtu;
201 }
202 
NetworkInterface_isLoopbackImpl(JNIEnv * env,jclass,jstring name)203 static jboolean NetworkInterface_isLoopbackImpl(JNIEnv* env, jclass, jstring name) {
204     return hasFlag(env, name, IFF_LOOPBACK);
205 }
206 
NetworkInterface_isPointToPointImpl(JNIEnv * env,jclass,jstring name)207 static jboolean NetworkInterface_isPointToPointImpl(JNIEnv* env, jclass, jstring name) {
208     return hasFlag(env, name, IFF_POINTOPOINT); // Unix API typo!
209 }
210 
NetworkInterface_isUpImpl(JNIEnv * env,jclass,jstring name)211 static jboolean NetworkInterface_isUpImpl(JNIEnv* env, jclass, jstring name) {
212     return hasFlag(env, name, IFF_UP);
213 }
214 
NetworkInterface_supportsMulticastImpl(JNIEnv * env,jclass,jstring name)215 static jboolean NetworkInterface_supportsMulticastImpl(JNIEnv* env, jclass, jstring name) {
216     return hasFlag(env, name, IFF_MULTICAST);
217 }
218 
219 static JNINativeMethod gMethods[] = {
220     NATIVE_METHOD(NetworkInterface, getAllInterfaceAddressesImpl, "()[Ljava/net/InterfaceAddress;"),
221     NATIVE_METHOD(NetworkInterface, getHardwareAddressImpl, "(Ljava/lang/String;)[B"),
222     NATIVE_METHOD(NetworkInterface, getMTUImpl, "(Ljava/lang/String;)I"),
223     NATIVE_METHOD(NetworkInterface, isLoopbackImpl, "(Ljava/lang/String;)Z"),
224     NATIVE_METHOD(NetworkInterface, isPointToPointImpl, "(Ljava/lang/String;)Z"),
225     NATIVE_METHOD(NetworkInterface, isUpImpl, "(Ljava/lang/String;)Z"),
226     NATIVE_METHOD(NetworkInterface, supportsMulticastImpl, "(Ljava/lang/String;)Z"),
227 };
register_java_net_NetworkInterface(JNIEnv * env)228 int register_java_net_NetworkInterface(JNIEnv* env) {
229     return jniRegisterNativeMethods(env, "java/net/NetworkInterface", gMethods, NELEM(gMethods));
230 }
231