Tony Garnock-Jones <tonygarnockjones@gmail.com>
If you find that this library lacks some feature you need, or you have a suggestion for improving it, please don’t hesitate to get in touch with me!
Using this library, you can discover the external IP address of your home router, and can manage port mappings from the public internet to internal TCP and UDP ports.
The library implements
NAT-PMP, the Apple/IETF protocol for opening TCP and UDP ports on home routers, and
UPnP, in particular the "WANIPConnection" service, the Microsoft et al. protocol for doing the same, plus a whole lot more.
It provides both a low-level interface to each of the two protocols as well as a high-level interface that abstracts away from the details of the particular NAT traversal techniques available.
The high-level interface to the library lets you automatically manage NAT port mappings by simply changing calls to udp-bind! and tcp-listen to udp-bind!/public and tcp-listen/public , respectively.
Each socket managed by the library is associated with a mapping-change-listener , a background thread that tracks changes to the NAT configuration, keeping a set of "port assignments" up-to-date. A user-supplied callback (on-mapping-change) is called every time the port assignment set changes.
Each port assignment in a set is an address at which the corresponding socket is reachable. A set of port assignments includes both local (internal to the NAT) and public (external to the NAT) addresses.
procedure
hostname-stringport-no[ #:on-mapping-changeon-mapping-change])udp-socket:udp?
procedure
[ max-allow-waitreuse?hostname#:on-mapping-changeon-mapping-change])
procedure
initial-local-addresslocal-porton-mapping-change)initial-local-address:string?
struct
#:prefab)thread:thread?
struct
addressportnat-traversal-technique)#:prefab)address:string?
procedure
procedure
This library provides utilities for discovering and classifying interface IP addresses, and for discovering the local default gateway IP.
procedure
procedure
( interface-ip-addresses )→(listof string? )
procedure
the first non-private, non-local IP address found is considered best;
the first private, non-local IP address found is considered second-best;
any other address found is considered third-best;
and if no addresses were found at all, "127.0.0.1" is returned.
procedure
( wildcard-ip-address? addr)→boolean?
addr:string?
procedure
( localhost-ip-address? addr)→boolean?
addr:string?
procedure
( private-ip-address? addr)→boolean?
addr:string?
struct
internal-addressinternal-portexternal-portlifetime)#:prefab)internal-port:integer?external-port:integer?lifetime:integer?
(require nat-traversal/nat-pmp)
NAT-PMP depends on being able to learn the IP address of the current default gateway. It does so by calling gateway-ip-address .
Requests made to the gateway using NAT-PMP will eventually time out if the gateway does not support NAT-PMP. When this happens, an exception is raised by the routine making the request.
procedure
( nat-pmp-external-ip-address)→string?
procedure
protocollocal-portrequested-port[ #:refresh-intervalrefresh-interval#:on-mappingon-mapping])→persistent-mapping?local-port:integer?requested-port:integer?
Every time the externally mapped port changes (including when the mapping is first established!) the #:on-mapping callback is called with the updated mapping information. Note that the callback is invoked directly from the mapping’s thread - if it raises an exception, it will kill the persistent mapping.
If you can’t or don’t want to use persistent mapping, the following routines let you explicitly manage mappings with the gateway.
procedure
( nat-pmp-delete-mapping!mapping)→void?
mapping:mapping?
(require nat-traversal/upnp-ip-gateway)
TODO
procedure
( stop-persistent-mapping!p)→void?
p:persistent-mapping?
procedure
( refresh-persistent-mapping!p)→void?
p:persistent-mapping?
(require nat-traversal/upnp)
Routines for discovering UPnP services and calling service actions.
parameter
( default-scan-time)→exact-nonnegative-integer?
seconds:exact-nonnegative-integer?
#:transparent)
struct
#:prefab)type:string?control-url:url?event-url:url?scpd-url:url?
struct
#:prefab)name:string?
Each of the yielded dispatchers is a procedure taking a variable number of arguments:
If the first argument is 'descriptor, the underlying upnp-service is returned.
If the first argument is 'actions, a hashtable mapping action name strings to upnp-service-action instances is returned.
Otherwise, the first argument must be a string naming an action supported by the service being dispatched to. The number of additional arguments must be equal to the number of arguments expected by the named action, and they must all be strings. The result will be a hashtable mapping result name to string result value.
NAT-PMP is currently defined in an Internet-Draft.
UPnP is a vast expanse of entangled specification.
The core discovery mechanism is SSDP.
Everything else in UPnP is done with SOAP (!) over HTTP.
You can download the 70MB (!) specification zip file from the UPnP forum. Fair warning, it’s not an easy read.