initial commit
This commit is contained in:
commit
a7ce8970b1
|
@ -0,0 +1,12 @@
|
|||
nimcache/
|
||||
|
||||
# Ignore dynamic, static libs and libtool archive files
|
||||
*.so
|
||||
*.dylib
|
||||
*.a
|
||||
*.la
|
||||
*.exe
|
||||
*.dll
|
||||
|
||||
/examples/miniupnpc_test
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
[submodule "vendor/miniupnp"]
|
||||
ignore = dirty
|
||||
branch = master
|
||||
path = vendor/miniupnp
|
||||
url = https://github.com/miniupnp/miniupnp.git
|
||||
[submodule "vendor/libnatpmp"]
|
||||
path = vendor/libnatpmp
|
||||
url = https://github.com/miniupnp/libnatpmp.git
|
||||
ignore = dirty
|
||||
branch = master
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2018 Status Research & Development GmbH
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Status Research & Development GmbH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,69 @@
|
|||
# Nim NAT traversal using wrappers for miniupnpc and libnatpmp
|
||||
|
||||
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
|
||||
[![License: Apache](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
|
||||
![Stability: experimental](https://img.shields.io/badge/stability-experimental-orange.svg)
|
||||
|
||||
## Installation
|
||||
|
||||
This repository uses submodules for
|
||||
[miniupnp](https://github.com/miniupnp/miniupnp) and
|
||||
[libnatpmp](https://github.com/miniupnp/libnatpmp), so either clone it all in
|
||||
one go with `git clone --recurse-submodules <REPO_URL>` or clone it normally
|
||||
and then run `git submodule update --init --recursive`.
|
||||
|
||||
Custom task that builds the static libraries before installation (optional):
|
||||
|
||||
```bash
|
||||
nimble installWithBundledLibs
|
||||
```
|
||||
|
||||
Regular `nimble install` will be enough, if you're planning on using the system libraries.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [nim-result](https://github.com/arnetheduck/nim-result)
|
||||
|
||||
## Usage
|
||||
|
||||
See the [examples](examples) directory.
|
||||
|
||||
By default, your code will be linked to bundled static libraries. If you want to dynamically link against your system libraries,
|
||||
pass the "-d:miniupnpcUseSystemLibs" flag to the Nim compiler.
|
||||
|
||||
Let's see both scenarios in action:
|
||||
|
||||
```bash
|
||||
nimble buildBundledLibs
|
||||
# statically linked against the bundled libminiupnpc.a:
|
||||
nim c -r -f examples/miniupnpc_test.nim
|
||||
# dynamically linked agains the system libminiupnpc.so:
|
||||
nim c -r -f -d:miniupnpcUseSystemLibs examples/miniupnpc_test.nim
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
miniupnpc:
|
||||
|
||||
- add IPv6 pinhole helper procs for the Miniupnp type
|
||||
|
||||
- remove / comment out unused API functions
|
||||
|
||||
- "importc" constants from headers instead of copying them
|
||||
|
||||
libnatpmp:
|
||||
|
||||
- add wrapper
|
||||
|
||||
## License
|
||||
|
||||
These wrappers are licensed and distributed under either of
|
||||
|
||||
* MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT
|
||||
|
||||
or
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
at your option. These files may not be copied, modified, or distributed except according to those terms.
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
# 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.
|
||||
|
||||
# This is the equivalent of miniupnpc/pymoduletest.py (without the command line args).
|
||||
|
||||
import nat_traversal/miniupnpc, result, strformat
|
||||
|
||||
template checkError(expr, body: untyped): untyped =
|
||||
block:
|
||||
let res {.inject.} = expr
|
||||
if res.isOk:
|
||||
body
|
||||
else:
|
||||
echo res.error
|
||||
quit(1)
|
||||
|
||||
var upnp = newMiniupnp()
|
||||
upnp.discoverDelay = 200
|
||||
echo &"Discovering... (delay={upnp.discoverDelay})"
|
||||
|
||||
checkError upnp.discover():
|
||||
echo res.value, " device(s) detected"
|
||||
|
||||
case upnp.selectIGD():
|
||||
of IGDNotFound:
|
||||
echo "Internet Gateway Device not found. Giving up."
|
||||
quit(1)
|
||||
of IGDFound:
|
||||
echo "Internet Gateway Device found."
|
||||
of IGDNotConnected:
|
||||
echo "Internet Gateway Device found but it's not connected. Trying anyway."
|
||||
of NotAnIGD:
|
||||
echo "Some device found, but it's recognised as an Internet Gateway Device. Trying anyway."
|
||||
|
||||
echo "Local ip address: ", upnp.lanaddr
|
||||
var externalIP: string
|
||||
checkError upnp.externalIPAddress():
|
||||
externalIP = res.value
|
||||
echo "External ip address: ", res.value
|
||||
checkError upnp.statusInfo():
|
||||
echo &"Status: {res.value.status}, uptime: {res.value.uptime}, lastConnError: {res.value.lastconnerror}"
|
||||
checkError upnp.connectionType():
|
||||
echo &"Connection type: {res.value}"
|
||||
|
||||
var bytesSent, bytesReceived, packetsSent, packetsReceived: culonglong
|
||||
checkError upnp.totalBytesSent():
|
||||
bytesSent = res.value
|
||||
checkError upnp.totalBytesReceived():
|
||||
bytesReceived = res.value
|
||||
checkError upnp.totalPacketsSent():
|
||||
packetsSent = res.value
|
||||
checkError upnp.totalPacketsReceived():
|
||||
packetsReceived = res.value
|
||||
echo &"Total bytes: sent {bytesSent}, received {bytesReceived}"
|
||||
echo &"Total packets: sent {packetsSent}, received {packetsReceived}"
|
||||
|
||||
proc printPortMapping(pm: PortMappingRes) =
|
||||
echo &"Port mapping: {externalIP}:{pm.externalPort} -> {pm.internalClient}:{pm.internalPort} ({pm.protocol}, \"{pm.description}\", enabled: {pm.enabled}, lease duration: {pm.leaseDuration})"
|
||||
|
||||
## enable this if you don't already have a redirection for port 64000:
|
||||
if false:
|
||||
let port = "64000"
|
||||
checkError upnp.addPortMapping(port, TCP, upnp.lanAddr, port, "port mapping test", 0, externalIP):
|
||||
echo "Added port mapping for: ", port
|
||||
checkError upnp.getSpecificPortMapping(port, TCP):
|
||||
printPortMapping(res.value)
|
||||
var randomPort: string
|
||||
checkError upnp.deletePortMapping(port, TCP):
|
||||
echo "Deleted port mapping for: ", port
|
||||
doAssert(upnp.getSpecificPortMapping(port, TCP).isErr)
|
||||
|
||||
var i = 0
|
||||
while true:
|
||||
let res = upnp.getGenericPortMapping(i)
|
||||
if res.isErr:
|
||||
break
|
||||
printPortMapping(res.value)
|
||||
inc(i)
|
||||
|
||||
let res = upnp.getPortMappingNumberOfEntries()
|
||||
if res.isOk:
|
||||
echo "Port mapping number of entries: ", res.value
|
||||
else:
|
||||
echo &"getPortMappingNumberOfEntries() is not supported by this IGD. Error message: \"{res.error}\""
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
-p:".."
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
packageName = "nat_traversal"
|
||||
version = "0.0.1"
|
||||
author = "Status Research & Development GmbH"
|
||||
description = "miniupnp client wrapper"
|
||||
license = "Apache License 2.0 or MIT"
|
||||
skipDirs = @["examples"]
|
||||
|
||||
### Dependencies
|
||||
requires "nim >= 0.19.0", "result"
|
||||
|
||||
proc compileStaticLibraries() =
|
||||
withDir "vendor/miniupnp/miniupnpc":
|
||||
exec("make libminiupnpc.a")
|
||||
|
||||
task buildBundledLibs, "build bundled libraries":
|
||||
compileStaticLibraries()
|
||||
|
||||
task installWithBundledLibs, "install with bundled libs":
|
||||
compileStaticLibraries()
|
||||
exec("nimble install")
|
||||
|
|
@ -0,0 +1,900 @@
|
|||
# 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 #
|
||||
################################
|
||||
|
||||
import ospaths
|
||||
|
||||
when defined(miniupnpcUseSystemLibs):
|
||||
{.passC: staticExec("pkg-config --cflags miniupnpc").}
|
||||
{.passL: staticExec("pkg-config --libs miniupnpc").}
|
||||
else:
|
||||
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".}
|
||||
|
||||
################
|
||||
# upnperrors.h #
|
||||
################
|
||||
|
||||
## strupnperror()
|
||||
## Return a string description of the UPnP error code
|
||||
## or NULL for undefinded errors
|
||||
proc strupnperror*(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
|
||||
|
||||
proc ParsePortListing*(buffer: cstring; bufsize: cint;
|
||||
pdata: ptr PortMappingParserData) {.
|
||||
importc: "ParsePortListing", header: "portlistingparse.h".}
|
||||
|
||||
proc FreePortListing*(pdata: ptr PortMappingParserData) {.
|
||||
importc: "FreePortListing", header: "portlistingparse.h".}
|
||||
|
||||
##############
|
||||
# miniwget.h #
|
||||
##############
|
||||
|
||||
proc miniwget*(a1: cstring; a2: ptr cint; a3: cuint; a4: ptr cint): pointer {.
|
||||
importc: "miniwget", header: "miniwget.h".}
|
||||
|
||||
proc miniwget_getaddr*(a1: cstring; a2: ptr cint; a3: cstring; a4: cint; a5: cuint;
|
||||
a6: ptr cint): pointer {.importc: "miniwget_getaddr",
|
||||
header: "miniwget.h".}
|
||||
|
||||
proc parseURL*(a1: cstring; a2: cstring; a3: ptr cushort; a4: cstringArray; a5: ptr cuint): cint {.
|
||||
importc: "parseURL", header: "miniwget.h".}
|
||||
|
||||
##################
|
||||
# upnpcommands.h #
|
||||
##################
|
||||
|
||||
## MiniUPnPc return codes :
|
||||
#TODO: write a macro for importing constants
|
||||
var UPNPCOMMAND_SUCCESSvar {.importc: "UPNPCOMMAND_SUCCESS", header: "upnpcommands.h".}: cint
|
||||
let UPNPCOMMAND_SUCCESS* = UPNPCOMMAND_SUCCESSvar
|
||||
const
|
||||
# UPNPCOMMAND_SUCCESS* = (0)
|
||||
UPNPCOMMAND_UNKNOWN_ERROR* = (-1)
|
||||
UPNPCOMMAND_INVALID_ARGS* = (-2)
|
||||
UPNPCOMMAND_HTTP_ERROR* = (-3) # not covered by strupnperror()
|
||||
UPNPCOMMAND_INVALID_RESPONSE* = (-4)
|
||||
UPNPCOMMAND_MEM_ALLOC_ERROR* = (-5) # not covered by strupnperror()
|
||||
|
||||
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
|
||||
|
||||
# proc IGDstartelt*(a1: pointer; a2: cstring; a3: cint) {.importc: "IGDstartelt",
|
||||
# header: "igd_desc_parse.h".}
|
||||
# proc IGDendelt*(a1: pointer; a2: cstring; a3: cint) {.importc: "IGDendelt",
|
||||
# header: "igd_desc_parse.h".}
|
||||
# proc IGDdata*(a1: pointer; a2: cstring; a3: cint) {.importc: "IGDdata",
|
||||
# header: "igd_desc_parse.h".}
|
||||
# when defined(DEBUG):
|
||||
# proc printIGD*(a1: ptr IGDdatas) {.importc: "printIGD", header: "igd_desc_parse.h".}
|
||||
|
||||
#############
|
||||
# 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* = (0)
|
||||
UPNPDISCOVER_UNKNOWN_ERROR* = (-1)
|
||||
UPNPDISCOVER_SOCKET_ERROR* = (-101)
|
||||
UPNPDISCOVER_MEMORY_ERROR* = (-102)
|
||||
|
||||
## versions :
|
||||
const
|
||||
MINIUPNPC_VERSION* = "2.1"
|
||||
MINIUPNPC_API_VERSION* = 17
|
||||
|
||||
## 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* = 0
|
||||
UPNP_LOCAL_PORT_SAME* = 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".}
|
||||
|
||||
## parserootdesc() :
|
||||
## parse root XML description of a UPnP device and fill the IGDdatas
|
||||
## structure.
|
||||
proc parserootdesc*(a1: cstring; a2: cint; a3: ptr IGDdatas) {.importc: "parserootdesc",
|
||||
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 GetUPNPUrls*(a1: ptr UPNPUrls; a2: ptr IGDdatas; a3: cstring; a4: cuint) {.
|
||||
importc: "GetUPNPUrls", 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 result, strutils
|
||||
|
||||
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 miniupnpFinalizer(x: Miniupnp) =
|
||||
freeUPNPDevlist(x.devList)
|
||||
x.devList = nil
|
||||
freeUPNPUrls(addr(x.urls))
|
||||
|
||||
proc newMiniupnp*(): Miniupnp =
|
||||
new(result, miniupnpFinalizer)
|
||||
if result.ttl == 0.cuchar:
|
||||
result.ttl = 2.cuchar
|
||||
|
||||
proc upnpError*(errno: cint): cstring =
|
||||
if errno == UPNPCOMMAND_HTTP_ERROR:
|
||||
return "Miniupnpc HTTP error"
|
||||
elif errno == UPNPCOMMAND_MEM_ALLOC_ERROR:
|
||||
return strupnperror(UPNPDISCOVER_MEMORY_ERROR)
|
||||
else:
|
||||
return strupnperror(errno)
|
||||
|
||||
# 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
|
||||
self.devList = upnpDiscover(self.discoverDelay,
|
||||
self.multicastIF.cstring,
|
||||
self.minisSdpdSocket.cstring,
|
||||
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 == UPNPCOMMAND_HTTP_ERROR.culonglong:
|
||||
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 == UPNPCOMMAND_HTTP_ERROR.culonglong:
|
||||
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 == UPNPCOMMAND_HTTP_ERROR.culonglong:
|
||||
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 == UPNPCOMMAND_HTTP_ERROR.culonglong:
|
||||
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
|
||||
if extIP == "":
|
||||
let IPRes = self.externalIPAddress()
|
||||
if IPRes.isErr():
|
||||
result.err(IPRes.error)
|
||||
return
|
||||
extIP = IPRes.value
|
||||
let res = UPNP_AddPortMapping(self.urls.controlURL,
|
||||
addr(self.data.first.servicetype),
|
||||
externalPort.cstring,
|
||||
internalPort.cstring,
|
||||
internalHost.cstring,
|
||||
desc.cstring,
|
||||
cstring($protocol),
|
||||
extIP.cstring,
|
||||
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
|
||||
if extIP == "":
|
||||
let IPRes = self.externalIPAddress()
|
||||
if IPRes.isErr():
|
||||
result.err(IPRes.error)
|
||||
return
|
||||
extIP = IPRes.value
|
||||
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,
|
||||
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] =
|
||||
let res = UPNP_DeletePortMapping(self.urls.controlURL,
|
||||
addr(self.data.first.servicetype),
|
||||
externalPort.cstring,
|
||||
cstring($protocol),
|
||||
remoteHost.cstring)
|
||||
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)
|
||||
let res = UPNP_GetSpecificPortMappingEntry(self.urls.controlURL,
|
||||
addr(self.data.first.servicetype),
|
||||
externalPort.cstring,
|
||||
cstring($protocol),
|
||||
remoteHost.cstring,
|
||||
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 = bool(parseInt(enabledStr))
|
||||
trimString(leaseDurationStr)
|
||||
portMapping.leaseDuration = parseBiggestUInt(leaseDurationStr)
|
||||
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 = parseEnum[UPNPProtocol](protocolStr)
|
||||
trimString(portMapping.description)
|
||||
trimString(enabledStr)
|
||||
portMapping.enabled = bool(parseInt(enabledStr))
|
||||
trimString(portMapping.remoteHost)
|
||||
trimString(leaseDurationStr)
|
||||
portMapping.leaseDuration = parseBiggestUInt(leaseDurationStr)
|
||||
result.ok(portMapping)
|
||||
else:
|
||||
result.err(upnpError(res))
|
||||
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 6d5c0db6a06036fcd97bdba10b474d5369582dba
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1ef1deec01bca16d4bbf3f690cbc06ddd19adedc
|
Loading…
Reference in New Issue