You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
510 lines
24 KiB
510 lines
24 KiB
/*
|
|
* ============================================================================
|
|
* The Apache Software License, Version 1.1
|
|
* ============================================================================
|
|
*
|
|
* Copyright (C) 2002 The Apache Software Foundation. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modifica-
|
|
* tion, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3. The end-user documentation included with the redistribution, if any, must
|
|
* include the following acknowledgment: "This product includes software
|
|
* developed by SuperBonBon Industries (http://www.sbbi.net/)."
|
|
* Alternately, this acknowledgment may appear in the software itself, if
|
|
* and wherever such third-party acknowledgments normally appear.
|
|
*
|
|
* 4. The names "UPNPLib" and "SuperBonBon Industries" must not be
|
|
* used to endorse or promote products derived from this software without
|
|
* prior written permission. For written permission, please contact
|
|
* info@sbbi.net.
|
|
*
|
|
* 5. Products derived from this software may not be called
|
|
* "SuperBonBon Industries", nor may "SBBI" appear in their name,
|
|
* without prior written permission of SuperBonBon Industries.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT,INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
|
|
* DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* This software consists of voluntary contributions made by many individuals
|
|
* on behalf of SuperBonBon Industries. For more information on
|
|
* SuperBonBon Industries, please see <http://www.sbbi.net/>.
|
|
*/
|
|
package net.sbbi.upnp.impls;
|
|
|
|
import java.io.IOException;
|
|
import java.net.InetAddress;
|
|
import java.net.NetworkInterface;
|
|
import java.net.UnknownHostException;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.Set;
|
|
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
import net.sbbi.upnp.Discovery;
|
|
import net.sbbi.upnp.devices.UPNPDevice;
|
|
import net.sbbi.upnp.devices.UPNPRootDevice;
|
|
import net.sbbi.upnp.messages.ActionMessage;
|
|
import net.sbbi.upnp.messages.ActionResponse;
|
|
import net.sbbi.upnp.messages.StateVariableMessage;
|
|
import net.sbbi.upnp.messages.StateVariableResponse;
|
|
import net.sbbi.upnp.messages.UPNPMessageFactory;
|
|
import net.sbbi.upnp.messages.UPNPResponseException;
|
|
import net.sbbi.upnp.services.UPNPService;
|
|
|
|
/**
|
|
* This class can be used to access some funtionalities on the
|
|
* InternetGatewayDevice on your network without having to know
|
|
* anything about the required input/output parameters.
|
|
* All device functions are not provided.
|
|
* @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
|
|
* @version 1.0
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public class InternetGatewayDevice {
|
|
|
|
private final static Log log = LogFactory.getLog( InternetGatewayDevice.class );
|
|
|
|
private UPNPRootDevice igd;
|
|
private UPNPMessageFactory msgFactory;
|
|
|
|
public InternetGatewayDevice( UPNPRootDevice igd ) throws UnsupportedOperationException {
|
|
this( igd, true, true );
|
|
}
|
|
|
|
private InternetGatewayDevice( UPNPRootDevice igd, boolean WANIPConnection, boolean WANPPPConnection ) throws UnsupportedOperationException {
|
|
this.igd = igd;
|
|
UPNPDevice myIGDWANConnDevice = igd.getChildDevice( "urn:schemas-upnp-org:device:WANConnectionDevice:1" );
|
|
if ( myIGDWANConnDevice == null ) {
|
|
throw new UnsupportedOperationException( "device urn:schemas-upnp-org:device:WANConnectionDevice:1 not supported by IGD device " + igd.getModelName() );
|
|
}
|
|
|
|
UPNPService wanIPSrv = myIGDWANConnDevice.getService( "urn:schemas-upnp-org:service:WANIPConnection:1" );
|
|
UPNPService wanPPPSrv = myIGDWANConnDevice.getService( "urn:schemas-upnp-org:service:WANPPPConnection:1" );
|
|
|
|
if ( ( WANIPConnection && WANPPPConnection ) && ( wanIPSrv == null && wanPPPSrv == null ) ) {
|
|
throw new UnsupportedOperationException( "Unable to find any urn:schemas-upnp-org:service:WANIPConnection:1 or urn:schemas-upnp-org:service:WANPPPConnection:1 service" );
|
|
} else if ( ( WANIPConnection && !WANPPPConnection ) && wanIPSrv == null ) {
|
|
throw new UnsupportedOperationException( "Unable to find any urn:schemas-upnp-org:service:WANIPConnection:1 service" );
|
|
} else if ( ( !WANIPConnection && WANPPPConnection ) && wanPPPSrv == null ) {
|
|
throw new UnsupportedOperationException( "Unable to find any urn:schemas-upnp-org:service:WANPPPConnection:1 service" );
|
|
}
|
|
|
|
if ( wanIPSrv != null && wanPPPSrv == null ) {
|
|
msgFactory = UPNPMessageFactory.getNewInstance( wanIPSrv );
|
|
} else if ( wanPPPSrv != null && wanIPSrv == null ) {
|
|
msgFactory = UPNPMessageFactory.getNewInstance( wanPPPSrv );
|
|
} else {
|
|
// Unable to test the following code since no router implementing both IP and PPP connection on hands..
|
|
/*// discover the active WAN interface using the WANCommonInterfaceConfig specs
|
|
UPNPDevice wanDevice = igd.getChildDevice( "urn:schemas-upnp-org:device:WANDevice:1" );
|
|
UPNPService configService = wanDevice.getService( "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" );
|
|
if ( configService != null ) {
|
|
// retreive the first active connection
|
|
ServiceAction act = configService.getUPNPServiceAction( "GetActiveConnection" );
|
|
if ( act != null ) {
|
|
UPNPMessageFactory msg = UPNPMessageFactory.getNewInstance( configService );
|
|
String deviceContainer = null;
|
|
String serviceID = null;
|
|
try {
|
|
// always lookup for the first index of active connections.
|
|
ActionResponse resp = msg.getMessage( "GetActiveConnection" ).setInputParameter( "NewActiveConnectionIndex", 0 ).service();
|
|
deviceContainer = resp.getOutActionArgumentValue( "NewActiveConnDeviceContainer" );
|
|
serviceID = resp.getOutActionArgumentValue( "NewActiveConnectionServiceID" );
|
|
} catch ( IOException ex ) {
|
|
// no response returned
|
|
} catch ( UPNPResponseException respEx ) {
|
|
// should never happen unless the damn thing is bugged
|
|
}
|
|
if ( deviceContainer != null && deviceContainer.trim().length() > 0 &&
|
|
serviceID != null && serviceID.trim().length() > 0 ) {
|
|
for ( Iterator i = igd.getChildDevices().iterator(); i.hasNext(); ) {
|
|
UPNPDevice dv = (UPNPDevice)i.next();
|
|
|
|
if ( deviceContainer.startsWith( dv.getUDN() ) &&
|
|
dv.getDeviceType().indexOf( ":WANConnectionDevice:" ) != -1 ) {
|
|
myIGDWANConnDevice = dv;
|
|
break;
|
|
}
|
|
}
|
|
msgFactory = UPNPMessageFactory.getNewInstance( myIGDWANConnDevice.getServiceByID( serviceID ) );
|
|
}
|
|
}
|
|
}*/
|
|
// Doing a tricky test with external IP address, the unactive interface should return a null value or none
|
|
if ( testWANInterface( wanIPSrv ) ) {
|
|
msgFactory = UPNPMessageFactory.getNewInstance( wanIPSrv );
|
|
} else if( testWANInterface( wanPPPSrv ) ) {
|
|
msgFactory = UPNPMessageFactory.getNewInstance( wanPPPSrv );
|
|
}
|
|
if ( msgFactory == null ) {
|
|
// Nothing found using WANCommonInterfaceConfig! IP by default
|
|
log.warn( "Unable to detect active WANIPConnection, dfaulting to urn:schemas-upnp-org:service:WANIPConnection:1" );
|
|
msgFactory = UPNPMessageFactory.getNewInstance( wanIPSrv );
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean testWANInterface( UPNPService srv ) {
|
|
UPNPMessageFactory tmp = UPNPMessageFactory.getNewInstance( srv );
|
|
|
|
ActionMessage msg = tmp.getMessage( "GetExternalIPAddress" );
|
|
String ipToParse = null;
|
|
try {
|
|
ipToParse = msg.service().getOutActionArgumentValue( "NewExternalIPAddress" );
|
|
} catch ( UPNPResponseException ex ) {
|
|
// ok probably not the IP interface
|
|
} catch ( IOException ex ) {
|
|
// not really normal
|
|
log.warn( "IOException occured during device detection", ex );
|
|
}
|
|
if ( ipToParse != null && ipToParse.length() > 0 && !ipToParse.equals( "0.0.0.0" ) ) {
|
|
try {
|
|
return InetAddress.getByName( ipToParse ) != null;
|
|
} catch ( UnknownHostException ex ) {
|
|
// ok a crappy IP provided, definitly the wrong interface..
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Retreives the IDG UNPNRootDevice object
|
|
* @return the UNPNRootDevie object bound to this object
|
|
*/
|
|
public UPNPRootDevice getIGDRootDevice() {
|
|
return igd;
|
|
}
|
|
|
|
/**
|
|
* Lookup all the IGD (IP or PPP) devices on the network. If a device implements both
|
|
* IP and PPP, the active service will be used for nat mappings.
|
|
* @param timeout the timeout in ms to listen for devices response, -1 for default value
|
|
* @return an array of devices to play with or null if nothing found.
|
|
* @throws IOException if some IO Exception occurs during discovery
|
|
*/
|
|
public static InternetGatewayDevice[] getDevices( int timeout ) throws IOException {
|
|
return lookupDeviceDevices( timeout, Discovery.DEFAULT_TTL, Discovery.DEFAULT_MX, true, true, null );
|
|
}
|
|
|
|
/**
|
|
* Lookup all the IGD (IP urn:schemas-upnp-org:service:WANIPConnection:1, or PPP urn:schemas-upnp-org:service:WANPPPConnection:1)
|
|
* devices for a given network interface. If a device implements both
|
|
* IP and PPP, the active service will be used for nat mappings.
|
|
* @param timeout the timeout in ms to listen for devices response, -1 for default value
|
|
* @param ttl the discovery ttl such as {@link net.sbbi.upnp.Discovery#DEFAULT_TTL}
|
|
* @param mx the discovery mx such as {@link net.sbbi.upnp.Discovery#DEFAULT_MX}
|
|
* @param ni the network interface where to lookup IGD devices
|
|
* @return an array of devices to play with or null if nothing found.
|
|
* @throws IOException if some IO Exception occurs during discovery
|
|
*/
|
|
public static InternetGatewayDevice[] getDevices( int timeout, int ttl, int mx, NetworkInterface ni ) throws IOException {
|
|
return lookupDeviceDevices( timeout, ttl, mx, true, true, ni );
|
|
}
|
|
|
|
/**
|
|
* Lookup all the IGD IP devices on the network (urn:schemas-upnp-org:service:WANIPConnection:1 service)
|
|
* @param timeout the timeout in ms to listen for devices response, -1 for default value
|
|
* @return an array of devices to play with or null if nothing found or if found devices
|
|
* do not have the urn:schemas-upnp-org:service:WANIPConnection:1 service
|
|
* @deprecated use generic {@link #getDevices(int)} or {@link #getDevices(int, int, int, NetworkInterface)} methods since this one is not
|
|
* usable with all IGD devices ( will only work with devices implementing the urn:schemas-upnp-org:service:WANIPConnection:1 service )
|
|
*/
|
|
@Deprecated
|
|
public static InternetGatewayDevice[] getIPDevices( int timeout ) throws IOException {
|
|
return lookupDeviceDevices( timeout, Discovery.DEFAULT_TTL, Discovery.DEFAULT_MX, true, false, null );
|
|
}
|
|
|
|
/**
|
|
* Lookup all the IGD PPP devices on the network (urn:schemas-upnp-org:service:WANPPPConnection:1 service)
|
|
* @param timeout the timeout in ms to listen for devices response, -1 for default value
|
|
* @return an array of devices to play with or null if nothing found or if found devices
|
|
* do not have the urn:schemas-upnp-org:service:WANPPPConnection:1 service
|
|
* @deprecated use generic {@link #getDevices(int)} or {@link #getDevices(int, int, int, NetworkInterface)} methods since this one is not
|
|
* usable with all IGD devices ( will only work with devices implementing the urn:schemas-upnp-org:service:WANPPPConnection:1 service )
|
|
*/
|
|
@Deprecated
|
|
public static InternetGatewayDevice[] getPPPDevices( int timeout ) throws IOException {
|
|
return lookupDeviceDevices( timeout, Discovery.DEFAULT_TTL, Discovery.DEFAULT_MX, false, true, null );
|
|
}
|
|
|
|
private static InternetGatewayDevice[] lookupDeviceDevices( int timeout, int ttl, int mx, boolean WANIPConnection, boolean WANPPPConnection, NetworkInterface ni ) throws IOException {
|
|
UPNPRootDevice[] devices = null;
|
|
InternetGatewayDevice[] rtrVal = null;
|
|
if ( timeout == -1 ) {
|
|
devices = Discovery.discover( Discovery.DEFAULT_TIMEOUT, ttl, mx, "urn:schemas-upnp-org:device:InternetGatewayDevice:1", ni );
|
|
} else {
|
|
devices = Discovery.discover( timeout, ttl, mx, "urn:schemas-upnp-org:device:InternetGatewayDevice:1", ni );
|
|
}
|
|
|
|
if ( devices != null ) {
|
|
Set valid = new HashSet();
|
|
for ( int i = 0; i < devices.length; i++ ) {
|
|
try {
|
|
valid.add( new InternetGatewayDevice( devices[i], WANIPConnection, WANPPPConnection ) );
|
|
} catch ( UnsupportedOperationException ex ) {
|
|
// the device is either not IP or PPP
|
|
if ( log.isDebugEnabled() ) log.debug( "UnsupportedOperationException during discovery " + ex.getMessage() );
|
|
}
|
|
}
|
|
if ( valid.size() == 0 ) {
|
|
return null;
|
|
}
|
|
rtrVal = new InternetGatewayDevice[valid.size()];
|
|
int i = 0;
|
|
for ( Iterator itr = valid.iterator(); itr.hasNext(); ) {
|
|
rtrVal[i++] = (InternetGatewayDevice)itr.next();
|
|
}
|
|
|
|
}
|
|
return rtrVal;
|
|
}
|
|
|
|
/**
|
|
* Retreives the external IP address
|
|
* @return a String representing the external IP
|
|
* @throws UPNPResponseException if the devices returns an error code
|
|
* @throws IOException if some error occurs during communication with the device
|
|
*/
|
|
public String getExternalIPAddress() throws UPNPResponseException, IOException {
|
|
ActionMessage msg = msgFactory.getMessage( "GetExternalIPAddress" );
|
|
return msg.service().getOutActionArgumentValue( "NewExternalIPAddress" );
|
|
}
|
|
|
|
/**
|
|
* Retreives a generic port mapping entry.
|
|
* @param newPortMappingIndex the index to lookup in the nat table of the upnp device
|
|
* @return an action response Object containing the following fields :
|
|
* NewRemoteHost, NewExternalPort, NewProtocol, NewInternalPort,
|
|
* NewInternalClient, NewEnabled, NewPortMappingDescription, NewLeaseDuration or null if the index does not exists
|
|
* @throws IOException if some error occurs during communication with the device
|
|
* @throws UPNPResponseException if some unexpected error occurs on the UPNP device
|
|
*/
|
|
public ActionResponse getGenericPortMappingEntry( int newPortMappingIndex ) throws IOException, UPNPResponseException {
|
|
|
|
ActionMessage msg = msgFactory.getMessage( "GetGenericPortMappingEntry" );
|
|
msg.setInputParameter( "NewPortMappingIndex", newPortMappingIndex );
|
|
|
|
try {
|
|
return msg.service();
|
|
} catch ( UPNPResponseException ex ) {
|
|
if ( ex.getDetailErrorCode() == 714 ) {
|
|
return null;
|
|
}
|
|
throw ex;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Retreives information about a specific port mapping
|
|
* @param remoteHost the remote host ip to check, null if wildcard
|
|
* @param externalPort the port to check
|
|
* @param protocol the protocol for the mapping, either TCP or UDP
|
|
* @return an action response Object containing the following fields :
|
|
* NewInternalPort, NewInternalClient, NewEnabled, NewPortMappingDescription, NewLeaseDuration or
|
|
* null if no such entry exists in the device NAT table
|
|
* @throws IOException if some error occurs during communication with the device
|
|
* @throws UPNPResponseException if some unexpected error occurs on the UPNP device
|
|
*/
|
|
public ActionResponse getSpecificPortMappingEntry( String remoteHost, int externalPort, String protocol ) throws IOException, UPNPResponseException {
|
|
remoteHost = remoteHost == null ? "" : remoteHost;
|
|
checkPortMappingProtocol( protocol );
|
|
checkPortRange( externalPort );
|
|
|
|
ActionMessage msg = msgFactory.getMessage( "GetSpecificPortMappingEntry" );
|
|
msg.setInputParameter( "NewRemoteHost", remoteHost )
|
|
.setInputParameter( "NewExternalPort", externalPort )
|
|
.setInputParameter( "NewProtocol", protocol );
|
|
|
|
try {
|
|
return msg.service();
|
|
} catch ( UPNPResponseException ex ) {
|
|
if ( ex.getDetailErrorCode() == 714 ) {
|
|
return null;
|
|
}
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Configures a nat entry on the UPNP device.
|
|
* @param description the mapping description, null for no description
|
|
* @param remoteHost the remote host ip for this entry, null for a wildcard value
|
|
* @param internalPort the internal client port where data should be redirected
|
|
* @param externalPort the external port to open on the UPNP device an map on the internal client, 0 for a wildcard value
|
|
* @param internalClient the internal client ip where data should be redirected
|
|
* @param leaseDuration the lease duration in seconds 0 for an infinite time
|
|
* @param protocol the protocol, either TCP or UDP
|
|
* @return true if the port is mapped false if the mapping is allready done for another internal client
|
|
* @throws IOException if some error occurs during communication with the device
|
|
* @throws UPNPResponseException if the device does not accept some settings :<br/>
|
|
* 402 Invalid Args See UPnP Device Architecture section on Control<br/>
|
|
* 501 Action Failed See UPnP Device Architecture section on Control<br/>
|
|
* 715 WildCardNotPermittedInSrcIP The source IP address cannot be wild-carded<br/>
|
|
* 716 WildCardNotPermittedInExtPort The external port cannot be wild-carded <br/>
|
|
* 724 SamePortValuesRequired Internal and External port values must be the same<br/>
|
|
* 725 OnlyPermanentLeasesSupported The NAT implementation only supports permanent lease times on port mappings<br/>
|
|
* 726 RemoteHostOnlySupportsWildcard RemoteHost must be a wildcard and cannot be a specific IP address or DNS name<br/>
|
|
* 727 ExternalPortOnlySupportsWildcard ExternalPort must be a wildcard and cannot be a specific port value
|
|
*/
|
|
public boolean addPortMapping( String description, String remoteHost,
|
|
int internalPort, int externalPort,
|
|
String internalClient, int leaseDuration,
|
|
String protocol ) throws IOException, UPNPResponseException {
|
|
remoteHost = remoteHost == null ? "" : remoteHost;
|
|
checkPortMappingProtocol( protocol );
|
|
if ( externalPort != 0 ) {
|
|
checkPortRange( externalPort );
|
|
}
|
|
checkPortRange( internalPort );
|
|
description = description == null ? "" : description;
|
|
if ( leaseDuration < 0 ) throw new IllegalArgumentException( "Invalid leaseDuration (" + leaseDuration + ") value" );
|
|
|
|
ActionMessage msg = msgFactory.getMessage( "AddPortMapping" );
|
|
msg.setInputParameter( "NewRemoteHost", remoteHost )
|
|
.setInputParameter( "NewExternalPort", externalPort )
|
|
.setInputParameter( "NewProtocol", protocol )
|
|
.setInputParameter( "NewInternalPort", internalPort )
|
|
.setInputParameter( "NewInternalClient", internalClient )
|
|
.setInputParameter( "NewEnabled", true )
|
|
.setInputParameter( "NewPortMappingDescription", description )
|
|
.setInputParameter( "NewLeaseDuration", leaseDuration );
|
|
try {
|
|
msg.service();
|
|
return true;
|
|
} catch ( UPNPResponseException ex ) {
|
|
if ( ex.getDetailErrorCode() == 718 ) {
|
|
return false;
|
|
}
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes a port mapping on the IDG device
|
|
* @param remoteHost the host ip for which the mapping was done, null value for a wildcard value
|
|
* @param externalPort the port to close
|
|
* @param protocol the protocol for the mapping, TCP or UDP
|
|
* @return true if the port has been unmapped correctly otherwise false ( entry does not exists ).
|
|
* @throws IOException if some error occurs during communication with the device
|
|
* @throws UPNPResponseException if the devices returns an error message
|
|
*/
|
|
public boolean deletePortMapping( String remoteHost, int externalPort, String protocol ) throws IOException, UPNPResponseException {
|
|
|
|
remoteHost = remoteHost == null ? "" : remoteHost;
|
|
checkPortMappingProtocol( protocol );
|
|
checkPortRange( externalPort );
|
|
ActionMessage msg = msgFactory.getMessage( "DeletePortMapping" );
|
|
msg.setInputParameter( "NewRemoteHost", remoteHost )
|
|
.setInputParameter( "NewExternalPort", externalPort )
|
|
.setInputParameter( "NewProtocol", protocol );
|
|
try {
|
|
msg.service();
|
|
return true;
|
|
} catch ( UPNPResponseException ex ) {
|
|
if ( ex.getDetailErrorCode() == 714 ) {
|
|
return false;
|
|
}
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retreives the current number of mapping in the NAT table
|
|
* @return the nat table current number of mappings or null if the device does not allow to query state variables
|
|
* @throws IOException if some error occurs during communication with the device
|
|
* @throws UPNPResponseException if the devices returns an error message with error code other than 404
|
|
*/
|
|
public Integer getNatMappingsCount() throws IOException, UPNPResponseException {
|
|
|
|
Integer rtrval = null;
|
|
StateVariableMessage natTableSize = msgFactory.getStateVariableMessage( "PortMappingNumberOfEntries" );
|
|
try {
|
|
StateVariableResponse resp = natTableSize.service();
|
|
rtrval = Integer.valueOf( resp.getStateVariableValue() );
|
|
} catch ( UPNPResponseException ex ) {
|
|
// 404 can happen if device do not implement state variables queries
|
|
if ( ex.getDetailErrorCode() != 404 ) {
|
|
throw ex;
|
|
}
|
|
}
|
|
return rtrval;
|
|
}
|
|
|
|
/**
|
|
* Computes the total entries in avaliable in the nat table size, not that this method is not guaranteed to work
|
|
* with all upnp devices since it is not an generic IGD command
|
|
* @return the number of entries or null if the NAT table size cannot be computed for the device
|
|
* @throws IOException if some error occurs during communication with the device
|
|
* @throws UPNPResponseException if the devices returns an error message with error code other than 713 or 402
|
|
*/
|
|
public Integer getNatTableSize() throws IOException, UPNPResponseException {
|
|
|
|
// first let's look at the first index.. some crappy devices do not start with index 0
|
|
// we stop at index 50
|
|
int startIndex = -1;
|
|
for ( int i = 0; i < 50; i++ ) {
|
|
try {
|
|
this.getGenericPortMappingEntry( i );
|
|
startIndex = i;
|
|
break;
|
|
} catch ( UPNPResponseException ex ) {
|
|
// some devices return the 402 code
|
|
if ( ex.getDetailErrorCode() != 713 && ex.getDetailErrorCode() != 402 ) {
|
|
throw ex;
|
|
}
|
|
}
|
|
}
|
|
if ( startIndex == -1 ) {
|
|
// humm nothing found within the first 200 indexes..
|
|
// returning null
|
|
return null;
|
|
}
|
|
int size = 0;
|
|
while ( true ) {
|
|
|
|
try {
|
|
this.getGenericPortMappingEntry( startIndex++ );
|
|
size++;
|
|
} catch ( UPNPResponseException ex ) {
|
|
if ( ex.getDetailErrorCode() == 713 || ex.getDetailErrorCode() == 402 ) {
|
|
/// ok index unknown
|
|
break;
|
|
}
|
|
throw ex;
|
|
}
|
|
}
|
|
return Integer.valueOf( size );
|
|
}
|
|
|
|
private void checkPortMappingProtocol( String prot ) throws IllegalArgumentException {
|
|
if ( prot == null || ( !prot.equals( "TCP" ) && !prot.equals( "UDP" ) ) )
|
|
throw new IllegalArgumentException( "PortMappingProtocol must be either TCP or UDP" );
|
|
}
|
|
|
|
private void checkPortRange( int port ) throws IllegalArgumentException {
|
|
if ( port < 1 || port > 65535 )
|
|
throw new IllegalArgumentException( "Port range must be between 1 and 65535" );
|
|
}
|
|
|
|
|
|
}
|