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