package com.workshare.msnos.core.protocols.ip; import java.io.IOException; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.workshare.msnos.soup.SingleElementSet; public class Network { public static final String SYSP_NET_BINDINGS = "com.ws.msnos.network.bindings"; private static Logger log = LoggerFactory.getLogger(Network.class); private final byte[] address; private final short prefix; public Network(byte[] address, short prefix) { this.address = address; this.prefix = prefix; } public Network(InterfaceAddress inetAddress) { this(inetAddress.getAddress().getAddress(), inetAddress.getNetworkPrefixLength()); } public boolean isIpv4() { return 4 == this.address.length; } public byte[] getAddress() { return this.address; } public short getPrefix() { return this.prefix; } // FIXME public boolean isPrivate() { InetAddress inetAddress = null; try { inetAddress = InetAddress.getByAddress(address); } catch (UnknownHostException e) { log.error("Unknown host exception when checking if an address is private ", e); } return inetAddress.isSiteLocalAddress(); } public byte[] getNetmask() { byte[] netmask = new byte[address.length]; int fullmask = prefix > 0 ? 0x00 - (1 << ((8 * address.length) - prefix)) : 0xFFFFFFFF; for (int i = 0; i < address.length; i++) { int shift = 8 * (address.length - i - 1); int bytemask = fullmask; bytemask >>= shift; bytemask &= 0xff; netmask[i] = (byte) bytemask; } return netmask; } public String getHostString() { StringBuffer sb = new StringBuffer(); for (int i = 0; i < address.length; i++) { if (i > 0) sb.append('.'); int x = (int) (address[i] & 0xff); sb.append(x); } return sb.toString(); } @Override public String toString() { return getHostString() + "/" + prefix; } // TODO: dumb implementation, should be improved @Override public int hashCode() { int tot = prefix; for (byte b : address) { int i = 1 + b; tot *= i; } return tot; } @Override public boolean equals(Object obj) { try { Network other = (Network) obj; if (address.length != other.address.length) return false; if (prefix != other.prefix) return false; for (int i = 0; i < address.length; i++) { if (address[i] != other.address[i]) return false; } return true; } catch (Exception ignore) { return false; } } public static byte[] createAddressFromString(String address) throws IOException { if (address == null) return null; try { final InetAddress byName = InetAddress.getByName(address); return byName.getAddress(); } catch(UnknownHostException ex) { log.debug("Failed to resolve host {} (DNS problem?) let's check if it's a x.y.z.k address", address); if (isValidDottedIpv4Address(address)) { String[] nibbles = address.trim().split("\\."); byte[] bytes = new byte[nibbles.length]; for (int i=0; i<nibbles.length; i++) { int ival = Integer.valueOf(nibbles[i]); bytes[i] = (byte)(ival&0xff); } if (log.isDebugEnabled()) log.debug("Address resolved to {}", Arrays.asList(bytes)); return bytes; } else { log.debug("Address {} NOT resolved :(", address); return null; } } } public static Set<Network> listAll(boolean ipv4only, boolean includeVirtual) { Enumeration<NetworkInterface> nics = null; try { nics = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e) { log.error("FATAL - Socket Exception getting NIC info", e); System.exit(-1); } return listAll(nics, ipv4only, includeVirtual, new AddressResolver()); } public static Set<Network> listAll(Enumeration<NetworkInterface> nics, boolean ipv4only, boolean includeVirtual, AddressResolver addresResolver) { Set<Network> nets = resolvePublicIPs(addresResolver); if (nets.size() == 0) nets = getHardwareAddresses(nics, ipv4only, includeVirtual); return nets; } public static Set<Network> getHardwareAddresses(Enumeration<NetworkInterface> nics, boolean ipv4only, boolean includeVirtual) { String bindings = System.getProperty(SYSP_NET_BINDINGS); Set<Network> nets = new HashSet<Network>(); while (nics.hasMoreElements()) { NetworkInterface nic = nics.nextElement(); if (bindings != null && !bindings.contains(nic.getName())) { log.warn("Interface \"{}\" excluded as listed bindings are \"{}\"", nic.getName(), bindings); continue; } if (!includeVirtual && nic.isVirtual()) continue; if (isLoopback(nic)) continue; nets.addAll(list(nic, ipv4only)); } return nets; } public static Set<Network> resolvePublicIPs(AddressResolver addresResolver) { log.debug("Trying to resolve a public address on the cloud..."); Network publicIP = addresResolver.findPublicIP(); if (publicIP != null) { log.debug("Found! IP: {}", publicIP); return new SingleElementSet<Network>(publicIP); } else { log.debug("Unable to found the public IP :("); return Collections.emptySet(); } } public static Set<Network> list(NetworkInterface nic, boolean ipv4Only) { Set<Network> lans = new HashSet<Network>(); final List<InterfaceAddress> nicAddresses = nic.getInterfaceAddresses(); for (InterfaceAddress nicAddress : nicAddresses) { if (nicAddress.getAddress().isLoopbackAddress()) continue; final Network net = new Network(nicAddress); if (!net.isIpv4() && ipv4Only) continue; lans.add(net); } return lans; } private static boolean isLoopback(NetworkInterface nic) { try { return nic.isLoopback(); } catch (SocketException e) { log.warn("Unable to determine if interface {} is a loopback", nic); return false; } } public static boolean isValidDottedIpv4Address(String address) { String[] nibbles = address.trim().split("\\."); if (nibbles.length != 4) return false; for (int i=0; i<4; i++) { int ival = toInt(nibbles[i], -1); if (ival < 0 || ival > 255) return false; } return true; } private static int toInt(final String sval, int defval) { try { return Integer.valueOf(sval); } catch (Exception any) { return defval; } } }