Upnp

object Upnp

UPnP IGD port mapping: a port of the SSDP-discovery + SOAP-control half of libtorrent's upnp (upnp.cpp). Opens an inbound port on a NAT gateway so peers can reach us. SSDP runs over UDP multicast; the control step is plain SOAP-over-HTTP.

The codec functions (mSearchRequest, parseLocation, findControlUrl, resolveControlUrl, addPortMappingBody, deletePortMappingBody) are total functions of their arguments with no I/O. The driver functions (discover, describe, openIgd, mapTcpAndUdp, removeMapping, …) layer the UDP/HTTP round-trips on top. The engine schedules them (initial map plus a lease-refresh timer); this file owns the protocol.

XML is built/scanned as strings (no XML library, because the documents are tiny and fixed), exactly as libtorrent does with its hand-rolled xml_parse / find_control_url.

Types

Link copied to clipboard
data class ControlInfo(val serviceType: String, val controlUrl: String, val urlBase: String? = null, val modelName: String? = null)

The control endpoint extracted from an IGD device description: the service type to use in the SOAP namespace / SOAPAction, the (possibly relative) control URL, and any <URLBase> the device advertised.

Properties

Link copied to clipboard

The IGD service types find_control_url accepts, in libtorrent's priority order (upnp.cpp find_control_url). The first matching <serviceType> in the device description wins.

Link copied to clipboard
const val DEFAULT_LEASE_SECONDS: Int = 3600

Default UPnP lease duration in seconds (settings_pack::upnp_lease_duration default).

Link copied to clipboard
const val IGD_DEVICE: String
Link copied to clipboard
const val SSDP_HOST: String
Link copied to clipboard
const val SSDP_PORT: Int = 1900
Link copied to clipboard

WANIPConnection v1: the default service type libtorrent emits in SOAP.

Functions

Link copied to clipboard
suspend fun addPortMapping(client: HttpClient, controlUrl: String, externalPort: Int, internalPort: Int, internalClient: String, tcp: Boolean, leaseSeconds: Int = DEFAULT_LEASE_SECONDS, description: String = "KiteTorrent", serviceType: String = WAN_IP_SERVICE): Boolean

POST an AddPortMapping SOAP action to the gateway's control URL. Returns true on success.

Link copied to clipboard
fun addPortMappingBody(externalPort: Int, internalPort: Int, internalClient: String, tcp: Boolean, leaseSeconds: Int, description: String, serviceType: String = WAN_IP_SERVICE): String

The SOAP body for AddPortMapping (upnp.cpp create_port_mapping).

Link copied to clipboard
suspend fun deletePortMapping(client: HttpClient, controlUrl: String, externalPort: Int, tcp: Boolean, serviceType: String = WAN_IP_SERVICE): Boolean

POST a DeletePortMapping SOAP action to the gateway's control URL. Returns true on success.

Link copied to clipboard
fun deletePortMappingBody(externalPort: Int, tcp: Boolean, serviceType: String = WAN_IP_SERVICE): String

The SOAP body for DeletePortMapping (upnp.cpp delete_port_mapping).

Link copied to clipboard
suspend fun describe(client: HttpClient, locationUrl: String): IgdControl?

GET the device-description XML from a LOCATION URL discovered via SSDP and parse out the WAN(IP|PPP)Connection control endpoint. Returns a fully-resolved IgdControl (absolute control URL + service type) or null if the device has no usable IGD service. Ports the GET + on_upnp_xml step of upnp.cpp.

Link copied to clipboard
suspend fun discover(udp: UdpSocket, attempts: Int = 3): List<String>

Broadcast an M-SEARCH and collect the LOCATION URLs of responding gateways (the device-description XML URLs). Sends a few times since UDP multicast is lossy.

Link copied to clipboard
suspend fun discoverIgd(client: HttpClient, udp: UdpSocket, attempts: Int = 3): IgdControl?

Discover gateways via SSDP on udp and return the control handle of the first one that exposes a WAN(IP|PPP)Connection service. The engine typically calls this once, caches the IgdControl, then drives mapTcpAndUdp/removeMapping against it.

Link copied to clipboard

Scan an IGD device-description XML document for the first WAN(IP|PPP)Connection service and its <controlURL>, plus any <URLBase>. Ports find_control_url (upnp.cpp): we walk <service> blocks, accept the first whose <serviceType> is one of ACCEPTED_SERVICE_TYPES, and take the <controlURL> inside that same service block. Returns null when no usable service is present (libtorrent then marks the device disabled).

Link copied to clipboard
suspend fun mapTcpAndUdp(client: HttpClient, igd: IgdControl, externalPort: Int, internalPort: Int, internalClient: String, leaseSeconds: Int = DEFAULT_LEASE_SECONDS, description: String = "KiteTorrent"): UpnpMapping

(Re)map both the TCP and UDP halves of a peer port on the gateway described by igd. The call is idempotent: re-issuing the same AddPortMapping refreshes the lease (this is exactly how the engine's lease-refresh timer keeps the mapping alive, mirroring upnp::update_map re-adding before expiry).

Link copied to clipboard
fun mSearchRequest(searchTarget: String = IGD_DEVICE, mx: Int = 2): ByteArray

The SSDP M-SEARCH datagram that asks IGDs on the LAN to announce themselves.

Link copied to clipboard
suspend fun openIgd(client: HttpClient, locationUrl: String): IgdControl?

Run the full IGD flow against a single discovered LOCATION: GET + parse the device description, returning a usable control handle. Convenience over describe.

Link copied to clipboard
fun parseLocation(response: String): String?

Pull the LOCATION: header out of an SSDP response.

Link copied to clipboard
suspend fun removeMapping(client: HttpClient, mapping: UpnpMapping): Boolean

Remove a mapping previously created by mapTcpAndUdp, deleting whichever of the TCP/UDP halves were established. Returns true if every previously-mapped protocol was successfully deleted.

Link copied to clipboard

Resolve the (possibly relative) <controlURL> against the device-description locationUrl and any advertised <URLBase>, exactly as on_upnp_xml does: