mirror of
https://github.com/waku-org/nwaku.git
synced 2025-01-15 17:35:45 +00:00
885 lines
37 KiB
Nim
885 lines
37 KiB
Nim
# Copyright (c) 2019 Status Research & Development GmbH
|
|
# Licensed under either of
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
# at your option.
|
|
# This file may not be copied, modified, or distributed except according to
|
|
# those terms.
|
|
|
|
################################
|
|
# headers and library location #
|
|
################################
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
import ./utils
|
|
|
|
when defined(miniupnpcUseSystemLibs):
|
|
{.passC: staticExec("pkg-config --cflags miniupnpc").}
|
|
{.passL: staticExec("pkg-config --libs miniupnpc").}
|
|
else:
|
|
import os
|
|
const includePath = currentSourcePath.parentDir().parentDir() / "vendor" / "miniupnp" / "miniupnpc"
|
|
{.passC: "-I" & includePath.}
|
|
# We can't use the {.link.} pragma in here, because it would place the static
|
|
# library archive as the first object to be linked, which would lead to all
|
|
# its exported symbols being ignored. We move it into the last position with {.passL.}.
|
|
{.passL: includePath / "libminiupnpc.a".}
|
|
|
|
when defined(windows):
|
|
import nativesockets # for that wsaStartup() call at the end
|
|
{.passC: "-DMINIUPNP_STATICLIB".}
|
|
{.passL: "-lws2_32 -liphlpapi".}
|
|
|
|
################
|
|
# upnperrors.h #
|
|
################
|
|
|
|
## strupnperror()
|
|
## Return a string description of the UPnP error code
|
|
## or NULL for undefinded errors
|
|
proc upnpError*(err: cint): cstring {.importc: "strupnperror",
|
|
header: "upnperrors.h".}
|
|
|
|
######################
|
|
# portlistingparse.h #
|
|
######################
|
|
|
|
type
|
|
portMappingElt* {.size: sizeof(cint).} = enum
|
|
PortMappingEltNone, PortMappingEntry, NewRemoteHost, NewExternalPort,
|
|
NewProtocol, NewInternalPort, NewInternalClient, NewEnabled, NewDescription,
|
|
NewLeaseTime
|
|
|
|
PortMapping* {.importc: "struct PortMapping", header: "portlistingparse.h", bycopy.} = object
|
|
l_next* {.importc: "l_next".}: ptr PortMapping ## list next element
|
|
leaseTime* {.importc: "leaseTime".}: culonglong ## assume the used C version is at lead C99 (see miniupnpctypes.h for the definition of UNSIGNED_INTEGER)
|
|
externalPort* {.importc: "externalPort".}: cushort
|
|
internalPort* {.importc: "internalPort".}: cushort
|
|
remoteHost* {.importc: "remoteHost".}: array[64, char]
|
|
internalClient* {.importc: "internalClient".}: array[64, char]
|
|
description* {.importc: "description".}: array[64, char]
|
|
protocol* {.importc: "protocol".}: array[4, char]
|
|
enabled* {.importc: "enabled".}: cuchar
|
|
|
|
PortMappingParserData* {.importc: "struct PortMappingParserData",
|
|
header: "portlistingparse.h", bycopy.} = object
|
|
l_head* {.importc: "l_head".}: ptr PortMapping ## list head
|
|
curelt* {.importc: "curelt".}: portMappingElt
|
|
|
|
##################
|
|
# upnpcommands.h #
|
|
##################
|
|
|
|
## MiniUPnPc return codes :
|
|
const
|
|
UPNPCOMMAND_SUCCESS* = cint(0)
|
|
UPNPCOMMAND_UNKNOWN_ERROR* = cint(-1)
|
|
UPNPCOMMAND_INVALID_ARGS* = cint(-2)
|
|
UPNPCOMMAND_HTTP_ERROR* = cint(-3)
|
|
UPNPCOMMAND_INVALID_RESPONSE* = cint(-4)
|
|
UPNPCOMMAND_MEM_ALLOC_ERROR* = cint(-5)
|
|
|
|
proc UPNP_GetTotalBytesSent*(controlURL: cstring; servicetype: cstring): culonglong {.
|
|
importc: "UPNP_GetTotalBytesSent", header: "upnpcommands.h".}
|
|
|
|
proc UPNP_GetTotalBytesReceived*(controlURL: cstring; servicetype: cstring): culonglong {.
|
|
importc: "UPNP_GetTotalBytesReceived", header: "upnpcommands.h".}
|
|
|
|
proc UPNP_GetTotalPacketsSent*(controlURL: cstring; servicetype: cstring): culonglong {.
|
|
importc: "UPNP_GetTotalPacketsSent", header: "upnpcommands.h".}
|
|
|
|
proc UPNP_GetTotalPacketsReceived*(controlURL: cstring; servicetype: cstring): culonglong {.
|
|
importc: "UPNP_GetTotalPacketsReceived", header: "upnpcommands.h".}
|
|
|
|
## UPNP_GetStatusInfo()
|
|
## status and lastconnerror are 64 byte buffers
|
|
## Return values :
|
|
## UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
|
## or a UPnP Error code
|
|
proc UPNP_GetStatusInfo*(controlURL: cstring; servicetype: cstring; status: cstring;
|
|
uptime: ptr cuint; lastconnerror: cstring): cint {.
|
|
importc: "UPNP_GetStatusInfo", header: "upnpcommands.h".}
|
|
|
|
## UPNP_GetConnectionTypeInfo()
|
|
## argument connectionType is a 64 character buffer
|
|
## Return Values :
|
|
## UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
|
## or a UPnP Error code
|
|
proc UPNP_GetConnectionTypeInfo*(controlURL: cstring; servicetype: cstring;
|
|
connectionType: cstring): cint {.
|
|
importc: "UPNP_GetConnectionTypeInfo", header: "upnpcommands.h".}
|
|
|
|
## UPNP_GetExternalIPAddress() call the corresponding UPNP method.
|
|
## if the third arg is not null the value is copied to it.
|
|
## at least 16 bytes must be available
|
|
##
|
|
## Return values :
|
|
## 0 : SUCCESS
|
|
## NON ZERO : ERROR Either an UPnP error code or an unknown error.
|
|
##
|
|
## possible UPnP Errors :
|
|
## 402 Invalid Args - See UPnP Device Architecture section on Control.
|
|
## 501 Action Failed - See UPnP Device Architecture section on Control.
|
|
proc UPNP_GetExternalIPAddress*(controlURL: cstring; servicetype: cstring;
|
|
extIpAdd: cstring): cint {.
|
|
importc: "UPNP_GetExternalIPAddress", header: "upnpcommands.h".}
|
|
|
|
## UPNP_GetLinkLayerMaxBitRates()
|
|
## call WANCommonInterfaceConfig:1#GetCommonLinkProperties
|
|
##
|
|
## return values :
|
|
## UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
|
## or a UPnP Error Code.
|
|
proc UPNP_GetLinkLayerMaxBitRates*(controlURL: cstring; servicetype: cstring;
|
|
bitrateDown: ptr cuint; bitrateUp: ptr cuint): cint {.
|
|
importc: "UPNP_GetLinkLayerMaxBitRates", header: "upnpcommands.h".}
|
|
|
|
## UPNP_AddPortMapping()
|
|
## if desc is NULL, it will be defaulted to "libminiupnpc"
|
|
## remoteHost is usually NULL because IGD don't support it.
|
|
##
|
|
## Return values :
|
|
## 0 : SUCCESS
|
|
## NON ZERO : ERROR. Either an UPnP error code or an unknown error.
|
|
##
|
|
## List of possible UPnP errors for AddPortMapping :
|
|
## errorCode errorDescription (short) - Description (long)
|
|
## 402 Invalid Args - See UPnP Device Architecture section on Control.
|
|
## 501 Action Failed - See UPnP Device Architecture section on Control.
|
|
## 606 Action not authorized - The action requested REQUIRES authorization and
|
|
## the sender was not authorized.
|
|
## 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
|
|
## wild-carded
|
|
## 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
|
|
## 718 ConflictInMappingEntry - The port mapping entry specified conflicts
|
|
## with a mapping assigned previously to another client
|
|
## 724 SamePortValuesRequired - Internal and External port values
|
|
## must be the same
|
|
## 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
|
|
## permanent lease times on port mappings
|
|
## 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
|
|
## and cannot be a specific IP address or DNS name
|
|
## 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
|
|
## cannot be a specific port value
|
|
## 728 NoPortMapsAvailable - There are not enough free ports available to
|
|
## complete port mapping.
|
|
## 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
|
|
## due to conflict with other mechanisms.
|
|
## 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
|
|
##
|
|
proc UPNP_AddPortMapping*(controlURL: cstring; servicetype: cstring;
|
|
extPort: cstring; inPort: cstring; inClient: cstring;
|
|
desc: cstring; proto: cstring; remoteHost: cstring;
|
|
leaseDuration: cstring): cint {.
|
|
importc: "UPNP_AddPortMapping", header: "upnpcommands.h".}
|
|
|
|
## UPNP_AddAnyPortMapping()
|
|
## if desc is NULL, it will be defaulted to "libminiupnpc"
|
|
## remoteHost is usually NULL because IGD don't support it.
|
|
##
|
|
## Return values :
|
|
## 0 : SUCCESS
|
|
## NON ZERO : ERROR. Either an UPnP error code or an unknown error.
|
|
##
|
|
## List of possible UPnP errors for AddPortMapping :
|
|
## errorCode errorDescription (short) - Description (long)
|
|
## 402 Invalid Args - See UPnP Device Architecture section on Control.
|
|
## 501 Action Failed - See UPnP Device Architecture section on Control.
|
|
## 606 Action not authorized - The action requested REQUIRES authorization and
|
|
## the sender was not authorized.
|
|
## 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
|
|
## wild-carded
|
|
## 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
|
|
## 728 NoPortMapsAvailable - There are not enough free ports available to
|
|
## complete port mapping.
|
|
## 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
|
|
## due to conflict with other mechanisms.
|
|
## 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
|
|
##
|
|
proc UPNP_AddAnyPortMapping*(controlURL: cstring; servicetype: cstring;
|
|
extPort: cstring; inPort: cstring; inClient: cstring;
|
|
desc: cstring; proto: cstring; remoteHost: cstring;
|
|
leaseDuration: cstring; reservedPort: cstring): cint {.
|
|
importc: "UPNP_AddAnyPortMapping", header: "upnpcommands.h".}
|
|
|
|
## UPNP_DeletePortMapping()
|
|
## Use same argument values as what was used for AddPortMapping().
|
|
## remoteHost is usually NULL because IGD don't support it.
|
|
## Return Values :
|
|
## 0 : SUCCESS
|
|
## NON ZERO : error. Either an UPnP error code or an undefined error.
|
|
##
|
|
## List of possible UPnP errors for DeletePortMapping :
|
|
## 402 Invalid Args - See UPnP Device Architecture section on Control.
|
|
## 606 Action not authorized - The action requested REQUIRES authorization
|
|
## and the sender was not authorized.
|
|
## 714 NoSuchEntryInArray - The specified value does not exist in the array
|
|
proc UPNP_DeletePortMapping*(controlURL: cstring; servicetype: cstring;
|
|
extPort: cstring; proto: cstring; remoteHost: cstring): cint {.
|
|
importc: "UPNP_DeletePortMapping", header: "upnpcommands.h".}
|
|
|
|
## UPNP_DeletePortRangeMapping()
|
|
## Use same argument values as what was used for AddPortMapping().
|
|
## remoteHost is usually NULL because IGD don't support it.
|
|
## Return Values :
|
|
## 0 : SUCCESS
|
|
## NON ZERO : error. Either an UPnP error code or an undefined error.
|
|
##
|
|
## List of possible UPnP errors for DeletePortMapping :
|
|
## 606 Action not authorized - The action requested REQUIRES authorization
|
|
## and the sender was not authorized.
|
|
## 730 PortMappingNotFound - This error message is returned if no port
|
|
## mapping is found in the specified range.
|
|
## 733 InconsistentParameters - NewStartPort and NewEndPort values are not consistent.
|
|
proc UPNP_DeletePortMappingRange*(controlURL: cstring; servicetype: cstring;
|
|
extPortStart: cstring; extPortEnd: cstring;
|
|
proto: cstring; manage: cstring): cint {.
|
|
importc: "UPNP_DeletePortMappingRange", header: "upnpcommands.h".}
|
|
|
|
## UPNP_GetPortMappingNumberOfEntries()
|
|
## not supported by all routers
|
|
proc UPNP_GetPortMappingNumberOfEntries*(controlURL: cstring; servicetype: cstring;
|
|
numEntries: ptr cuint): cint {.
|
|
importc: "UPNP_GetPortMappingNumberOfEntries", header: "upnpcommands.h".}
|
|
|
|
## UPNP_GetSpecificPortMappingEntry()
|
|
## retrieves an existing port mapping
|
|
## params :
|
|
## in extPort
|
|
## in proto
|
|
## in remoteHost
|
|
## out intClient (16 bytes)
|
|
## out intPort (6 bytes)
|
|
## out desc (80 bytes)
|
|
## out enabled (4 bytes)
|
|
## out leaseDuration (16 bytes)
|
|
##
|
|
## return value :
|
|
## UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
|
## or a UPnP Error Code.
|
|
##
|
|
## List of possible UPnP errors for _GetSpecificPortMappingEntry :
|
|
## 402 Invalid Args - See UPnP Device Architecture section on Control.
|
|
## 501 Action Failed - See UPnP Device Architecture section on Control.
|
|
## 606 Action not authorized - The action requested REQUIRES authorization
|
|
## and the sender was not authorized.
|
|
## 714 NoSuchEntryInArray - The specified value does not exist in the array.
|
|
##
|
|
proc UPNP_GetSpecificPortMappingEntry*(controlURL: cstring; servicetype: cstring;
|
|
extPort: cstring; proto: cstring;
|
|
remoteHost: cstring; intClient: cstring;
|
|
intPort: cstring; desc: cstring;
|
|
enabled: cstring; leaseDuration: cstring): cint {.
|
|
importc: "UPNP_GetSpecificPortMappingEntry", header: "upnpcommands.h".}
|
|
|
|
## UPNP_GetGenericPortMappingEntry()
|
|
## params :
|
|
## in index
|
|
## out extPort (6 bytes)
|
|
## out intClient (16 bytes)
|
|
## out intPort (6 bytes)
|
|
## out protocol (4 bytes)
|
|
## out desc (80 bytes)
|
|
## out enabled (4 bytes)
|
|
## out rHost (64 bytes)
|
|
## out duration (16 bytes)
|
|
##
|
|
## return value :
|
|
## UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
|
## or a UPnP Error Code.
|
|
##
|
|
## Possible UPNP Error codes :
|
|
## 402 Invalid Args - See UPnP Device Architecture section on Control.
|
|
## 606 Action not authorized - The action requested REQUIRES authorization
|
|
## and the sender was not authorized.
|
|
## 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
|
|
##
|
|
proc UPNP_GetGenericPortMappingEntry*(controlURL: cstring; servicetype: cstring;
|
|
index: cstring; extPort: cstring;
|
|
intClient: cstring; intPort: cstring;
|
|
protocol: cstring; desc: cstring;
|
|
enabled: cstring; rHost: cstring;
|
|
duration: cstring): cint {.
|
|
importc: "UPNP_GetGenericPortMappingEntry", header: "upnpcommands.h".}
|
|
|
|
## UPNP_GetListOfPortMappings() Available in IGD v2
|
|
##
|
|
##
|
|
## Possible UPNP Error codes :
|
|
## 606 Action not Authorized
|
|
## 730 PortMappingNotFound - no port mapping is found in the specified range.
|
|
## 733 InconsistantParameters - NewStartPort and NewEndPort values are not
|
|
## consistent.
|
|
##
|
|
proc UPNP_GetListOfPortMappings*(controlURL: cstring; servicetype: cstring;
|
|
startPort: cstring; endPort: cstring;
|
|
protocol: cstring; numberOfPorts: cstring;
|
|
data: ptr PortMappingParserData): cint {.
|
|
importc: "UPNP_GetListOfPortMappings", header: "upnpcommands.h".}
|
|
|
|
## IGD:2, functions for service WANIPv6FirewallControl:1
|
|
proc UPNP_GetFirewallStatus*(controlURL: cstring; servicetype: cstring;
|
|
firewallEnabled: ptr cint;
|
|
inboundPinholeAllowed: ptr cint): cint {.
|
|
importc: "UPNP_GetFirewallStatus", header: "upnpcommands.h".}
|
|
|
|
proc UPNP_GetOutboundPinholeTimeout*(controlURL: cstring; servicetype: cstring;
|
|
remoteHost: cstring; remotePort: cstring;
|
|
intClient: cstring; intPort: cstring;
|
|
proto: cstring; opTimeout: ptr cint): cint {.
|
|
importc: "UPNP_GetOutboundPinholeTimeout", header: "upnpcommands.h".}
|
|
|
|
proc UPNP_AddPinhole*(controlURL: cstring; servicetype: cstring; remoteHost: cstring;
|
|
remotePort: cstring; intClient: cstring; intPort: cstring;
|
|
proto: cstring; leaseTime: cstring; uniqueID: cstring): cint {.
|
|
importc: "UPNP_AddPinhole", header: "upnpcommands.h".}
|
|
|
|
proc UPNP_UpdatePinhole*(controlURL: cstring; servicetype: cstring;
|
|
uniqueID: cstring; leaseTime: cstring): cint {.
|
|
importc: "UPNP_UpdatePinhole", header: "upnpcommands.h".}
|
|
|
|
proc UPNP_DeletePinhole*(controlURL: cstring; servicetype: cstring; uniqueID: cstring): cint {.
|
|
importc: "UPNP_DeletePinhole", header: "upnpcommands.h".}
|
|
|
|
proc UPNP_CheckPinholeWorking*(controlURL: cstring; servicetype: cstring;
|
|
uniqueID: cstring; isWorking: ptr cint): cint {.
|
|
importc: "UPNP_CheckPinholeWorking", header: "upnpcommands.h".}
|
|
|
|
proc UPNP_GetPinholePackets*(controlURL: cstring; servicetype: cstring;
|
|
uniqueID: cstring; packets: ptr cint): cint {.
|
|
importc: "UPNP_GetPinholePackets", header: "upnpcommands.h".}
|
|
|
|
####################
|
|
# igd_desc_parse.h #
|
|
####################
|
|
|
|
## Structure to store the result of the parsing of UPnP
|
|
## descriptions of Internet Gateway Devices
|
|
const
|
|
MINIUPNPC_URL_MAXSIZE* = (128)
|
|
|
|
type
|
|
IGDdatas_service* {.importc: "struct IGDdatas_service",
|
|
header: "igd_desc_parse.h", bycopy.} = object
|
|
controlurl* {.importc: "controlurl".}: array[MINIUPNPC_URL_MAXSIZE, char]
|
|
eventsuburl* {.importc: "eventsuburl".}: array[MINIUPNPC_URL_MAXSIZE, char]
|
|
scpdurl* {.importc: "scpdurl".}: array[MINIUPNPC_URL_MAXSIZE, char]
|
|
servicetype* {.importc: "servicetype".}: array[MINIUPNPC_URL_MAXSIZE, char] ## char devicetype[MINIUPNPC_URL_MAXSIZE];
|
|
|
|
IGDdatas* {.importc: "struct IGDdatas", header: "igd_desc_parse.h", bycopy.} = object
|
|
cureltname* {.importc: "cureltname".}: array[MINIUPNPC_URL_MAXSIZE, char]
|
|
urlbase* {.importc: "urlbase".}: array[MINIUPNPC_URL_MAXSIZE, char]
|
|
presentationurl* {.importc: "presentationurl".}: array[MINIUPNPC_URL_MAXSIZE,
|
|
char]
|
|
level* {.importc: "level".}: cint ## int state;
|
|
## "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"
|
|
CIF* {.importc: "CIF".}: IGDdatas_service ## "urn:schemas-upnp-org:service:WANIPConnection:1"
|
|
## "urn:schemas-upnp-org:service:WANPPPConnection:1"
|
|
first* {.importc: "first".}: IGDdatas_service ## if both WANIPConnection and WANPPPConnection are present
|
|
second* {.importc: "second".}: IGDdatas_service ## "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1"
|
|
IPv6FC* {.importc: "IPv6FC".}: IGDdatas_service ## tmp
|
|
tmp* {.importc: "tmp".}: IGDdatas_service
|
|
|
|
#############
|
|
# upnpdev.h #
|
|
#############
|
|
|
|
type
|
|
UPNPDev* {.importc: "struct UPNPDev", header: "upnpdev.h", bycopy.} = object
|
|
pNext* {.importc: "pNext".}: ptr UPNPDev
|
|
descURL* {.importc: "descURL".}: cstring
|
|
st* {.importc: "st".}: cstring
|
|
usn* {.importc: "usn".}: cstring
|
|
scope_id* {.importc: "scope_id".}: cuint
|
|
buffer* {.importc: "buffer".}: array[3, char]
|
|
|
|
## freeUPNPDevlist()
|
|
## free list returned by upnpDiscover()
|
|
proc freeUPNPDevlist*(devlist: ptr UPNPDev) {.importc: "freeUPNPDevlist",
|
|
header: "upnpdev.h".}
|
|
|
|
###############
|
|
# miniupnpc.h #
|
|
###############
|
|
|
|
## error codes :
|
|
const UPNPDISCOVER_SUCCESS* = cint(0)
|
|
const UPNPDISCOVER_UNKNOWN_ERROR* = cint(-1)
|
|
const UPNPDISCOVER_SOCKET_ERROR* = cint(-101)
|
|
const UPNPDISCOVER_MEMORY_ERROR* = cint(-102)
|
|
|
|
## versions :
|
|
# We use importConst here because when a system header is used, we want the
|
|
# number from the actual header file.
|
|
importConst(MINIUPNPC_VERSION, "miniupnpc.h", cstring)
|
|
importConst(MINIUPNPC_API_VERSION, "miniupnpc.h", cint)
|
|
|
|
## Source port:
|
|
## Using "1" as an alias for 1900 for backwards compatibility
|
|
## (presuming one would have used that for the "sameport" parameter)
|
|
const UPNP_LOCAL_PORT_ANY* = cint(0)
|
|
const UPNP_LOCAL_PORT_SAME* = cint(1)
|
|
|
|
## Structures definitions :
|
|
type
|
|
UPNParg* {.importc: "struct UPNParg", header: "miniupnpc.h", bycopy.} = object
|
|
elt* {.importc: "elt".}: cstring
|
|
val* {.importc: "val".}: cstring
|
|
|
|
proc simpleUPnPcommand*(a1: cint; a2: cstring; a3: cstring; a4: cstring; a5: ptr UPNParg;
|
|
a6: ptr cint): cstring {.importc: "simpleUPnPcommand",
|
|
header: "miniupnpc.h".}
|
|
|
|
## upnpDiscover()
|
|
## discover UPnP devices on the network.
|
|
## The discovered devices are returned as a chained list.
|
|
## It is up to the caller to free the list with freeUPNPDevlist().
|
|
## delay (in millisecond) is the maximum time for waiting any device
|
|
## response.
|
|
## If available, device list will be obtained from MiniSSDPd.
|
|
## Default path for minissdpd socket will be used if minissdpdsock argument
|
|
## is NULL.
|
|
## If multicastif is not NULL, it will be used instead of the default
|
|
## multicast interface for sending SSDP discover packets.
|
|
## If localport is set to UPNP_LOCAL_PORT_SAME(1) SSDP packets will be sent
|
|
## from the source port 1900 (same as destination port), if set to
|
|
## UPNP_LOCAL_PORT_ANY(0) system assign a source port, any other value will
|
|
## be attempted as the source port.
|
|
## "searchalltypes" parameter is useful when searching several types,
|
|
## if 0, the discovery will stop with the first type returning results.
|
|
## TTL should default to 2.
|
|
proc upnpDiscover*(delay: cint; multicastif: cstring; minissdpdsock: cstring;
|
|
localport: cint; ipv6: cint; ttl: cuchar; error: ptr cint): ptr UPNPDev {.
|
|
importc: "upnpDiscover", header: "miniupnpc.h".}
|
|
|
|
proc upnpDiscoverAll*(delay: cint; multicastif: cstring; minissdpdsock: cstring;
|
|
localport: cint; ipv6: cint; ttl: cuchar; error: ptr cint): ptr UPNPDev {.
|
|
importc: "upnpDiscoverAll", header: "miniupnpc.h".}
|
|
|
|
proc upnpDiscoverDevice*(device: cstring; delay: cint; multicastif: cstring;
|
|
minissdpdsock: cstring; localport: cint; ipv6: cint;
|
|
ttl: cuchar; error: ptr cint): ptr UPNPDev {.
|
|
importc: "upnpDiscoverDevice", header: "miniupnpc.h".}
|
|
|
|
proc upnpDiscoverDevices*(deviceTypes: ptr cstring; delay: cint; multicastif: cstring;
|
|
minissdpdsock: cstring; localport: cint; ipv6: cint;
|
|
ttl: cuchar; error: ptr cint; searchalltypes: cint): ptr UPNPDev {.
|
|
importc: "upnpDiscoverDevices", header: "miniupnpc.h".}
|
|
|
|
## structure used to get fast access to urls
|
|
## controlURL: controlURL of the WANIPConnection
|
|
## ipcondescURL: url of the description of the WANIPConnection
|
|
## controlURL_CIF: controlURL of the WANCommonInterfaceConfig
|
|
## controlURL_6FC: controlURL of the WANIPv6FirewallControl
|
|
##
|
|
type
|
|
UPNPUrls* {.importc: "struct UPNPUrls", header: "miniupnpc.h", bycopy.} = object
|
|
controlURL* {.importc: "controlURL".}: cstring
|
|
ipcondescURL* {.importc: "ipcondescURL".}: cstring
|
|
controlURL_CIF* {.importc: "controlURL_CIF".}: cstring
|
|
controlURL_6FC* {.importc: "controlURL_6FC".}: cstring
|
|
rootdescURL* {.importc: "rootdescURL".}: cstring
|
|
|
|
## UPNP_GetValidIGD() :
|
|
## return values :
|
|
## 0 = NO IGD found
|
|
## 1 = A valid connected IGD has been found
|
|
## 2 = A valid IGD has been found but it reported as
|
|
## not connected
|
|
## 3 = an UPnP device has been found but was not recognized as an IGD
|
|
##
|
|
## In any non-zero return case, the urls and data structures
|
|
## passed as parameters are set. Don't forget to call freeUPNPUrls(urls) to
|
|
## free allocated memory.
|
|
##
|
|
proc UPNP_GetValidIGD*(devlist: ptr UPNPDev; urls: ptr UPNPUrls; data: ptr IGDdatas;
|
|
lanaddr: cstring; lanaddrlen: cint): cint {.
|
|
importc: "UPNP_GetValidIGD", header: "miniupnpc.h".}
|
|
|
|
## UPNP_GetIGDFromUrl()
|
|
## Used when skipping the discovery process.
|
|
## When succeding, urls, data, and lanaddr arguments are set.
|
|
## return value :
|
|
## 0 - Not ok
|
|
## 1 - OK
|
|
proc UPNP_GetIGDFromUrl*(rootdescurl: cstring; urls: ptr UPNPUrls; data: ptr IGDdatas;
|
|
lanaddr: cstring; lanaddrlen: cint): cint {.
|
|
importc: "UPNP_GetIGDFromUrl", header: "miniupnpc.h".}
|
|
|
|
proc freeUPNPUrls*(a1: ptr UPNPUrls) {.importc: "FreeUPNPUrls", header: "miniupnpc.h".}
|
|
|
|
## return 0 or 1
|
|
proc UPNPIGD_IsConnected*(a1: ptr UPNPUrls; a2: ptr IGDdatas): cint {.
|
|
importc: "UPNPIGD_IsConnected", header: "miniupnpc.h".}
|
|
|
|
###################
|
|
# custom wrappers #
|
|
###################
|
|
|
|
import stew/results, std/strutils
|
|
export results
|
|
|
|
type Miniupnp* = ref object
|
|
devList*: ptr UPNPDev
|
|
urls*: UPNPUrls
|
|
data*: IGDdatas
|
|
discoverDelay*: cint # in ms, the delay defaults to 1000ms if this is left 0
|
|
multicastIF*: string
|
|
miniSsdpdSocket*: string
|
|
localPort*: cint
|
|
ipv6*: cint
|
|
ttl*: cuchar
|
|
error*: cint
|
|
lanAddr*: string
|
|
|
|
proc close*(x: Miniupnp) =
|
|
if x.devList != nil:
|
|
freeUPNPDevlist(x.devList)
|
|
x.devList = nil
|
|
freeUPNPUrls(addr(x.urls))
|
|
|
|
proc newMiniupnp*(): Miniupnp =
|
|
new(result)
|
|
result.ttl = 2.cuchar
|
|
|
|
proc `=deepCopy`*(x: Miniupnp): Miniupnp =
|
|
doAssert(false, "not implemented")
|
|
|
|
# trim a Nim string to the length of the internal cstring
|
|
proc trimString(s: var string) =
|
|
s.setLen(len(s.cstring))
|
|
|
|
## returns the number of devices discovered or an error string
|
|
proc discover*(self: Miniupnp): Result[int, cstring] =
|
|
if self.devList != nil:
|
|
freeUPNPDevlist(self.devList)
|
|
self.error = 0
|
|
var
|
|
multicastIF = if self.multicastIF.len > 0: self.multicastIF.cstring else: nil
|
|
miniSsdpdSocket = if self.miniSsdpdSocket.len > 0: self.miniSsdpdSocket.cstring else: nil
|
|
self.devList = upnpDiscover(self.discoverDelay,
|
|
multicastIF,
|
|
miniSsdpdSocket,
|
|
self.localPort,
|
|
self.ipv6,
|
|
self.ttl,
|
|
addr(self.error))
|
|
var
|
|
dev = self.devList
|
|
i = 0
|
|
|
|
while dev != nil:
|
|
inc i
|
|
dev = dev.pNext
|
|
|
|
if self.error == 0:
|
|
result.ok(i)
|
|
else:
|
|
result.err(upnpError(self.error))
|
|
|
|
type SelectIGDResult* = enum
|
|
IGDNotFound = 0
|
|
IGDFound = 1
|
|
IGDNotConnected = 2
|
|
NotAnIGD = 3
|
|
|
|
proc selectIGD*(self: Miniupnp): SelectIGDResult =
|
|
let lanaddrlen = 40.cint
|
|
self.lanAddr.setLen(40)
|
|
result = UPNP_GetValidIGD(self.devList,
|
|
addr(self.urls),
|
|
addr(self.data),
|
|
self.lanAddr.cstring,
|
|
lanaddrlen).SelectIGDResult
|
|
trimString(self.lanAddr)
|
|
|
|
type SentReceivedResult = Result[culonglong, cstring]
|
|
|
|
proc totalBytesSent*(self: Miniupnp): SentReceivedResult =
|
|
let res = UPNP_GetTotalBytesSent(self.urls.controlURL_CIF, addr(self.data.CIF.servicetype))
|
|
if res == cast[culonglong](UPNPCOMMAND_HTTP_ERROR):
|
|
result.err(upnpError(res.cint))
|
|
else:
|
|
result.ok(res)
|
|
|
|
proc totalBytesReceived*(self: Miniupnp): SentReceivedResult =
|
|
let res = UPNP_GetTotalBytesReceived(self.urls.controlURL_CIF, addr(self.data.CIF.servicetype))
|
|
if res == cast[culonglong](UPNPCOMMAND_HTTP_ERROR):
|
|
result.err(upnpError(res.cint))
|
|
else:
|
|
result.ok(res)
|
|
|
|
proc totalPacketsSent*(self: Miniupnp): SentReceivedResult =
|
|
let res = UPNP_GetTotalPacketsSent(self.urls.controlURL_CIF, addr(self.data.CIF.servicetype))
|
|
if res == cast[culonglong](UPNPCOMMAND_HTTP_ERROR):
|
|
result.err(upnpError(res.cint))
|
|
else:
|
|
result.ok(res)
|
|
|
|
proc totalPacketsReceived*(self: Miniupnp): SentReceivedResult =
|
|
let res = UPNP_GetTotalPacketsReceived(self.urls.controlURL_CIF, addr(self.data.CIF.servicetype))
|
|
if res == cast[culonglong](UPNPCOMMAND_HTTP_ERROR):
|
|
result.err(upnpError(res.cint))
|
|
else:
|
|
result.ok(res)
|
|
|
|
type StatusInfo* = object
|
|
status*: string
|
|
uptime*: cuint
|
|
lastconnerror*: string
|
|
|
|
proc statusInfo*(self: Miniupnp): Result[StatusInfo, cstring] =
|
|
var si: StatusInfo
|
|
si.status.setLen(64)
|
|
si.lastconnerror.setLen(64)
|
|
let res = UPNP_GetStatusInfo(self.urls.controlURL,
|
|
addr(self.data.first.servicetype),
|
|
si.status.cstring,
|
|
addr(si.uptime),
|
|
si.lastconnerror.cstring)
|
|
if res == UPNPCOMMAND_SUCCESS:
|
|
trimString(si.status)
|
|
trimString(si.lastconnerror)
|
|
result.ok(si)
|
|
else:
|
|
result.err(upnpError(res))
|
|
|
|
proc connectionType*(self: Miniupnp): Result[string, cstring] =
|
|
var connType = newString(64)
|
|
let res = UPNP_GetConnectionTypeInfo(self.urls.controlURL,
|
|
addr(self.data.first.servicetype),
|
|
connType.cstring)
|
|
if res == UPNPCOMMAND_SUCCESS:
|
|
trimString(connType)
|
|
result.ok(connType)
|
|
else:
|
|
result.err(upnpError(res))
|
|
|
|
proc externalIPAddress*(self: Miniupnp): Result[string, cstring] =
|
|
var externalIP = newString(40)
|
|
let res = UPNP_GetExternalIPAddress(self.urls.controlURL,
|
|
addr(self.data.first.servicetype),
|
|
externalIP.cstring)
|
|
if res == UPNPCOMMAND_SUCCESS:
|
|
trimString(externalIP)
|
|
result.ok(externalIP)
|
|
else:
|
|
result.err(upnpError(res))
|
|
|
|
type UPNPProtocol* = enum
|
|
TCP = "TCP"
|
|
UDP = "UDP"
|
|
|
|
proc addPortMapping*(self: Miniupnp,
|
|
externalPort: string,
|
|
protocol: UPNPProtocol,
|
|
internalHost: string,
|
|
internalPort: string,
|
|
desc = "miniupnpc",
|
|
leaseDuration = 0,
|
|
externalIP = ""): Result[bool, cstring] =
|
|
var extIP = externalIP.cstring
|
|
if externalIP == "":
|
|
# Some IGDs can't handle explicit external IPs here (they fail with "RemoteHostOnlySupportsWildcard").
|
|
# That's why we default to an empty address, which gets converted into a
|
|
# NULL pointer for the wrapped library.
|
|
extIP = nil
|
|
let res = UPNP_AddPortMapping(self.urls.controlURL,
|
|
addr(self.data.first.servicetype),
|
|
externalPort.cstring,
|
|
internalPort.cstring,
|
|
internalHost.cstring,
|
|
desc.cstring,
|
|
cstring($protocol),
|
|
extIP,
|
|
cstring($leaseDuration))
|
|
if res == UPNPCOMMAND_SUCCESS:
|
|
result.ok(true)
|
|
else:
|
|
result.err(upnpError(res))
|
|
|
|
## (IGD:2 only)
|
|
## Returns the actual external port (that may differ from the requested one), or
|
|
## an error string.
|
|
proc addAnyPortMapping*(self: Miniupnp,
|
|
externalPort: string,
|
|
protocol: UPNPProtocol,
|
|
internalHost: string,
|
|
internalPort: string,
|
|
desc = "miniupnpc",
|
|
leaseDuration = 0,
|
|
externalIP = ""): Result[string, cstring] =
|
|
var extIP = externalIP.cstring
|
|
if externalIP == "":
|
|
extIP = nil
|
|
var reservedPort = newString(6)
|
|
let res = UPNP_AddAnyPortMapping(self.urls.controlURL,
|
|
addr(self.data.first.servicetype),
|
|
externalPort.cstring,
|
|
internalPort.cstring,
|
|
internalHost.cstring,
|
|
desc.cstring,
|
|
cstring($protocol),
|
|
extIP,
|
|
cstring($leaseDuration),
|
|
reservedPort.cstring)
|
|
if res == UPNPCOMMAND_SUCCESS:
|
|
trimString(reservedPort)
|
|
result.ok(reservedPort)
|
|
else:
|
|
result.err(upnpError(res))
|
|
|
|
proc deletePortMapping*(self: Miniupnp,
|
|
externalPort: string,
|
|
protocol: UPNPProtocol,
|
|
remoteHost = ""): Result[bool, cstring] =
|
|
var remHost = remoteHost.cstring
|
|
if remoteHost == "":
|
|
remHost = nil
|
|
let res = UPNP_DeletePortMapping(self.urls.controlURL,
|
|
addr(self.data.first.servicetype),
|
|
externalPort.cstring,
|
|
cstring($protocol),
|
|
remHost)
|
|
if res == UPNPCOMMAND_SUCCESS:
|
|
result.ok(true)
|
|
else:
|
|
result.err(upnpError(res))
|
|
|
|
proc deletePortMappingRange*(self: Miniupnp,
|
|
externalPortStart: string,
|
|
externalPortEnd: string,
|
|
protocol: UPNPProtocol,
|
|
manage = false): Result[bool, cstring] =
|
|
let res = UPNP_DeletePortMappingRange(self.urls.controlURL,
|
|
addr(self.data.first.servicetype),
|
|
externalPortStart.cstring,
|
|
externalPortEnd.cstring,
|
|
cstring($protocol),
|
|
cstring($(manage.int)))
|
|
if res == UPNPCOMMAND_SUCCESS:
|
|
result.ok(true)
|
|
else:
|
|
result.err(upnpError(res))
|
|
|
|
## not supported by all routers
|
|
proc getPortMappingNumberOfEntries*(self: Miniupnp): Result[cuint, cstring] =
|
|
var numEntries: cuint
|
|
let res = UPNP_GetPortMappingNumberOfEntries(self.urls.controlURL,
|
|
addr(self.data.first.servicetype),
|
|
addr(numEntries))
|
|
if res == UPNPCOMMAND_SUCCESS:
|
|
result.ok(numEntries)
|
|
else:
|
|
result.err(upnpError(res))
|
|
|
|
type PortMappingRes* = object
|
|
externalPort*: string
|
|
internalClient*: string
|
|
internalPort*: string
|
|
protocol*: UPNPProtocol
|
|
description*: string
|
|
enabled*: bool
|
|
remoteHost*: string
|
|
leaseDuration*: uint64
|
|
|
|
proc getSpecificPortMapping*(self: Miniupnp,
|
|
externalPort: string,
|
|
protocol: UPNPProtocol,
|
|
remoteHost = ""): Result[PortMappingRes, cstring] =
|
|
var
|
|
portMapping = PortMappingRes(externalPort: externalPort,
|
|
protocol: protocol,
|
|
remoteHost: remoteHost)
|
|
enabledStr = newString(4)
|
|
leaseDurationStr = newString(16)
|
|
|
|
portMapping.internalClient.setLen(40)
|
|
portMapping.internalPort.setLen(6)
|
|
portMapping.description.setLen(80)
|
|
var remHost = remoteHost.cstring
|
|
if remoteHost == "":
|
|
remHost = nil
|
|
let res = UPNP_GetSpecificPortMappingEntry(self.urls.controlURL,
|
|
addr(self.data.first.servicetype),
|
|
externalPort.cstring,
|
|
cstring($protocol),
|
|
remHost,
|
|
portMapping.internalClient.cstring,
|
|
portMapping.internalPort.cstring,
|
|
portMapping.description.cstring,
|
|
enabledStr.cstring,
|
|
leaseDurationStr.cstring)
|
|
if res == UPNPCOMMAND_SUCCESS:
|
|
trimString(portMapping.internalClient)
|
|
trimString(portMapping.internalPort)
|
|
trimString(portMapping.description)
|
|
trimString(enabledStr)
|
|
portMapping.enabled = try:
|
|
bool(parseInt(enabledStr))
|
|
except ValueError: # shouldn't happen..
|
|
false
|
|
trimString(leaseDurationStr)
|
|
portMapping.leaseDuration =
|
|
try:
|
|
parseBiggestUInt(leaseDurationStr)
|
|
except ValueError:
|
|
return err("upnp: cannot parsse lease duration")
|
|
result.ok(portMapping)
|
|
else:
|
|
result.err(upnpError(res))
|
|
|
|
proc getGenericPortMapping*(self: Miniupnp,
|
|
index: int): Result[PortMappingRes, cstring] =
|
|
var
|
|
portMapping: PortMappingRes
|
|
protocolStr = newString(4)
|
|
enabledStr = newString(4)
|
|
leaseDurationStr = newString(16)
|
|
|
|
portMapping.externalPort.setLen(6)
|
|
portMapping.internalClient.setLen(40)
|
|
portMapping.internalPort.setLen(6)
|
|
portMapping.description.setLen(80)
|
|
portMapping.remoteHost.setLen(64)
|
|
let res = UPNP_GetGenericPortMappingEntry(self.urls.controlURL,
|
|
addr(self.data.first.servicetype),
|
|
cstring($index),
|
|
portMapping.externalPort.cstring,
|
|
portMapping.internalClient.cstring,
|
|
portMapping.internalPort.cstring,
|
|
protocolStr.cstring,
|
|
portMapping.description.cstring,
|
|
enabledStr.cstring,
|
|
portMapping.remoteHost.cstring,
|
|
leaseDurationStr.cstring)
|
|
if res == UPNPCOMMAND_SUCCESS:
|
|
trimString(portMapping.externalPort)
|
|
trimString(portMapping.internalClient)
|
|
trimString(portMapping.internalPort)
|
|
trimString(protocolStr)
|
|
portMapping.protocol =
|
|
try:
|
|
parseEnum[UPNPProtocol](protocolStr)
|
|
except ValueError:
|
|
return err("upnp: cannot parse upnp protocol")
|
|
trimString(portMapping.description)
|
|
trimString(enabledStr)
|
|
portMapping.enabled =
|
|
try:
|
|
bool(parseInt(enabledStr))
|
|
except ValueError:
|
|
false
|
|
trimString(portMapping.remoteHost)
|
|
trimString(leaseDurationStr)
|
|
portMapping.leaseDuration =
|
|
try:
|
|
parseBiggestUInt(leaseDurationStr)
|
|
except ValueError:
|
|
return err("upnp: cannot parse duration")
|
|
result.ok(portMapping)
|
|
else:
|
|
result.err(upnpError(res))
|
|
|