/* * 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. */ package java.net; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import libcore.io.ErrnoException; import libcore.io.IoUtils; import libcore.io.Libcore; import libcore.io.NetworkOs; import static libcore.io.OsConstants.*; /*-[ #include "IOSPrimitiveArray.h" #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <net/if.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <ifaddrs.h> #include <net/if_dl.h> #include <sys/sockio.h> ]-*/ /** * This class is used to represent a network interface of the local device. An * interface is defined by its address and a platform dependent name. The class * provides methods to get all information about the available interfaces of the * system or to identify the local interface of a joined multicast group. */ public final class NetworkInterface extends Object { private final String name; private final int interfaceIndex; private final List<InterfaceAddress> interfaceAddresses; private final List<InetAddress> addresses; private final List<NetworkInterface> children = new LinkedList<NetworkInterface>(); private NetworkInterface parent = null; private NetworkInterface(String name, int interfaceIndex, List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses) { this.name = name; this.interfaceIndex = interfaceIndex; this.addresses = addresses; this.interfaceAddresses = interfaceAddresses; } /*-[ static void iterateAddrInfo(const char* interfaceName, BOOL (^iterator)(struct ifaddrs *)) { struct ifaddrs *ap; if (getifaddrs(&ap) < 0) { @throw JavaNetNetworkInterface_makeSocketErrnoExceptionWithNSString_withInt_( @"getifaddrs", errno); } for (struct ifaddrs *apit = ap; apit != NULL; apit = apit->ifa_next) { if (!interfaceName || !strcmp(apit->ifa_name, interfaceName)) { if (!iterator(apit)) { break; } } } freeifaddrs(ap); } typedef struct { IOSByteArray *result; int index; } GetIpv6AddressesData; ]-*/; static NetworkInterface forUnboundMulticastSocket() { // This is what the RI returns for a MulticastSocket that hasn't been constrained // to a specific interface. return new NetworkInterface(null, -1, Arrays.asList(Inet6Address.ANY), Collections.<InterfaceAddress>emptyList()); } /** * Returns the index for the network interface, or -1 if unknown. * @since 1.7 */ public int getIndex() { return interfaceIndex; } /** * Returns the name of this network interface (such as "eth0" or "lo"). */ public String getName() { return name; } /** * Returns an enumeration of the addresses bound to this network interface. */ public Enumeration<InetAddress> getInetAddresses() { return Collections.enumeration(addresses); } /** * Returns a human-readable name for this network interface. On Android, this is the same * string as returned by {@link #getName}. */ public String getDisplayName() { return name; } /** * Returns the {@code NetworkInterface} corresponding to the named network interface, or null * if no interface has this name. * * @throws SocketException if an error occurs. * @throws NullPointerException if {@code interfaceName == null}. */ public static NetworkInterface getByName(String interfaceName) throws SocketException { if (interfaceName == null) { throw new NullPointerException("interfaceName == null"); } /* * get the list of interfaces, and then loop through the list to look * for one with a matching name */ int interfaceIndex = getInterfaceIndex(interfaceName); if (interfaceIndex <= 0) { return null; } List<InetAddress> addresses = new ArrayList<InetAddress>(); List<InterfaceAddress> interfaceAddresses = new ArrayList<InterfaceAddress>(); collectIpv6Addresses(interfaceName, interfaceIndex, addresses, interfaceAddresses); collectIpv4Address(interfaceName, addresses, interfaceAddresses); return new NetworkInterface(interfaceName, interfaceIndex, addresses, interfaceAddresses); } private static SocketException rethrowAsSocketException(Exception ex) throws SocketException { SocketException result = new SocketException(); result.initCause(ex); throw result; } /** * Returns the {@code NetworkInterface} corresponding to the given address, or null if no * interface has this address. * * @throws SocketException if an error occurs. * @throws NullPointerException if {@code address == null}. */ public static NetworkInterface getByInetAddress(InetAddress address) throws SocketException { if (address == null) { throw new NullPointerException("address == null"); } for (NetworkInterface networkInterface : getNetworkInterfacesList()) { if (networkInterface.addresses.contains(address)) { return networkInterface; } } return null; } /** * Returns the NetworkInterface corresponding to the given interface index, or null if no * interface has this index. * * @throws SocketException if an error occurs. * @since 1.7 */ public static NetworkInterface getByIndex(int index) throws SocketException { String name = Libcore.os.if_indextoname(index); if (name == null) { return null; } return NetworkInterface.getByName(name); } /** * Gets a list of all network interfaces available on the local system or * {@code null} if no interface is available. * * @return the list of {@code NetworkInterface} instances representing the * available interfaces. * @throws SocketException * if an error occurs while getting the network interface * information. */ public static Enumeration<NetworkInterface> getNetworkInterfaces() throws SocketException { return Collections.enumeration(getNetworkInterfacesList()); } /** * Compares the specified object to this {@code NetworkInterface} and * returns whether they are equal or not. The object must be an instance of * {@code NetworkInterface} with the same name, display name, and list * of interface addresses. * * @param obj * the object to compare with this instance. * @return {@code true} if the specified object is equal to this {@code * NetworkInterface}, {@code false} otherwise. * @see #hashCode() */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof NetworkInterface)) { return false; } NetworkInterface rhs = (NetworkInterface) obj; // TODO: should the order of the addresses matter (we use List.equals)? return interfaceIndex == rhs.interfaceIndex && name.equals(rhs.name) && addresses.equals(rhs.addresses); } /** * Returns the hash code for this {@code NetworkInterface}. Since the * name should be unique for each network interface the hash code is * generated using the name. */ @Override public int hashCode() { return name.hashCode(); } /** * Returns a string containing details of this network interface. * The exact format is deliberately unspecified. Callers that require a specific * format should build a string themselves, using this class' accessor methods. */ @Override public String toString() { StringBuilder sb = new StringBuilder(25); sb.append("["); sb.append(name); sb.append("]["); sb.append(interfaceIndex); sb.append("]"); for (InetAddress address : addresses) { sb.append("["); sb.append(address.toString()); sb.append("]"); } return sb.toString(); } /** * Returns a List of the InterfaceAddresses for this network interface. * @since 1.6 */ public List<InterfaceAddress> getInterfaceAddresses() { return Collections.unmodifiableList(interfaceAddresses); } /** * Returns an enumeration of all the sub-interfaces of this network interface. * Sub-interfaces are also known as virtual interfaces. * * <p>For example, {@code eth0:1} would be a sub-interface of {@code eth0}. * * @return an Enumeration of all the sub-interfaces of this network interface * @since 1.6 */ public Enumeration<NetworkInterface> getSubInterfaces() { return Collections.enumeration(children); } /** * Returns the parent NetworkInterface of this interface if this is a * sub-interface, or null if it's a physical (non virtual) interface. * * @return the NetworkInterface this interface is attached to. * @since 1.6 */ public NetworkInterface getParent() { return parent; } /** * Returns true if this network interface is up. * * @return true if the interface is up. * @throws SocketException if an I/O error occurs. * @since 1.6 */ public boolean isUp() throws SocketException { return hasFlag(IFF_UP); } /** * Returns true if this network interface is a loopback interface. * * @return true if the interface is a loopback interface. * @throws SocketException if an I/O error occurs. * @since 1.6 */ public boolean isLoopback() throws SocketException { return hasFlag(IFF_LOOPBACK); } /** * Returns true if this network interface is a point-to-point interface. * (For example, a PPP connection using a modem.) * * @return true if the interface is point-to-point. * @throws SocketException if an I/O error occurs. * @since 1.6 */ public boolean isPointToPoint() throws SocketException { return hasFlag(IFF_POINTOPOINT); } /** * Returns true if this network interface supports multicast. * * @throws SocketException if an I/O error occurs. * @since 1.6 */ public boolean supportsMulticast() throws SocketException { return hasFlag(IFF_MULTICAST); } private native boolean hasFlag(int mask) throws SocketException /*-[ __block int flags = 0; iterateAddrInfo([self->name_ UTF8String], ^(struct ifaddrs *ia) { flags = ia->ifa_flags; return NO; // Stop iteration }); return (flags & mask) != 0; ]-*/; /** * Returns the hardware address of the interface, if it has one, or null otherwise. * * @throws SocketException if an I/O error occurs. * @since 1.6 */ public native byte[] getHardwareAddress() throws SocketException /*-[ const char* name = [self->name_ UTF8String]; if (!name) { return NULL; } __block IOSByteArray* result = NULL; BOOL (^hardwareAddressIterator)(struct ifaddrs *) = ^(struct ifaddrs *ia) { if (ia->ifa_addr->sa_family == AF_LINK) { struct sockaddr_dl *addr = (struct sockaddr_dl *) ia->ifa_addr; if (addr->sdl_alen == 6) { char *bytes = (char *) LLADDR(addr); result = [IOSByteArray arrayWithLength:6]; memcpy(result->buffer_, bytes, 6); return NO; // Stop iteration } } return YES; // Continue iteration }; iterateAddrInfo(name, hardwareAddressIterator); return result; ]-*/; /** * Returns the Maximum Transmission Unit (MTU) of this interface. * * @return the value of the MTU for the interface. * @throws SocketException if an I/O error occurs. * @since 1.6 */ public native int getMTU() throws SocketException /*-[ if (!self->name_) { return 0; } int sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { @throw JavaNetNetworkInterface_makeSocketErrnoExceptionWithNSString_withInt_( @"socket", errno); } struct ifreq ifreq; memset(&ifreq, 0, sizeof(struct ifreq)); strcpy(ifreq.ifr_name, [self->name_ UTF8String]); if (ioctl(sock, SIOCGIFMTU, &ifreq) < 0) { close(sock); @throw JavaNetNetworkInterface_makeSocketErrnoExceptionWithNSString_withInt_( @"ioctl", errno); } close(sock); return ifreq.ifr_mtu; ]-*/; /** * Returns true if this interface is a virtual interface (also called * a sub-interface). Virtual interfaces are, on some systems, interfaces * created as a child of a physical interface and given different settings * (like address or MTU). Usually the name of the interface will the name of * the parent followed by a colon (:) and a number identifying the child, * since there can be several virtual interfaces attached to a single * physical interface. * * @return true if this interface is a virtual interface. * @since 1.6 */ public boolean isVirtual() { return parent != null; } private static List<NetworkInterface> getNetworkInterfacesList() throws SocketException { String[] interfaceNames = getInterfaceNames(); NetworkInterface[] interfaces = new NetworkInterface[interfaceNames.length]; boolean[] done = new boolean[interfaces.length]; for (int i = 0; i < interfaceNames.length; ++i) { interfaces[i] = NetworkInterface.getByName(interfaceNames[i]); // http://b/5833739: getByName can return null if the interface went away between our // readdir(2) and our stat(2), so mark interfaces that disappeared as 'done'. if (interfaces[i] == null) { done[i] = true; } } List<NetworkInterface> result = new ArrayList<NetworkInterface>(); for (int counter = 0; counter < interfaces.length; counter++) { // If this interface has been dealt with already, continue. if (done[counter]) { continue; } int counter2 = counter; // Checks whether the following interfaces are children. for (; counter2 < interfaces.length; counter2++) { if (done[counter2]) { continue; } if (interfaces[counter2].name.startsWith(interfaces[counter].name + ":")) { interfaces[counter].children.add(interfaces[counter2]); interfaces[counter2].parent = interfaces[counter]; interfaces[counter].addresses.addAll(interfaces[counter2].addresses); done[counter2] = true; } } result.add(interfaces[counter]); done[counter] = true; } return result; } private static native String[] getInterfaceNames() /*-[ NSMutableArray *names = [NSMutableArray array]; struct ifaddrs *interfaces = NULL; if (getifaddrs(&interfaces) == 0) { struct ifaddrs *addr = interfaces; while (addr) { if (addr->ifa_addr->sa_family == AF_INET) { [names addObject:[NSString stringWithUTF8String:addr->ifa_name]]; } addr = addr->ifa_next; } } freeifaddrs(interfaces); return [IOSObjectArray arrayWithNSArray:names type:NSString_class_()]; ]-*/; private static native int getInterfaceIndex(String interfaceName) /*-[ return if_nametoindex([interfaceName UTF8String]); ]-*/; private static SocketException makeSocketErrnoException(String functionName, int errno) { return new SocketException(new ErrnoException(functionName, errno)); } private static void collectIpv6Addresses(String interfaceName, int interfaceIndex, List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses) throws SocketException { byte[] bytes = getIpv6Addresses(interfaceName); if (bytes != null) { for (int i = 0; i < bytes.length; i += 32) { byte[] addressBytes = new byte[16]; byte[] netmaskBytes = new byte[16]; System.arraycopy(bytes, i, addressBytes, 0, 16); System.arraycopy(bytes, i + 16, netmaskBytes, 0, 16); Inet6Address inet6Address = new Inet6Address(addressBytes, null, interfaceIndex); addresses.add(inet6Address); interfaceAddresses.add(new InterfaceAddress(inet6Address, (short) ipv6NetmaskToPrefixLength(netmaskBytes))); } } } private static native byte[] getIpv6Addresses(String interfaceName) /*-[ if (!interfaceName) { return nil; } const char *name = [interfaceName UTF8String]; __block int count = 0; iterateAddrInfo(name, ^(struct ifaddrs *ia) { if (ia->ifa_addr && ia->ifa_addr->sa_family == AF_INET6) { count++; } return YES; // Continue iteration }); if (count == 0) { return nil; } IOSByteArray *result = [IOSByteArray arrayWithLength:16 * 2 * count]; __block GetIpv6AddressesData data = {result, 0}; BOOL (^getIpv6AddressesIterator)(struct ifaddrs *) = ^(struct ifaddrs *ia) { if (ia->ifa_addr && ia->ifa_addr->sa_family == AF_INET6) { struct sockaddr_in6* addr = (struct sockaddr_in6*) ia->ifa_addr; struct sockaddr_in6* netmask = (struct sockaddr_in6*) ia->ifa_netmask; memcpy(data.result->buffer_ + (16 * 2 * data.index), addr->sin6_addr.s6_addr, 16); if (netmask) { memcpy(data.result->buffer_ + (16 * 2 * data.index) + 16, netmask->sin6_addr.s6_addr, 16); } data.index++; } return YES; // Continue iteration }; iterateAddrInfo(name, getIpv6AddressesIterator); return result; ]-*/; private static int ipv6NetmaskToPrefixLength(byte[] netmask) { int prefixLength = 0; int index = 0; // Find the first byte != 0xff while (index < netmask.length) { int b = netmask[index++] & 0xff; if (b != 0xff) { break; } prefixLength += 8; } if (index == netmask.length) { return prefixLength; } byte b = netmask[index]; // Find the first bit != 1 in b for (int bit = 7; bit != 0; bit--) { if ((b & (1 << bit)) == 0) { break; } prefixLength++; } return prefixLength; } private static void collectIpv4Address(String interfaceName, List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses) throws SocketException { FileDescriptor fd = null; try { fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, 0); InetAddress address = NetworkOs.ioctlInetAddress(fd, SIOCGIFADDR, interfaceName); InetAddress broadcast = Inet4Address.ANY; try { broadcast = NetworkOs.ioctlInetAddress(fd, SIOCGIFBRDADDR, interfaceName); } catch (ErrnoException e) { if (e.errno != EINVAL) { throw e; } } InetAddress netmask = NetworkOs.ioctlInetAddress(fd, SIOCGIFNETMASK, interfaceName); if (broadcast.equals(Inet4Address.ANY)) { broadcast = null; } addresses.add(address); interfaceAddresses.add(new InterfaceAddress((Inet4Address) address, (Inet4Address) broadcast, (Inet4Address) netmask)); } catch (ErrnoException errnoException) { if (errnoException.errno != EADDRNOTAVAIL && errnoException.errno != EOPNOTSUPP) { // EADDRNOTAVAIL just means no IPv4 address for this interface. // EOPNOTSUPP means the interface doesn't have an address, such as the lo interface. // Anything else is a real error. throw rethrowAsSocketException(errnoException); } } catch (Exception ex) { throw rethrowAsSocketException(ex); } finally { IoUtils.closeQuietly(fd); } } }