package com.limegroup.gnutella.util; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Arrays; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.LinkedList; import java.util.Iterator; import com.limegroup.gnutella.ByteOrder; import com.limegroup.gnutella.PushEndpoint; import com.limegroup.gnutella.RouterService; import com.limegroup.gnutella.messages.BadPacketException; import com.limegroup.gnutella.messages.IPPortCombo; import com.limegroup.gnutella.settings.ConnectionSettings; /** * This class handles common utility functions for networking tasks. */ //2345678|012345678|012345678|012345678|012345678|012345678|012345678|012345678| public final class NetworkUtils { /** * The list of invalid addresses. */ private static final byte [] INVALID_ADDRESSES_BYTE = new byte[]{(byte)0,(byte)255}; /** * The list of private addresses. */ private static final int [][] PRIVATE_ADDRESSES_BYTE = new int[][]{ {0xFF000000,0}, {0xFF000000,127 << 24}, {0xFF000000,255 << 24}, {0xFF000000,10 << 24}, {0xFFF00000,(172 << 24) | (16 << 16)}, {0xFFFF0000,(169 << 24) | (254 << 16)}, {0xFFFF0000,(192 << 24) | (168 << 16)}}; /** * The list of local addresses. */ private static final byte LOCAL_ADDRESS_BYTE = (byte)127; /** * Ensure that this class cannot be constructed. */ private NetworkUtils() {} /** * Determines if the given addr or port is valid. * Both must be valid for this to return true. */ public static boolean isValidAddressAndPort(byte[] addr, int port) { return isValidAddress(addr) && isValidPort(port); } /** * Determines if the given addr or port is valid. * Both must be valid for this to return true. */ public static boolean isValidAddressAndPort(String addr, int port) { return isValidAddress(addr) && isValidPort(port); } /** * Returns whether or not the specified port is within the valid range of * ports. * * @param port the port number to check */ public static boolean isValidPort(int port) { if((port & 0xFFFF0000) != 0) return false; if(port == 0) return false; return true; } /** * Returns whether or not the specified address is valid. */ public static boolean isValidAddress(byte[] addr) { return addr[0]!=INVALID_ADDRESSES_BYTE[0] && addr[0]!=INVALID_ADDRESSES_BYTE[1]; } /** * Returns whether or not the specified InetAddress is valid. */ public static boolean isValidAddress(InetAddress addr) { return isValidAddress(addr.getAddress()); } /** * Returns whether or not the specified host is a valid address. */ public static boolean isValidAddress(String host) { try { return isValidAddress(InetAddress.getByName(host)); } catch(UnknownHostException uhe) { return false; } } /** * Returns whether or not the supplied address is a local address. */ public static boolean isLocalAddress(InetAddress addr) { try { if( addr.getAddress()[0]==LOCAL_ADDRESS_BYTE ) return true; InetAddress address = InetAddress.getLocalHost(); return Arrays.equals(address.getAddress(), addr.getAddress()); } catch(UnknownHostException e) { return false; } } /** * Returns whether or not the two ip addresses share the same * first octet in their address. * * @param addr0 the first address to compare * @param addr1 the second address to compare */ public static boolean isCloseIP(byte[] addr0, byte[] addr1) { return addr0[0] == addr1[0]; } /** * Returns whether or not the two ip addresses share the same * first two octets in their address -- the most common * indication that they may be on the same network. * * Private networks are NOT CONSIDERED CLOSE. * * @param addr0 the first address to compare * @param addr1 the second address to compare */ public static boolean isVeryCloseIP(byte[] addr0, byte[] addr1) { // if 0 is not a private address but 1 is, then the next // check will fail anyway, so this is okay. if( isPrivateAddress(addr0) ) return false; else return addr0[0] == addr1[0] && addr0[1] == addr1[1]; } /** * Returns whether or not the given ip address shares the same * first three octets as the address for this node -- the most * common indication that they may be on the same network. * * @param addr the address to compare */ public static boolean isVeryCloseIP(byte[] addr) { return isVeryCloseIP(RouterService.getAddress(), addr); } /** * Returns whether or not this node has a private address. * * @return <tt>true</tt> if this node has a private address, * otherwise <tt>false</tt> */ public static boolean isPrivate() { return isPrivateAddress(RouterService.getAddress()); } /** * Checks to see if the given address is a firewalled address. * * @param address the address to check */ public static boolean isPrivateAddress(byte[] address) { if( !ConnectionSettings.LOCAL_IS_PRIVATE.getValue() ) return false; int addr = ((address[0] & 0xFF) << 24) | ((address[1] & 0xFF)<< 16); for (int i =0;i< 7;i++){ if ((addr & PRIVATE_ADDRESSES_BYTE[i][0]) == PRIVATE_ADDRESSES_BYTE[i][1]) return true; } return false; } /** * Utility method for determing whether or not the given * address is private taking an InetAddress object as argument * like the isLocalAddress(InetAddress) method. Delegates to * <tt>isPrivateAddress(byte[] address)</tt>. * * @return <tt>true</tt> if the specified address is private, * otherwise <tt>false</tt> */ public static boolean isPrivateAddress(InetAddress address) { return isPrivateAddress(address.getAddress()); } /** * Utility method for determing whether or not the given * address is private. Delegates to * <tt>isPrivateAddress(byte[] address)</tt>. * * Returns true if the host is unknown. * * @return <tt>true</tt> if the specified address is private, * otherwise <tt>false</tt> */ public static boolean isPrivateAddress(String address) { try { return isPrivateAddress(InetAddress.getByName(address)); } catch(UnknownHostException uhe) { return true; } } /** * Returns the ip (given in BIG-endian) format as standard * dotted-decimal, e.g., 192.168.0.1<p> * * @param ip the ip address in BIG-endian format * @return the IP address as a dotted-quad string */ public static final String ip2string(byte[] ip) { return ip2string(ip, 0); } /** * Returns the ip (given in BIG-endian) format of * buf[offset]...buf[offset+3] as standard dotted-decimal, e.g., * 192.168.0.1<p> * * @param ip the IP address to convert * @param offset the offset into the IP array to convert * @return the IP address as a dotted-quad string */ public static final String ip2string(byte[] ip, int offset) { // xxx.xxx.xxx.xxx => 15 chars StringBuffer sbuf = new StringBuffer(16); sbuf.append(ByteOrder.ubyte2int(ip[offset])); sbuf.append('.'); sbuf.append(ByteOrder.ubyte2int(ip[offset+1])); sbuf.append('.'); sbuf.append(ByteOrder.ubyte2int(ip[offset+2])); sbuf.append('.'); sbuf.append(ByteOrder.ubyte2int(ip[offset+3])); return sbuf.toString(); } /** * If host is not a valid host address, returns false. * Otherwise, returns true if connecting to host:port would connect to * this servent's listening port. * * @return <tt>true</tt> if the specified host/port combo is this servent, * otherwise <tt>false</tt>. */ public static boolean isMe(String host, int port) { byte[] cIP; try { cIP = InetAddress.getByName(host).getAddress(); } catch (IOException e) { return false; } return isMe(cIP, port); } /** * If host is not a valid host address, returns false. * Otherwise, returns true if connecting to host:port would connect to * this servent's listening port. * * @return <tt>true</tt> if the specified host/port combo is this servent, * otherwise <tt>false</tt>. */ public static boolean isMe(byte[] cIP, int port) { //Don't allow connections to yourself. We have to special //case connections to "127.*.*.*" since //they are aliases this machine. if (cIP[0]==(byte)127) { return port == RouterService.getPort(); } else { byte[] managerIP = RouterService.getAddress(); return port == RouterService.getPort() && Arrays.equals(cIP, managerIP); } } public static boolean isMe(IpPort me) { if (me == IpPortForSelf.instance()) return true; return isMe(me.getInetAddress().getAddress(),me.getPort()); } /** * Determines if the given socket is from a local host. */ public static boolean isLocalHost(Socket s) { String hostAddress = s.getInetAddress().getHostAddress(); return "127.0.0.1".equals(hostAddress); } /** * Packs a Collection of IpPorts into a byte array. */ public static byte[] packIpPorts(Collection ipPorts) { byte[] data = new byte[ipPorts.size() * 6]; int offset = 0; for(Iterator i = ipPorts.iterator(); i.hasNext(); ) { IpPort next = (IpPort)i.next(); byte[] addr = next.getInetAddress().getAddress(); int port = next.getPort(); System.arraycopy(addr, 0, data, offset, 4); offset += 4; ByteOrder.short2leb((short)port, data, offset); offset += 2; } return data; } /** * parses an ip:port byte-packed values. * * @return a collection of <tt>IpPort</tt> objects. * @throws BadPacketException if an invalid Ip is found or the size * is not divisble by six */ public static List unpackIps(byte [] data) throws BadPacketException { if (data.length % 6 != 0) throw new BadPacketException("invalid size"); int size = data.length/6; List ret = new ArrayList(size); byte [] current = new byte[6]; for (int i=0;i<size;i++) { System.arraycopy(data,i*6,current,0,6); ret.add(IPPortCombo.getCombo(current)); } return Collections.unmodifiableList(ret); } public static List unpackPushEPs(InputStream is) throws BadPacketException,IOException { List ret = new LinkedList(); DataInputStream dais = new DataInputStream(is); while (dais.available() > 0) ret.add(PushEndpoint.fromBytes(dais)); return Collections.unmodifiableList(ret); } /** * Returns an InetAddress representing the given IP address. */ public static InetAddress getByAddress(byte[] addr) throws UnknownHostException { String addrString = NetworkUtils.ip2string(addr); return InetAddress.getByName(addrString); } /** * @return whether the IpPort is a valid external address. */ public static boolean isValidExternalIpPort(IpPort addr) { if (addr == null) return false; byte [] b = addr.getInetAddress().getAddress(); return isValidAddress(b) && !isPrivateAddress(b) && isValidPort(addr.getPort()); } /** * @return A non-loopback IPv4 address of a network interface on the local * host. * @throws UnknownHostException */ public static InetAddress getLocalAddress() throws UnknownHostException { InetAddress addr = InetAddress.getLocalHost(); if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) { return addr; } try { Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); if (interfaces != null) { while (interfaces.hasMoreElements()) { Enumeration addresses = ((NetworkInterface)interfaces.nextElement()).getInetAddresses(); while (addresses.hasMoreElements()) { addr = (InetAddress)addresses.nextElement(); if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) { return addr; } } } } } catch (SocketException se) { } throw new UnknownHostException( "localhost has no interface with a non-loopback IPv4 address"); } }