/** * $RCSfile: ,v $ * $Revision: $ * $Date: $ * * Copyright (C) 2004-2011 Jive Software. All rights reserved. * * Licensed 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 net.java.sipmack.sip; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import net.java.sipmack.common.Log; /** * Title: Spark Phone * Description:JAIN-SIP Audio/Video phone application * New features: NAT / STUN * * @author Thiago Rocha Camargo (thiago@jivesoftware.com) */ public class NetworkAddressManager { private static NetworkAddressManager manager = null; private static InetAddress cachedLocalhost = null; private static int index = 0; private static List<InetAddressWrapper> addresses = new ArrayList<InetAddressWrapper>(); private NetworkAddressManager() { } private void init() { setProperties(); } /** * Initializes the address manager. */ public static void start() { cacheLocalhost(null); index = 0; if (manager != null) return; manager = new NetworkAddressManager(); manager.init(); // Detect and output network configuration (Firewall and NAT type) // only used for debugging currently. } /** * Shuts down the address manager and the underlying stun lib and deletes * the manager. */ public static void shutDown() { if (manager == null) return; manager = null; } /** * Returns an InetAddress instance representing the local host or null if no * IP address for the host could be found * * @return an InetAddress instance representing the local host or null if no * IP address for the host could be found */ public static InetAddress getLocalHost() { return getLocalHost(false); } /** * Returns a localhostAddress. * * @param anyAddressIsAccepted is 0.0.0.0 accepted as a return value. * @return the address that was detected the address of the localhost. */ public static InetAddress getLocalHost(boolean anyAddressIsAccepted) { Log.debug("NETWORK DETECTION"); if (cachedLocalhost != null) { Log.debug("SELECTED IP: " + cachedLocalhost); return cachedLocalhost; } Enumeration<NetworkInterface> ifaces = null; int i = 0; // Try to get the Preferred IP Address if (SIPConfig.getPreferredNetworkAddress() != null && !SIPConfig.getPreferredNetworkAddress().equals("")) { //Preferred is local host try { SIPConfig.setPreferredNetworkAddress(InetAddress.getLocalHost().getHostAddress()); } catch (UnknownHostException e) { Log.error(e); } } if (SIPConfig.getPreferredNetworkAddress() != null && !SIPConfig.getPreferredNetworkAddress().equals("")) { try { ifaces = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e) { e.printStackTrace(); } while (ifaces.hasMoreElements()) { NetworkInterface iface = (NetworkInterface) ifaces.nextElement(); Enumeration<InetAddress> iaddresses = iface.getInetAddresses(); while (iaddresses.hasMoreElements()) { InetAddress iaddress = iaddresses.nextElement(); if (iaddress.getHostAddress().equals(SIPConfig.getPreferredNetworkAddress())) { addresses.add(new InetAddressWrapper(iaddress, i++)); break; } } } } try { ifaces = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e) { e.printStackTrace(); } // Try to get best IP ( not loopback, not linklocal and not sitelocal ) while (ifaces.hasMoreElements()) { NetworkInterface iface = (NetworkInterface) ifaces.nextElement(); Enumeration<InetAddress> iaddresses = iface.getInetAddresses(); while (iaddresses.hasMoreElements()) { InetAddress iaddress = (InetAddress) iaddresses.nextElement(); if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress() && !iaddress.isSiteLocalAddress() && !(iaddress instanceof java.net.Inet6Address)) { addresses.add(new InetAddressWrapper(iaddress, i++)); } } } try { ifaces = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e) { e.printStackTrace(); } // Try to get best IP ( not loopback and not linklocal ) while (ifaces.hasMoreElements()) { NetworkInterface iface = (NetworkInterface) ifaces.nextElement(); Enumeration<InetAddress> iaddresses = iface.getInetAddresses(); while (iaddresses.hasMoreElements()) { InetAddress iaddress = (InetAddress) iaddresses.nextElement(); if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress() && !(iaddress instanceof java.net.Inet6Address)) { addresses.add(new InetAddressWrapper(iaddress, i++)); } } } // If Address list is empty, return localhost if (addresses.isEmpty()) try { return InetAddress.getLocalHost(); } catch (UnknownHostException e) { e.printStackTrace(); } Collections.sort(addresses); cacheLocalhost(addresses.get(index).getInetAddress()); setProperties(); return addresses.get(addresses.size() - 1).getInetAddress(); } /** * Caches localhost to make address discovery faster * * @param localhost localhost */ public static boolean cacheLocalhost(InetAddress localhost) { cachedLocalhost = localhost; return true; } // EMIL: I don't see where this method is used. Should probbaly Take it away /** * Tries to obtain a mapped/public address for the specified address and * port. If the STUN lib fails, tries to retrieve localhost, if that fails * too, returns null. * * @param address the address to resolve * @param port the port whose mapping we are interested in. * @return a public address corresponding to the specified port or null if * all attempts to retrieve such an address have failed. */ public static InetSocketAddress getPublicAddressFor(String address, int port) { return new InetSocketAddress(address, port); } /** * Tries to obtain a mapped/public address for the specified port. If the * STUN lib fails, tries to retrieve localhost, if that fails too, returns * null. * * @param port the port whose mapping we are interested in. * @return a public address corresponding to the specified port or null if * all attempts to retrieve such an address have failed. */ public static InetSocketAddress getPublicAddressFor(int port) { return new InetSocketAddress(getLocalHost(), port); } /** * Determines whether the address is the result of windows auto * configuration. (i.e. One that is in the 169.254.0.0 network) * * @param add the address to inspect * @return true if the address is autoconfigured by windows, false * otherwise. */ public static boolean isWindowsAutoConfiguredIPv4Address(InetAddress add) { return (add.getAddress()[0] & 0xFF) == 169 && (add.getAddress()[1] & 0xFF) == 254; } /** * Determines whether the address is an IPv4 link local address. IPv4 link * local addresses are those in the following networks: * <p/> * 10.0.0.0 to 10.255.255.255 172.16.0.0 to 172.31.255.255 192.168.0.0 to * 192.168.255.255 * * @param add the address to inspect * @return true if add is a link local ipv4 address and false if not. */ public static boolean isLinkLocalIPv4Address(InetAddress add) { byte address[] = add.getAddress(); if ((address[0] & 0xFF) == 10) return true; if ((address[0] & 0xFF) == 172 && (address[1] & 0xFF) >= 16 && address[1] <= 31) return true; if ((address[0] & 0xFF) == 192 && (address[1] & 0xFF) == 168) return true; return false; } /** * Determines whether the address could be used in a VoIP session. * Attention, routable address as determined by this method are not only * globally routable addresses in the general sense of the term. Link local * addresses such as 192.168.x.x or fe80::xxxx are also considered usable. * * @param address the address to test. * @return true if the address could be used in a VoIP session. */ public static boolean isRoutable(InetAddress address) { if (address instanceof Inet6Address) { return !address.isLoopbackAddress(); } else { return (!address.isLoopbackAddress()) && (!isWindowsAutoConfiguredIPv4Address(address)); } } private static void setProperties() { InetAddress selectedAddress = getLocalHost(); NetworkAddressManager.cacheLocalhost(selectedAddress); SIPConfig.setPreferredNetworkAddress(selectedAddress.getHostAddress()); SIPConfig.setIPAddress(selectedAddress.getHostAddress()); SIPConfig.setPublicAddress(selectedAddress.getHostAddress()); SIPConfig.setSystemProperties(); } public static void resetIndex() { index = 0; cachedLocalhost = null; } public static boolean nextIndex() { if (index < addresses.size() - 1) { index++; cacheLocalhost(addresses.get(index).getInetAddress()); setProperties(); return true; } return false; } public static List<InetAddressWrapper> getInetAddresses() { return addresses; } public static class InetAddressWrapper implements Comparable<InetAddressWrapper> { private int value = 0; private InetAddress inetAddress = null; public InetAddressWrapper(InetAddress inetAddress, int value) { this.inetAddress = inetAddress; this.value = value; } public int getValue() { return value; } public InetAddress getInetAddress() { return inetAddress; } public int compareTo(InetAddressWrapper o) { InetAddressWrapper other = o; if (this.value < other.getValue()) return -1; else if (this.value < other.getValue()) return 0; else return 1; } } public static void main(String args[]) { NetworkAddressManager.start(); boolean next = true; while (next) { System.out.println(NetworkAddressManager.getLocalHost()); next = NetworkAddressManager.nextIndex(); } } }