java.lang.Object
org.cybergarage.upnp.ControlPoint
net.i2p.router.transport.UPnP
- All Implemented Interfaces:
HTTPRequestListener, DeviceChangeListener, EventListener
Discovers UPnP internet gateways and forwards local ports through them.
Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) static classIPv6-specific port forwarding configuration.private classThis also renews all subscriptions.private static classCompare based on friendly name of the device.private classRemoves our port mappings on a thread.Field Summary
Modifier and TypeFieldDescriptionprivate static final AtomicIntegerCounter naming the UPnP jobs.private final I2PAppContextThe app context.Last event variable values, by name.private StringA blockingObject.toString().private ArgumentListArguments of the last failed action, for the log.private ServiceService of the last failed action, for the log.private final LogLogger for UPnP port forwarding.Other devices found, by unique device name.private booleanTrue if only permanent leases work on this device.private DeviceThe WAN device found on the network.private ServiceThe IPv4 port mapping service in use.private ServiceThe IPv6 firewall service, if found.private booleanTrue if the service lacks automatic port mapping.private static final booleanTrue to talk to UPnP on the same host as us, probably for testing.private static final StringBundle holding the translated console strings.private ForwardPortCallbackCallback to call when a forward fails or succeeds.static final intPort mapping lease requested from the gateway, in seconds.private final ObjectLock guarding the port mapping state.private final Set<ForwardPort> List of ports we have actually forwarded.private final Set<ForwardPort> List of ports we want to forward.private static final StringConfig key for the console's advanced mode.private static final StringConfig key turning UPnP off entirely.private static final StringSome schemas.private static final StringIGD 2 flavors, since 0.9.34.private booleanGuess that an upstream NAT defeats our mapping.private final ObjectLock for building the debug strings.private static final longLargest unsigned 32 bit value, for lease durations.private static final StringUPnP version 1 WAN device type.private static final StringUPnP version 2 WAN device type.private static final StringUPnP version 1 IP connection service type.private static final StringUPnP version 2 IP connection service type.private static final StringUPnP IPv6 firewall control service type.private static final StringUPnP version 1 PPP connection service type.private static final StringUPnP version 1 WAN connection device type.private static final StringUPnP version 2 WAN connection device type.Constructor Summary
ConstructorsConstructorDescriptionUPnP(I2PAppContext context, int ssdpPort, int httpPort, InetAddress[] binds) Starts SSDP discovery and the control HTTP service on the given ports and addresses.Method Summary
Modifier and TypeMethodDescriptionprivate final StringLooks up a localised string in the console bundle.private final StringLooks up a localised string and substitutes one argument.private final StringLooks up a localised string and substitutes two arguments.private booleanaddMapping(String protocol, int port, String description, ForwardPort fp) This always requests that the external port == the internal port, for now.private booleanaddMappingV4(Service service, String protocol, int port, String description, ForwardPort fp) This always requests that the external port == the internal port, for now.private booleanaddMappingV6(Service service, int port, UPnP.IPv6ForwardPort fp) This always requests that the external port == the internal port, for now.voiddeviceAdded(Device dev) Examines a newly discovered device and maps ports when it is a gateway.voiddeviceRemoved(Device dev) Drops a departed device and un-maps every port it had held.discoverService(Device router) Traverses the structure of the router device looking for the port mapping service.voideventNotifyReceived(String uuid, long seq, String varName, String value) EventListener callback - unused for now - supported in miniupnpd as of 1.1.As we only support a single active IGD, and we don't currently have any way to get any IPv6 addresses, this will return at most one IPv4 address.private intThe reported downstream bit rate in bits per second.private static StringThe local IP of the device.The addresses we want to bind to.private StringgetNATAddress(Service service) Queries the external IPv4 address, blocking on the control action.private int65536 isn't a lot, so check for dups.private StringgetOurAddress(String deflt) Bug fix: If the SSDP notify or search response sockets listen on more than one interface, cybergarage can get our IP address wrong, and then we send the wrong one to the UPnP device, which will reject it if it enforces strict addressing.private intThe reported upstream bit rate in bits per second.private booleanWhether we are behind an UPnP-enabled NAT/router.private static voidlistActions(Service service, StringBuilder sb) Append all actions of the service as an HTML list to the buffer; for debug output only.private static voidlistActionsArguments(Action action, StringBuilder sb) Append the action's arguments as an HTML list to the buffer; for debug output only.private static voidlistStateTable(Service serv, StringBuilder sb) Append the service state table HTML to the buffer; for debug output only.private voidlistSubDev(String prefix, Device dev, StringBuilder sb) Lists a device and its children in the debug output.private voidlistSubServices(Device dev, StringBuilder sb) Lists a device's services in the debug output.static voidDumps out device info in semi-HTML format.voidonChangePublicPorts(Set<ForwardPort> ports, ForwardPortCallback cb) Registers a callback when the given ports change.private static StringprotoToString(int p) Names a firewall protocol number.private voidAdds the port mappings the router needs.private voidregisterPorts(Set<ForwardPort> portsToForwardNow) Runs port registration in a background thread; postControlAction() can take many seconds, especially if it's failing, andonChangePublicPorts(Set, ForwardPortCallback)may be called from threads we don't want to slow down.private booleanremoveMapping(String protocol, int port, ForwardPort fp, boolean noLog) Removes a mapping through the matching NAT service, blocking on the action.private booleanremoveMappingV4(Service service, String protocol, int port, ForwardPort fp, boolean noLog) Removes an IPv4 port mapping.private booleanremoveMappingV6(Service service, String protocol, int port, UPnP.IPv6ForwardPort fp, boolean noLog) Removes an IPv6 port mapping.Warning: slow.private voidGo through the other devices, try re-adding them.booleanStart the UPnP client: register listeners and clear the port mappings.voidsearch()We overridesearch()to update the SSDPSearchResponseSocketList, SSDPNotifySocketList, and HTTPServerList every time.private DeviceSIDtoDevice(String sid) The device holding a service with the given subscription ID.private static booleanstringEquals(String a, String b) Compare two strings, either of which could be null.voidWARNING - Blocking up to 2 seconds.private StringFetch a UPnP service status variable as a boolean.private StringFetch a UPnP service status variable as a number.private StringFormats a failed action for the log.private StringFetch a UPnP service status variable as a duration.private booleantryAddMapping(String protocol, int port, String description, ForwardPort fp) Tries one port mapping on the service.voidRemove all active port mappings.private voidunregisterPorts(Set<ForwardPort> portsToForwardNow) Runs port unregistration in a background thread; postControlAction() can take many seconds, especially if it's failing, and onChangePublicPorts() may be called from threads we don't want to slow down, so throw this in a thread.private voidUpdate the SSDPSearchResponseSocketList, SSDPNotifySocketList, and HTTPServerList every time.Methods inherited from class ControlPoint
addDeviceChangeListener, addEventListener, addNotifyListener, addSearchResponseListener, finalize, getDevice, getDeviceDisposer, getDeviceList, getEventSubURI, getExpiredDeviceMonitoringInterval, getHTTPPort, getHTTPServerList, getRenewSubscriber, getSearchMx, getSSDPNotifySocketList, getSSDPPort, getSSDPSearchResponseSocketList, getSubscriberService, getUserData, hasDevice, httpRequestRecieved, isNMPRMode, isSubscribed, lock, notifyReceived, performAddDeviceListener, performEventListener, performNotifyListener, performRemoveDeviceListener, performSearchResponseListener, print, removeDevice, removeDevice, removeDeviceChangeListener, removeEventListener, removeExpiredDevices, removeNotifyListener, removeSearchResponseListener, renewSubscriberService, renewSubscriberService, renewSubscriberService, search, search, searchResponseReceived, setDeviceDisposer, setEventSubURI, setExpiredDeviceMonitoringInterval, setHTTPPort, setNMPRMode, setRenewSubscriber, setSearchMx, setSSDPPort, setUserData, start, start, start, stop, subscribe, subscribe, subscribe, subscribe, unlock, unsubscribe, unsubscribe, unsubscribeModifier and TypeMethodDescriptionvoidaddDeviceChangeListener(DeviceChangeListener listener) Add a device change listener.voidaddEventListener(EventListener listener) Add an event listener.voidaddNotifyListener(NotifyListener listener) Add a notify listener.voidAdd a search response listener.voidfinalize()Get a device by its name or UDN.Get the device disposer.Get the list of discovered devices.Get the event subscription URI.longGet the expired device monitoring interval.intGet the HTTP port.protected HTTPServerListI2P was privateGet the renew subscriber.intGet the search MX value.protected SSDPNotifySocketListGet the SSDP notify socket list.intGet the SSDP port.protected SSDPSearchResponseSocketListGet the SSDP search response socket list.getSubscriberService(String uuid) Find the service holding a subscription.Get the user-defined data.booleanCheck if a device with the given name or UDN exists.voidhttpRequestRecieved(HTTPRequest httpReq) Called when an HTTP request is received.booleanCheck if NMPR mode is enabled.booleanisSubscribed(Service service) Check whether this control point holds a subscription to a service.voidlock()Lock the mutexvoidnotifyReceived(SSDPPacket packet) Handle a received SSDP notification packet.voidNotify all registered device change listeners of an added device.voidperformEventListener(String uuid, long seq, String name, String value) Notify all registered event listeners of a state variable change.voidperformNotifyListener(SSDPPacket ssdpPacket) Notify all registered notify listeners of a received SSDP packet.voidNotify all registered device change listeners of a removed device.voidperformSearchResponseListener(SSDPPacket ssdpPacket) Notify all registered search response listeners of a received SSDP packet.voidprint()/////////////////////////////////////////////protected voidremoveDevice(String name) Remove a device by its name or UDN.protected voidremoveDevice(Device dev) Remove a device from the device list.voidRemove a device change listener.voidremoveEventListener(EventListener listener) Remove an event listener.voidRemove all expired devicesvoidremoveNotifyListener(NotifyListener listener) Remove a notify listener.voidRemove a search response listener.voidRenew all subscriber services with infinite timeoutvoidrenewSubscriberService(long timeout) Renew the subscriptions of every discovered device.voidrenewSubscriberService(Device dev, long timeout) Renew the subscriptions of a device and of its embedded devices.voidBroadcast an M-SEARCH for devices matching a target.voidBroadcast an M-SEARCH for devices matching a target.voidsearchResponseReceived(SSDPPacket packet) Handle a received SSDP search response packet.voidsetDeviceDisposer(Disposer disposer) Set the device disposer.voidsetEventSubURI(String url) Set the event subscription URI.voidsetExpiredDeviceMonitoringInterval(long interval) Set the expired device monitoring interval.voidsetHTTPPort(int port) Set the HTTP port.voidsetNMPRMode(boolean flag) Set the NMPR mode flag.voidSet the renew subscriber.voidsetSearchMx(int mx) Set the search MX value.voidsetSSDPPort(int port) Set the SSDP port.voidsetUserData(Object data) Set the user-defined data.booleanstart()Start with default search targetbooleanStart listening and search for devices of the given target.booleanStart the listening sockets and search for devices.booleanstop()Stop listening, release the ports and cancel all subscriptions.booleanSubscribe to a service with an infinite lifetime.booleanSubscribe to a service, renewing an existing subscription if there is one.booleanRenew an existing subscription to a service with an infinite lifetime.booleanRenew an existing subscription to a service.voidunlock()Unlock the mutexvoidUnsubscribe from all servicesvoidunsubscribe(Device device) Cancel the subscriptions of a device and of its embedded devices.booleanunsubscribe(Service service) Cancel a subscription to a service.
Field Details
_log
Logger for UPnP port forwarding._context
The app context.ROUTER_DEVICE
WAN_DEVICE
WANCON_DEVICE
UPnP version 1 WAN connection device type.- See Also:
WAN_IP_CONNECTION
UPnP version 1 IP connection service type.- See Also:
WAN_PPP_CONNECTION
UPnP version 1 PPP connection service type.- See Also:
ROUTER_DEVICE_2
IGD 2 flavors, since 0.9.34.- See Also:
WAN_DEVICE_2
UPnP version 2 WAN device type.- See Also:
WANCON_DEVICE_2
UPnP version 2 WAN connection device type.- See Also:
WAN_IP_CONNECTION_2
UPnP version 2 IP connection service type.- See Also:
WAN_IPV6_CONNECTION
UPnP IPv6 firewall control service type.- See Also:
LEASE_TIME_SECONDS
public static final int LEASE_TIME_SECONDSPort mapping lease requested from the gateway, in seconds.- See Also:
_router
The WAN device found on the network._service
The IPv4 port mapping service in use._service6
The IPv6 firewall service, if found._otherUDNs
_eventVars
_serviceLacksAPM
private volatile boolean _serviceLacksAPMTrue if the service lacks automatic port mapping._permanentLeasesOnly
private volatile boolean _permanentLeasesOnlyTrue if only permanent leases work on this device.lock
Lock guarding the port mapping state.thinksWeAreDoubleNatted
private volatile boolean thinksWeAreDoubleNattedGuess that an upstream NAT defeats our mapping.portsToForward
List of ports we want to forward.portsForwarded
List of ports we have actually forwarded.forwardCallback
Callback to call when a forward fails or succeeds.PROP_ADVANCED
Config key for the console's advanced mode.- See Also:
PROP_IGNORE
Config key turning UPnP off entirely.- See Also:
ALLOW_SAME_HOST
private static final boolean ALLOW_SAME_HOSTTrue to talk to UPnP on the same host as us, probably for testing.- See Also:
_lastAction
A blockingObject.toString(). That's interesting. Cache the last ArgumentList to speed it up some. Count onlistSubServices(Device, StringBuilder)to call multiple combinations of arguments so we don't get old data._lastService
Service of the last failed action, for the log._lastArgumentList
Arguments of the last failed action, for the log.toStringLock
Lock for building the debug strings.UINT_MAX
private static final long UINT_MAXLargest unsigned 32 bit value, for lease durations.- See Also:
__id
Counter naming the UPnP jobs.BUNDLE_NAME
Bundle holding the translated console strings.- See Also:
Constructor Details
UPnP
Starts SSDP discovery and the control HTTP service on the given ports and addresses.- Parameters:
context- the application context supplying the log and the property managerssdpPort- the local port to run SSDP discovery onhttpPort- the local port to run the UPnP control HTTP service onbinds- the local addresses to bind the SSDP and HTTP sockets to
Method Details
runPlugin
public boolean runPlugin()Start the UPnP client: register listeners and clear the port mappings.- Returns:
- true if the control point started successfully
terminate
public void terminate()WARNING - Blocking up to 2 seconds.getAddress
As we only support a single active IGD, and we don't currently have any way to get any IPv6 addresses, this will return at most one IPv4 address.Blocking!!!
- Returns:
- array of length 1 containing an IPv4 address, or null
deviceAdded
Examines a newly discovered device and maps ports when it is a gateway.- Specified by:
deviceAddedin interfaceDeviceChangeListener- Parameters:
dev- the device that was added
registerPortMappings
private void registerPortMappings()Adds the port mappings the router needs.discoverService
Traverses the structure of the router device looking for the port mapping service. The first Service will be the IPv4 Service. The second Service, if present, will be the IPv6 Service.- Parameters:
router- the root device of the discovered UPnP router, whose device tree is searched for a WAN connection device- Returns:
- a list of one (IPv4) or two (IPv4 then IPv6) services, or null if no WAN connection device was found
tryAddMapping
Tries one port mapping on the service.- Parameters:
protocol- tcp or udpport- the external portdescription- the mapping name shown on the devicefp- the forward port request- Returns:
- true if the mapping was added
unregisterPortMappings
public void unregisterPortMappings()Remove all active port mappings.deviceRemoved
Drops a departed device and un-maps every port it had held.- Specified by:
deviceRemovedin interfaceDeviceChangeListener- Parameters:
dev- the device that was removed
eventNotifyReceived
EventListener callback - unused for now - supported in miniupnpd as of 1.1.- Specified by:
eventNotifyReceivedin interfaceEventListener- Parameters:
uuid- the subscription UUID identifying the subscriptionseq- the sequence number of this event notificationvarName- the name of the state variable that changedvalue- the new value of the state variable
SIDtoDevice
retryOtherDevices
private void retryOtherDevices()Go through the other devices, try re-adding them.getLocalAddresses
updateInterfaces
private void updateInterfaces()Update the SSDPSearchResponseSocketList, SSDPNotifySocketList, and HTTPServerList every time. Otherwise, we are just listening on the interfaces that were present when started.search
public void search()We overridesearch()to update the SSDPSearchResponseSocketList, SSDPNotifySocketList, and HTTPServerList every time. Otherwise, we are just listening on the interfaces that were present when started.- Overrides:
searchin classControlPoint
stringEquals
isNATPresent
private boolean isNATPresent()Whether we are behind an UPnP-enabled NAT/router.- Returns:
- true if a device and service were found
getNATAddress
getUpstreamMaxBitRate
private int getUpstreamMaxBitRate()The reported upstream bit rate in bits per second. Blocking.- Returns:
- the reported upstream bit rate in bits per second. -1 if it's not available. Blocking.
getDownstreamMaxBitRate
private int getDownstreamMaxBitRate()The reported downstream bit rate in bits per second. Blocking.- Returns:
- the reported downstream bit rate in bits per second. -1 if it's not available. Blocking.
listStateTable
Append the service state table HTML to the buffer; for debug output only.- Parameters:
serv- the service whose state variables are listedsb- the buffer to append the HTML list to
listActionsArguments
Append the action's arguments as an HTML list to the buffer; for debug output only.- Parameters:
action- the action whose argument names are listedsb- the buffer to append the HTML list to
listActions
Append all actions of the service as an HTML list to the buffer; for debug output only. Each action's arguments are listed as well.- Parameters:
service- the service whose action list is dumpedsb- the buffer to append the HTML list to
toString
toLong
Fetch a UPnP service status variable as a number.- Parameters:
action- the action to invoke, e.g. GetCommonLinkPropertiesarg- the output argument holding the valueserv- the service to query- Returns:
- the value formatted with
DataHelper.formatSize2Decimal(long), or the raw text if it is not a number, prefixed with "> " when the raw value is 2^32 - 1, the largest the device counter can hold; null if the action failed, "" if the argument is missing
toTime
Fetch a UPnP service status variable as a duration.- Parameters:
action- the action to invoke, e.g. GetStatusInfoarg- the output argument holding the valueserv- the service to query- Returns:
- the value, given in seconds, formatted as a duration, or the raw text if it is not a number; null if the action failed, "" if the argument is missing
toBoolean
Fetch a UPnP service status variable as a boolean.- Parameters:
action- the action to invoke, e.g. GetFirewallStatusarg- the output argument holding the valueserv- the service to query- Returns:
- "true" if the value is exactly "1", "false" for anything else, including a failed action or a missing argument
listSubServices
Lists a device's services in the debug output.- Parameters:
dev- the device to listsb- where the text is appended
listSubDev
Lists a device and its children in the debug output.- Parameters:
prefix- the indentation for this leveldev- the device to listsb- where the text is appended
renderStatusHTML
Warning: slow.- Returns:
- an HTML fragment describing the discovered devices and the forwarded ports
addMapping
This always requests that the external port == the internal port, for now. Blocking!- Parameters:
protocol- "TCP" or "UDP", as the device expects itport- the port to open, used for both the external and the internal portdescription- text the device shows next to the mappingfp- the port to forward; isIP6 selects the IPv4 or IPv6 service- Returns:
- true if the NAT accepted the mapping; false if there is no NAT, no matching service, or the device rejected the action
addMappingV4
private boolean addMappingV4(Service service, String protocol, int port, String description, ForwardPort fp) This always requests that the external port == the internal port, for now. Blocking!- Parameters:
service- the WAN IPv4 connection service to send the action toprotocol- "TCP" or "UDP", as the device expects itport- the port to open, used for both the external and the internal portdescription- text the device shows next to the mappingfp- the port being forwarded; added to the forwarded set on success- Returns:
- true if the device accepted the AddPortMapping action; false if the device has no such action or rejects the request
addMappingV6
This always requests that the external port == the internal port, for now. Blocking!- Parameters:
service- WANIPv6FirewallControlport- the port to open a pinhole forfp- the IPv6 port to forward; its address and protocol are sent, and its UID is updated with the value the device returns- Returns:
- true if the device accepted the AddPinhole action; false if it does not support pinholing or rejects the request
getNewUID
private int getNewUID()65536 isn't a lot, so check for dups.- Returns:
- 0 - 65535
getIP
getOurAddress
Bug fix: If the SSDP notify or search response sockets listen on more than one interface, cybergarage can get our IP address wrong, and then we send the wrong one to the UPnP device, which will reject it if it enforces strict addressing.For example, if we have interfaces 192.168.1.1 and 192.168.2.1, we could get a response from 192.168.1.99 on the 192.168.2.1 interface, but when we send something to 192.168.1.99 it will go out the 192.168.1.1 interface with a request to forward to 192.168.2.1.
So return the address of ours that is closest to his.
- Parameters:
deflt- the address to fall back to, normally the interface address cybergarage picked; it is also considered as a candidate- Returns:
- our address or deflt on failure
removeMapping
Removes a mapping through the matching NAT service, blocking on the action.- Parameters:
protocol- "TCP" or "UDP", as the device expects itport- the external port to closefp- the port being un-forwarded; isIP6 selects the IPv4 or IPv6 servicenoLog- true to suppress the log message about the result- Returns:
- true if the mapping was removed; false if there is no NAT or no matching service for the address family
removeMappingV4
private boolean removeMappingV4(Service service, String protocol, int port, ForwardPort fp, boolean noLog) Removes an IPv4 port mapping.- Parameters:
service- the WAN IPv4 connection service to send the action toprotocol- "TCP" or "UDP", as the device expects itport- the external port to closefp- the port being un-forwarded; dropped from the forwarded set either waynoLog- true to suppress the log message about the result- Returns:
- true if the device accepted the DeletePortMapping action; false if it has no such action or rejects the request
removeMappingV6
private boolean removeMappingV6(Service service, String protocol, int port, UPnP.IPv6ForwardPort fp, boolean noLog) Removes an IPv6 port mapping.- Parameters:
service- the WANIPv6FirewallControl service to send the action toprotocol- unused, the pinhole is identified by its unique IDport- unused, the pinhole is identified by its unique IDfp- the IPv6 port being un-forwarded; a negative UID means nothing to removenoLog- true to suppress the log message about the result- Returns:
- true if the device accepted the DeletePinhole action; false if it does not support pinholing or rejects the request
onChangePublicPorts
Registers a callback when the given ports change. non-blocking.- Parameters:
ports- non-nullcb- the callback replaced; replacing a different one is logged as an error, and may be null to unregister
protoToString
Names a firewall protocol number.- Parameters:
p- the protocol number- Returns:
- tcp, udp, or the number itself
registerPorts
Runs port registration in a background thread; postControlAction() can take many seconds, especially if it's failing, andonChangePublicPorts(Set, ForwardPortCallback)may be called from threads we don't want to slow down.- Parameters:
portsToForwardNow- the ports to ask the device to forward, or null to renew subscriptions only, then exit
unregisterPorts
Runs port unregistration in a background thread; postControlAction() can take many seconds, especially if it's failing, and onChangePublicPorts() may be called from threads we don't want to slow down, so throw this in a thread.- Parameters:
portsToForwardNow- the ports to ask the device to stop forwarding, non-null
main
_t
_t
_t
Looks up a localised string and substitutes two arguments.- Parameters:
s- the message key, containing {0} and {1} placeholderso- the value substituted for {0}, not translatedo2- the value substituted for {1}, not translated- Returns:
- the translated string with the placeholders replaced, or s itself when no translation exists