package org.chris.portmapper.router.sbbi; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import net.sbbi.upnp.devices.UPNPRootDevice; import net.sbbi.upnp.impls.InternetGatewayDevice; import net.sbbi.upnp.messages.ActionResponse; import net.sbbi.upnp.messages.UPNPResponseException; //import org.apache.commons.logging.Log; //import org.apache.commons.logging.LogFactory; //import org.chris.portmapper.PortMapperApp; import org.apache.log4j.Logger; import org.chris.portmapper.model.PortMapping; import org.chris.portmapper.model.Protocol; import org.chris.portmapper.router.AbstractRouter; import org.chris.portmapper.router.RouterException; //import org.chris.portmapper.util.EncodingUtilities; /** * This class represents a router device and provides methods for managing port * mappings and getting information about the router. * * @author chris * @version $Id: SBBIRouter.java 61 2009-08-15 14:58:46Z christoph $ */ public class SBBIRouter extends AbstractRouter { // private final Log logger = LogFactory.getLog(this.getClass()); private final static Logger logger = Logger.getLogger( SBBIRouter.class ); /** * The wrapped router device. */ private InternetGatewayDevice router = null; /** * The timeout in milliseconds for finding a router device. */ private final static int DISCOVERY_TIMEOUT = 5000; /** * The maximum number of port mappings that we will try to retrieve from the * router. */ private final static int MAX_NUM_PORTMAPPINGS = 100; private SBBIRouter(InternetGatewayDevice router) { if (router == null) { throw new IllegalArgumentException("No router given"); } this.router = router; } /** * Find the router device in the network. * * @return the router device. * @throws RouterException * if no or more than one router devices where found. */ public static AbstractRouter findRouter() throws RouterException { InternetGatewayDevice device = findInternetGatewayDevice(); AbstractRouter r = new SBBIRouter(device); return r; } /** * Find all router devices in the network and check, that only one is found. * * @return the router device. * @throws RouterException * if no or more than one router devices where found. */ private static InternetGatewayDevice findInternetGatewayDevice() throws RouterException { InternetGatewayDevice[] devices; try { devices = InternetGatewayDevice.getDevices(DISCOVERY_TIMEOUT); } catch (IOException e) { throw new RouterException("Could not find devices", e); } if (devices == null || devices.length == 0) { throw new RouterException("No router devices found"); } if (devices.length != 1) { throw new RouterException("Found more than one router devices (" + devices.length + ")"); } return devices[0]; } public String getName() throws RouterException { return router.getIGDRootDevice().getModelName(); } public String getExternalIPAddress() throws RouterException { logger.debug("Get external IP address..."); String ipAddress; try { ipAddress = router.getExternalIPAddress(); } catch (UPNPResponseException e) { throw new RouterException("Could not get external IP", e); } catch (IOException e) { throw new RouterException("Could not get external IP", e); } logger.info("Got external IP address " + ipAddress + " for router."); return ipAddress; } public String getInternalHostName() { logger.debug("Get internal IP address..."); String ipAddress; ipAddress = router.getIGDRootDevice().getPresentationURL().getHost(); logger.info("Got internal host name '" + ipAddress + "' for router."); return ipAddress; } public int getInternalPort() { logger.debug("Get internal port of router..."); int port = router.getIGDRootDevice().getPresentationURL().getPort(); logger.info("Got internal port " + port + " for router."); return port; } public Collection<PortMapping> getPortMappings() throws RouterException { logger.info("Get all port mappings..."); Collection<PortMapping> mappings = new LinkedList<PortMapping>(); try { /* * This is a little trick to get all port mappings. There is a * method that gets the number of available port mappings * (getNatMappingsCount()), but it seems, that this method just * tries to get all port mappings and checks, if an error is * returned. * * In order to speed this up, we will do the same here, but stop, * when the first exception is thrown. */ boolean moreEntries = true; int currentMappingNumber = 0; while (moreEntries && currentMappingNumber < MAX_NUM_PORTMAPPINGS) { logger.debug("Getting port mapping with entry number " + currentMappingNumber + "..."); try { ActionResponse response = router .getGenericPortMappingEntry(currentMappingNumber); // Create a port mapping for the response. if (response != null) { mappings.add(PortMapping.create(response)); } else { logger.warn("Got a null port mapping for number " + currentMappingNumber + ". This may be a bug in UPNPLib."); } } catch (UPNPResponseException e) { // The error codes 713 and 714 mean, that no port mappings // where found for the current entry. See bug reports // https://sourceforge.net/tracker/index.php?func=detail&aid= // 1939749&group_id=213879&atid=1027466 // and http://www.sbbi.net/forum/viewtopic.php?p=394 if (e.getDetailErrorCode() == 713 || e.getDetailErrorCode() == 714) { moreEntries = false; logger.debug("Got no port mapping for entry number " + currentMappingNumber + ". Stop getting more entries."); } else { // Also ignore all other exceptions to workaround // possible router bugs. // https://sourceforge.net/tracker2/?func=detail&aid=2540478&group_id=213879&atid=1027466 moreEntries = false; logger.error( "Got exception when fetching port mapping for entry number " + currentMappingNumber + ". Stop getting more entries.", e); } } currentMappingNumber++; } // Check, if the max number of entries is reached and print a // warning message. if (currentMappingNumber == MAX_NUM_PORTMAPPINGS) { logger .warn("Reached max number of port mappings to get (" + MAX_NUM_PORTMAPPINGS + "). Perhaps not all port mappings where retrieved. Try to increase Router.MAX_NUM_PORTMAPPINGS."); } } catch (IOException e) { throw new RouterException("Could not get NAT mappings", e); } return mappings; } /* * (non-Javadoc) * * @see org.chris.portmapper.router.IRouter#logRouterInfo() */ public void logRouterInfo() throws RouterException { Map<String, String> info = new HashMap<String, String>(); UPNPRootDevice rootDevice = router.getIGDRootDevice(); info.put("friendlyName", rootDevice.getFriendlyName()); info.put("manufacturer", rootDevice.getManufacturer()); info.put("modelDescription", rootDevice.getModelDescription()); info.put("modelName", rootDevice.getModelName()); info.put("serialNumber", rootDevice.getSerialNumber()); info.put("vendorFirmware", rootDevice.getVendorFirmware()); info.put("modelNumber", rootDevice.getModelNumber()); info.put("modelURL", rootDevice.getModelURL()); info.put("manufacturerURL", rootDevice.getManufacturerURL() .toExternalForm()); info.put("presentationURL", rootDevice.getPresentationURL() .toExternalForm()); info.put("urlBase", rootDevice.getURLBase().toExternalForm()); SortedSet<String> sortedKeys = new TreeSet<String>(info.keySet()); for (String key : sortedKeys) { String value = info.get(key); logger.info("Router Info: " + key + " \t= " + value); } logger.info("def loc " + rootDevice.getDeviceDefLoc()); logger.info("def loc data " + rootDevice.getDeviceDefLocData()); logger.info("icons " + rootDevice.getDeviceIcons()); logger.info("device type " + rootDevice.getDeviceType()); logger.info("direct parent " + rootDevice.getDirectParent()); logger.info("disc udn " + rootDevice.getDiscoveryUDN()); logger.info("disc usn " + rootDevice.getDiscoveryUSN()); logger.info("udn " + rootDevice.getUDN()); } private boolean addPortMapping(String description, Protocol protocol, String remoteHost, int externalPort, String internalClient, int internalPort, int leaseDuration) throws RouterException { String protocolString = (protocol.equals(Protocol.TCP) ? "TCP" : "UDP"); // if (PortMapperApp.getInstance().getSettings().isUseEntityEncoding()) { // description = EncodingUtilities.htmlEntityEncode(description); // } try { boolean success = router.addPortMapping(description, null, internalPort, externalPort, internalClient, leaseDuration, protocolString); return success; } catch (IOException e) { throw new RouterException("Could not add port mapping", e); } catch (UPNPResponseException e) { throw new RouterException("Could not add port mapping", e); } } public void addPortMappings(Collection<PortMapping> mappings) throws RouterException { for (PortMapping portMapping : mappings) { logger.info("Adding port mapping " + portMapping); addPortMapping(portMapping); } } public void addPortMapping(PortMapping mapping) throws RouterException { logger.info("Adding port mapping " + mapping.getCompleteDescription()); addPortMapping(mapping.getDescription(), mapping.getProtocol(), mapping .getRemoteHost(), mapping.getExternalPort(), mapping .getInternalClient(), mapping.getInternalPort(), 0); } public void removeMapping(PortMapping mapping) throws RouterException { removePortMapping(mapping.getProtocol(), mapping.getRemoteHost(), mapping.getExternalPort()); } public void removePortMapping(Protocol protocol, String remoteHost, int externalPort) throws RouterException { String protocolString = (protocol.equals(Protocol.TCP) ? "TCP" : "UDP"); try { router.deletePortMapping(remoteHost, externalPort, protocolString); } catch (IOException e) { throw new RouterException("Could not remove port mapping", e); } catch (UPNPResponseException e) { throw new RouterException("Could not remove port mapping", e); } } public void disconnect() { // Nothing to do right now. } public long getValidityTime() { return router.getIGDRootDevice().getValidityTime(); } public long getUpTime() throws RouterException { // TODO Auto-generated method stub return 0; } }