summaryrefslogtreecommitdiff
path: root/ext/miniupnpc
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2015-11-16 18:28:55 -0800
committerAdam Ierymenko <adam.ierymenko@gmail.com>2015-11-16 18:28:55 -0800
commitcf6164e8477aaa65edd07d136ef1991288feff99 (patch)
tree5841803b4af61f237e761fe18fa8055e62da5079 /ext/miniupnpc
parenta13a21377c2d028d81b5cb26567ebe23bbb2e891 (diff)
downloadinfinitytier-cf6164e8477aaa65edd07d136ef1991288feff99.tar.gz
infinitytier-cf6164e8477aaa65edd07d136ef1991288feff99.zip
Build libminiupnpc from source now, and update version.
Diffstat (limited to 'ext/miniupnpc')
-rw-r--r--ext/miniupnpc/CMakeLists.txt178
-rw-r--r--ext/miniupnpc/Changelog.txt667
-rw-r--r--ext/miniupnpc/LICENSE27
-rw-r--r--ext/miniupnpc/MANIFEST.in5
-rw-r--r--ext/miniupnpc/Makefile379
-rw-r--r--ext/miniupnpc/Makefile.mingw98
-rw-r--r--ext/miniupnpc/README61
-rw-r--r--ext/miniupnpc/VERSION1
-rw-r--r--ext/miniupnpc/apiversions.txt167
-rw-r--r--ext/miniupnpc/codelength.h54
-rw-r--r--ext/miniupnpc/connecthostport.c264
-rw-r--r--ext/miniupnpc/connecthostport.h18
-rwxr-xr-xext/miniupnpc/external-ip.sh4
-rw-r--r--ext/miniupnpc/igd_desc_parse.c123
-rw-r--r--ext/miniupnpc/igd_desc_parse.h49
-rw-r--r--ext/miniupnpc/java/JavaBridgeTest.java97
-rwxr-xr-xext/miniupnpc/java/testjava.bat8
-rwxr-xr-xext/miniupnpc/java/testjava.sh8
-rw-r--r--ext/miniupnpc/listdevices.c110
-rw-r--r--ext/miniupnpc/man3/miniupnpc.355
-rw-r--r--ext/miniupnpc/mingw32make.bat8
-rw-r--r--ext/miniupnpc/minihttptestserver.c655
-rw-r--r--ext/miniupnpc/minisoap.c123
-rw-r--r--ext/miniupnpc/minisoap.h15
-rw-r--r--ext/miniupnpc/minissdpc.c849
-rw-r--r--ext/miniupnpc/minissdpc.h58
-rw-r--r--ext/miniupnpc/miniupnpc.c684
-rw-r--r--ext/miniupnpc/miniupnpc.def45
-rw-r--r--ext/miniupnpc/miniupnpc.h152
-rw-r--r--ext/miniupnpc/miniupnpc_declspec.h21
-rw-r--r--ext/miniupnpc/miniupnpcmodule.c695
-rw-r--r--ext/miniupnpc/miniupnpcstrings.h.cmake15
-rw-r--r--ext/miniupnpc/miniupnpcstrings.h.in23
-rw-r--r--ext/miniupnpc/miniupnpctypes.h19
-rw-r--r--ext/miniupnpc/miniwget.c626
-rw-r--r--ext/miniupnpc/miniwget.h30
-rw-r--r--ext/miniupnpc/minixml.c229
-rw-r--r--ext/miniupnpc/minixml.h37
-rw-r--r--ext/miniupnpc/minixmlvalid.c163
-rw-r--r--ext/miniupnpc/msvc/miniupnpc.sln29
-rw-r--r--ext/miniupnpc/msvc/miniupnpc.vcproj283
-rw-r--r--ext/miniupnpc/msvc/upnpc-static.vcproj195
-rw-r--r--ext/miniupnpc/portlistingparse.c172
-rw-r--r--ext/miniupnpc/portlistingparse.h65
-rw-r--r--ext/miniupnpc/pymoduletest.py88
-rw-r--r--ext/miniupnpc/receivedata.c105
-rw-r--r--ext/miniupnpc/receivedata.h19
-rw-r--r--ext/miniupnpc/setup.py28
-rw-r--r--ext/miniupnpc/setupmingw32.py28
-rw-r--r--ext/miniupnpc/testdesc/linksys_WAG200G_desc.values14
-rw-r--r--ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml110
-rw-r--r--ext/miniupnpc/testdesc/new_LiveBox_desc.values20
-rw-r--r--ext/miniupnpc/testdesc/new_LiveBox_desc.xml90
-rw-r--r--ext/miniupnpc/testigddescparse.c187
-rw-r--r--ext/miniupnpc/testminiwget.c53
-rwxr-xr-xext/miniupnpc/testminiwget.sh96
-rw-r--r--ext/miniupnpc/testminixml.c89
-rw-r--r--ext/miniupnpc/testportlistingparse.c151
-rw-r--r--ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue3
-rw-r--r--ext/miniupnpc/testreplyparse/DeletePortMapping.xml6
-rw-r--r--ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue2
-rw-r--r--ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml2
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue3
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml3
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue5
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml2
-rw-r--r--ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue1
-rw-r--r--ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml1
-rw-r--r--ext/miniupnpc/testreplyparse/readme.txt7
-rwxr-xr-xext/miniupnpc/testupnpigd.py84
-rw-r--r--ext/miniupnpc/testupnpreplyparse.c96
-rwxr-xr-xext/miniupnpc/testupnpreplyparse.sh14
-rwxr-xr-xext/miniupnpc/updateminiupnpcstrings.sh53
-rw-r--r--ext/miniupnpc/upnpc.c833
-rw-r--r--ext/miniupnpc/upnpcommands.c1237
-rw-r--r--ext/miniupnpc/upnpcommands.h348
-rw-r--r--ext/miniupnpc/upnpdev.c23
-rw-r--r--ext/miniupnpc/upnpdev.h36
-rw-r--r--ext/miniupnpc/upnperrors.c107
-rw-r--r--ext/miniupnpc/upnperrors.h26
-rw-r--r--ext/miniupnpc/upnpreplyparse.c197
-rw-r--r--ext/miniupnpc/upnpreplyparse.h63
-rw-r--r--ext/miniupnpc/wingenminiupnpcstrings.c83
83 files changed, 11877 insertions, 0 deletions
diff --git a/ext/miniupnpc/CMakeLists.txt b/ext/miniupnpc/CMakeLists.txt
new file mode 100644
index 00000000..dacb1f69
--- /dev/null
+++ b/ext/miniupnpc/CMakeLists.txt
@@ -0,0 +1,178 @@
+cmake_minimum_required (VERSION 2.6)
+
+project (miniupnpc C)
+set (MINIUPNPC_VERSION 1.9)
+set (MINIUPNPC_API_VERSION 15)
+
+if (NOT CMAKE_BUILD_TYPE)
+ if (WIN32)
+ set (DEFAULT_BUILD_TYPE MinSizeRel)
+ else (WIN32)
+ set (DEFAULT_BUILD_TYPE RelWithDebInfo)
+ endif(WIN32)
+ set (CMAKE_BUILD_TYPE ${DEFAULT_BUILD_TYPE} CACHE STRING
+ "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
+ FORCE)
+endif()
+
+option (UPNPC_BUILD_STATIC "Build static library" TRUE)
+option (UPNPC_BUILD_SHARED "Build shared library" TRUE)
+if (NOT WIN32)
+ option (UPNPC_BUILD_TESTS "Build test executables" TRUE)
+endif (NOT WIN32)
+option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE)
+
+mark_as_advanced (NO_GETADDRINFO)
+
+if (NO_GETADDRINFO)
+ add_definitions (-DNO_GETADDRINFO)
+endif (NO_GETADDRINFO)
+
+if (NOT WIN32)
+ add_definitions (-DMINIUPNPC_SET_SOCKET_TIMEOUT)
+ add_definitions (-D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112L)
+else (NOT WIN32)
+ add_definitions (-D_WIN32_WINNT=0x0501) # XP or higher for getnameinfo and friends
+endif (NOT WIN32)
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ add_definitions (-D_DARWIN_C_SOURCE)
+endif ()
+
+# Set compiler specific build flags
+if (CMAKE_COMPILER_IS_GNUC)
+ # Set our own default flags at first run.
+ if (NOT CONFIGURED)
+
+ if (NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS")
+ set (_PIC -fPIC)
+ endif (CMAKE_SYSTEM_NAME STREQUAL "AmigaOS")
+
+ set (CMAKE_C_FLAGS "${_PIC} -Wall $ENV{CFLAGS}" # CMAKE_C_FLAGS gets appended to the other C flags
+ CACHE STRING "Flags used by the C compiler during normal builds." FORCE)
+ set (CMAKE_C_FLAGS_DEBUG "-g -DDDEBUG"
+ CACHE STRING "Flags used by the C compiler during debug builds." FORCE)
+ set (CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG"
+ CACHE STRING "Flags used by the C compiler during release builds." FORCE)
+ set (CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG"
+ CACHE STRING "Flags used by the C compiler during release builds." FORCE)
+ set (CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG"
+ CACHE STRING "Flags used by the C compiler during release builds." FORCE)
+
+ endif (NOT CONFIGURED)
+endif ()
+
+configure_file (${CMAKE_SOURCE_DIR}/miniupnpcstrings.h.cmake ${CMAKE_BINARY_DIR}/miniupnpcstrings.h)
+include_directories (${CMAKE_BINARY_DIR})
+
+set (MINIUPNPC_SOURCES
+ igd_desc_parse.c
+ miniupnpc.c
+ minixml.c
+ minisoap.c
+ minissdpc.c
+ miniwget.c
+ upnpc.c
+ upnpcommands.c
+ upnpdev.c
+ upnpreplyparse.c
+ upnperrors.c
+ connecthostport.c
+ portlistingparse.c
+ receivedata.c
+)
+
+if (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS")
+ set (MINIUPNPC_SOURCES ${MINIUPNPC_SOURCES} minissdpc.c)
+endif (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS")
+
+if (WIN32)
+ set_source_files_properties (${MINIUPNPC_SOURCES} PROPERTIES
+ COMPILE_DEFINITIONS "MINIUPNP_STATICLIB;MINIUPNP_EXPORTS"
+ )
+endif (WIN32)
+
+if (WIN32)
+ find_library (WINSOCK2_LIBRARY NAMES ws2_32 WS2_32 Ws2_32)
+ find_library (IPHLPAPI_LIBRARY NAMES iphlpapi)
+ set (LDLIBS ${WINSOCK2_LIBRARY} ${IPHLPAPI_LIBRARY} ${LDLIBS})
+#elseif (CMAKE_SYSTEM_NAME STREQUAL "Solaris")
+# find_library (SOCKET_LIBRARY NAMES socket)
+# find_library (NSL_LIBRARY NAMES nsl)
+# find_library (RESOLV_LIBRARY NAMES resolv)
+# set (LDLIBS ${SOCKET_LIBRARY} ${NSL_LIBRARY} ${RESOLV_LIBRARY} ${LDLIBS})
+endif (WIN32)
+
+if (NOT UPNPC_BUILD_STATIC AND NOT UPNPC_BUILD_SHARED)
+ message (FATAL "Both shared and static libraries are disabled!")
+endif (NOT UPNPC_BUILD_STATIC AND NOT UPNPC_BUILD_SHARED)
+
+if (UPNPC_BUILD_STATIC)
+ add_library (upnpc-static STATIC ${MINIUPNPC_SOURCES})
+ set_target_properties (upnpc-static PROPERTIES OUTPUT_NAME "miniupnpc")
+ target_link_libraries (upnpc-static ${LDLIBS})
+ set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} upnpc-static)
+ set (UPNPC_LIBRARY_TARGET upnpc-static)
+endif (UPNPC_BUILD_STATIC)
+
+if (UPNPC_BUILD_SHARED)
+ add_library (upnpc-shared SHARED ${MINIUPNPC_SOURCES})
+ set_target_properties (upnpc-shared PROPERTIES OUTPUT_NAME "miniupnpc")
+ set_target_properties (upnpc-shared PROPERTIES VERSION ${MINIUPNPC_VERSION})
+ set_target_properties (upnpc-shared PROPERTIES SOVERSION ${MINIUPNPC_API_VERSION})
+ target_link_libraries (upnpc-shared ${LDLIBS})
+ set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} upnpc-shared)
+ set (UPNPC_LIBRARY_TARGET upnpc-shared)
+endif (UPNPC_BUILD_SHARED)
+
+if (UPNPC_BUILD_TESTS)
+ add_executable (testminixml testminixml.c minixml.c igd_desc_parse.c)
+ target_link_libraries (testminixml ${LDLIBS})
+
+ add_executable (minixmlvalid minixmlvalid.c minixml.c)
+ target_link_libraries (minixmlvalid ${LDLIBS})
+
+ add_executable (testupnpreplyparse testupnpreplyparse.c
+ minixml.c upnpreplyparse.c)
+ target_link_libraries (testupnpreplyparse ${LDLIBS})
+
+ add_executable (testigddescparse testigddescparse.c
+ igd_desc_parse.c minixml.c miniupnpc.c miniwget.c minissdpc.c
+ upnpcommands.c upnpreplyparse.c minisoap.c connecthostport.c
+ portlistingparse.c receivedata.c
+ )
+ target_link_libraries (testigddescparse ${LDLIBS})
+
+ add_executable (testminiwget testminiwget.c
+ miniwget.c miniupnpc.c minisoap.c upnpcommands.c minissdpc.c
+ upnpreplyparse.c minixml.c igd_desc_parse.c connecthostport.c
+ portlistingparse.c receivedata.c
+ )
+ target_link_libraries (testminiwget ${LDLIBS})
+
+# set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} testminixml minixmlvalid testupnpreplyparse testigddescparse testminiwget)
+endif (UPNPC_BUILD_TESTS)
+
+
+install (TARGETS ${UPNPC_INSTALL_TARGETS}
+ RUNTIME DESTINATION bin
+ LIBRARY DESTINATION lib${LIB_SUFFIX}
+ ARCHIVE DESTINATION lib${LIB_SUFFIX}
+)
+install (FILES
+ miniupnpc.h
+ miniwget.h
+ upnpcommands.h
+ igd_desc_parse.h
+ upnpreplyparse.h
+ upnperrors.h
+ upnpdev.h
+ miniupnpctypes.h
+ portlistingparse.h
+ miniupnpc_declspec.h
+ DESTINATION include/miniupnpc
+)
+
+set (CONFIGURED YES CACHE INTERNAL "")
+
+# vim: ts=2:sw=2
diff --git a/ext/miniupnpc/Changelog.txt b/ext/miniupnpc/Changelog.txt
new file mode 100644
index 00000000..bef61f59
--- /dev/null
+++ b/ext/miniupnpc/Changelog.txt
@@ -0,0 +1,667 @@
+$Id: Changelog.txt,v 1.219 2015/10/26 17:05:06 nanard Exp $
+miniUPnP client Changelog.
+
+2015/10/26:
+ snprintf() overflow check. check overflow in simpleUPnPcommand2()
+
+2015/10/25:
+ fix compilation with old macs
+ fix compilation with mingw32 (for Appveyor)
+ fix python module for python <= 2.3
+
+2015/10/08:
+ Change sameport to localport
+ see https://github.com/miniupnp/miniupnp/pull/120
+ increments API_VERSION to 15
+
+2015/09/15:
+ Fix buffer overflow in igd_desc_parse.c/IGDstartelt()
+ Discovered by Aleksandar Nikolic of Cisco Talos
+
+2015/08/28:
+ move ssdpDiscoverDevices() to minissdpc.c
+
+2015/08/27:
+ avoid unix socket leak in getDevicesFromMiniSSDPD()
+
+2015/08/16:
+ Also accept "Up" as ConnectionStatus value
+
+2015/07/23:
+ split getDevicesFromMiniSSDPD
+ add ttl argument to upnpDiscover() functions
+ increments API_VERSION to 14
+
+2015/07/22:
+ Read USN from SSDP messages.
+
+2015/07/15:
+ Check malloc/calloc
+
+2015/06/16:
+ update getDevicesFromMiniSSDPD() to process longer minissdpd
+ responses
+
+2015/05/22:
+ add searchalltypes param to upnpDiscoverDevices()
+ increments API_VERSION to 13
+
+2015/04/30:
+ upnpc: output version on the terminal
+
+2015/04/27:
+ _BSD_SOURCE is deprecated in favor of _DEFAULT_SOURCE
+ fix CMakeLists.txt COMPILE_DEFINITIONS
+ fix getDevicesFromMiniSSDPD() not setting scope_id
+ improve -r command of upnpc command line tool
+
+2014/11/17:
+ search all :
+ upnpDiscoverDevices() / upnpDiscoverAll() functions
+ listdevices executable
+ increment API_VERSION to 12
+ validate igd_desc_parse
+
+2014/11/13:
+ increment API_VERSION to 11
+
+2014/11/05:
+ simplified function GetUPNPUrls()
+
+2014/09/11:
+ use remoteHost arg of DeletePortMapping
+
+2014/09/06:
+ Fix python3 build
+
+2014/07/01:
+ Fix parsing of IGD2 root descriptions
+
+2014/06/10:
+ rename LIBSPEC to MINIUPNP_LIBSPEC
+
+2014/05/15:
+ Add support for IGD2 AddAnyPortMapping and DeletePortMappingRange
+
+2014/02/05:
+ handle EINPROGRESS after connect()
+
+2014/02/03:
+ minixml now handle XML comments
+
+VERSION 1.9 : released 2014/01/31
+
+2014/01/31:
+ added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
+ increment API_VERSION to 10
+
+2013/12/09:
+ --help and -h arguments in upnpc.c
+
+2013/10/07:
+ fixed potential buffer overrun in miniwget.c
+ Modified UPNP_GetValidIGD() to check for ExternalIpAddress
+
+2013/08/01:
+ define MAXHOSTNAMELEN if not already done
+
+2013/06/06:
+ update upnpreplyparse to allow larger values (128 chars instead of 64)
+
+2013/05/14:
+ Update upnpreplyparse to take into account "empty" elements
+ validate upnpreplyparse.c code with "make check"
+
+2013/05/03:
+ Fix Solaris build thanks to Maciej MaƂecki
+
+2013/04/27:
+ Fix testminiwget.sh for BSD
+
+2013/03/23:
+ Fixed Makefile for *BSD
+
+2013/03/11:
+ Update Makefile to use JNAerator version 0.11
+
+2013/02/11:
+ Fix testminiwget.sh for use with dash
+ Use $(DESTDIR) in Makefile
+
+VERSION 1.8 : released 2013/02/06
+
+2012/10/16:
+ fix testminiwget with no IPv6 support
+
+2012/09/27:
+ Rename all include guards to not clash with C99
+ (7.1.3 Reserved identifiers).
+
+2012/08/30:
+ Added -e option to upnpc program (set description for port mappings)
+
+2012/08/29:
+ Python 3 support (thanks to Christopher Foo)
+
+2012/08/11:
+ Fix a memory link in UPNP_GetValidIGD()
+ Try to handle scope id in link local IPv6 URL under MS Windows
+
+2012/07/20:
+ Disable HAS_IP_MREQN on DragonFly BSD
+
+2012/06/28:
+ GetUPNPUrls() now inserts scope into link-local IPv6 addresses
+
+2012/06/23:
+ More error return checks in upnpc.c
+ #define MINIUPNPC_GET_SRC_ADDR enables receivedata() to get scope_id
+ parseURL() now parses IPv6 addresses scope
+ new parameter for miniwget() : IPv6 address scope
+ increment API_VERSION to 9
+
+2012/06/20:
+ fixed CMakeLists.txt
+
+2012/05/29
+ Improvements in testminiwget.sh
+
+VERSION 1.7 : released 2012/05/24
+
+2012/05/01:
+ Cleanup settings of CFLAGS in Makefile
+ Fix signed/unsigned integer comparaisons
+
+2012/04/20:
+ Allow to specify protocol with TCP or UDP for -A option
+
+2012/04/09:
+ Only try to fetch XML description once in UPNP_GetValidIGD()
+ Added -ansi flag to compilation, and fixed C++ comments to ANSI C comments.
+
+2012/04/05:
+ minor improvements to minihttptestserver.c
+
+2012/03/15:
+ upnperrors.c returns valid error string for unrecognized error codes
+
+2012/03/08:
+ make minihttptestserver listen on loopback interface instead of 0.0.0.0
+
+2012/01/25:
+ Maven installation thanks to Alexey Kuznetsov
+
+2012/01/21:
+ Replace WIN32 macro by _WIN32
+
+2012/01/19:
+ Fixes in java wrappers thanks to Alexey Kuznetsov :
+ https://github.com/axet/miniupnp/tree/fix-javatest/miniupnpc
+ Make and install .deb packages (python) thanks to Alexey Kuznetsov :
+ https://github.com/axet/miniupnp/tree/feature-debbuild/miniupnpc
+
+2012/01/07:
+ The multicast interface can now be specified by name with IPv4.
+
+2012/01/02:
+ Install man page
+
+2011/11/25:
+ added header to Port Mappings list in upnpc.c
+
+2011/10/09:
+ Makefile : make clean now removes jnaerator generated files.
+ MINIUPNPC_VERSION in miniupnpc.h (updated by make)
+
+2011/09/12:
+ added rootdescURL to UPNPUrls structure.
+
+VERSION 1.6 : released 2011/07/25
+
+2011/07/25:
+ Update doc for version 1.6 release
+
+2011/06/18:
+ Fix for windows in miniwget.c
+
+2011/06/04:
+ display remote host in port mapping listing
+
+2011/06/03:
+ Fix in make install : there were missing headers
+
+2011/05/26:
+ Fix the socket leak in miniwget thanks to Richard Marsh.
+ Permit to add leaseduration in -a command. Display lease duration.
+
+2011/05/15:
+ Try both LinkLocal and SiteLocal multicast address for SSDP in IPv6
+
+2011/05/09:
+ add a test in testminiwget.sh.
+ more error checking in miniwget.c
+
+2011/05/06:
+ Adding some tool to test and validate miniwget.c
+ simplified and debugged miniwget.c
+
+2011/04/11:
+ moving ReceiveData() to a receivedata.c file.
+ parsing presentation url
+ adding IGD v2 WANIPv6FirewallControl commands
+
+2011/04/10:
+ update of miniupnpcmodule.c
+ comments in miniwget.c, update in testminiwget
+ Adding errors codes from IGD v2
+ new functions in upnpc.c for IGD v2
+
+2011/04/09:
+ Support for litteral ip v6 address in miniwget
+
+2011/04/08:
+ Adding support for urn:schemas-upnp-org:service:WANIPv6FirewallControl:1
+ Updating APIVERSION
+ Supporting IPV6 in upnpDiscover()
+ Adding a -6 option to upnpc command line tool
+
+2011/03/18:
+ miniwget/parseURL() : return an error when url param is null.
+ fixing GetListOfPortMappings()
+
+2011/03/14:
+ upnpDiscover() now reporting an error code.
+ improvements in comments.
+
+2011/03/11:
+ adding miniupnpcstrings.h.cmake and CMakeLists.txt files.
+
+2011/02/15:
+ Implementation of GetListOfPortMappings()
+
+2011/02/07:
+ updates to minixml to support character data starting with spaces
+ minixml now support CDATA
+ upnpreplyparse treats <NewPortListing> specificaly
+ change in simpleUPnPcommand to return the buffer (simplification)
+
+2011/02/06:
+ Added leaseDuration argument to AddPortMapping()
+ Starting to implement GetListOfPortMappings()
+
+2011/01/11:
+ updating wingenminiupnpcstrings.c
+
+2011/01/04:
+ improving updateminiupnpcstrings.sh
+
+VERSION 1.5 : released 2011/01/01
+
+2010/12/21:
+ use NO_GETADDRINFO macro to disable the use of getaddrinfo/freeaddrinfo
+
+2010/12/11:
+ Improvements on getHTTPResponse() code.
+
+2010/12/09:
+ new code for miniwget that handle Chunked transfer encoding
+ using getHTTPResponse() in SOAP call code
+ Adding MANIFEST.in for 'python setup.py bdist_rpm'
+
+2010/11/25:
+ changes to minissdpc.c to compile under Win32.
+ see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=729
+
+2010/09/17:
+ Various improvement to Makefile from MichaƂ Górny
+
+2010/08/05:
+ Adding the script "external-ip.sh" from Reuben Hawkins
+
+2010/06/09:
+ update to python module to match modification made on 2010/04/05
+ update to Java test code to match modification made on 2010/04/05
+ all UPNP_* function now return an error if the SOAP request failed
+ at HTTP level.
+
+2010/04/17:
+ Using GetBestRoute() under win32 in order to find the
+ right interface to use.
+
+2010/04/12:
+ Retrying with HTTP/1.1 if HTTP/1.0 failed. see
+ http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1703
+
+2010/04/07:
+ avoid returning duplicates in upnpDiscover()
+
+2010/04/05:
+ Create a connecthostport.h/.c with connecthostport() function
+ and use it in miniwget and miniupnpc.
+ Use getnameinfo() instead of inet_ntop or inet_ntoa
+ Work to make miniupnpc IPV6 compatible...
+ Add java test code.
+ Big changes in order to support device having both WANIPConnection
+ and WANPPPConnection.
+
+2010/04/04:
+ Use getaddrinfo() instead of gethostbyname() in miniwget.
+
+2010/01/06:
+ #define _DARWIN_C_SOURCE for Mac OS X
+
+2009/12/19:
+ Improve MinGW32 build
+
+2009/12/11:
+ adding a MSVC9 project to build the static library and executable
+
+2009/12/10:
+ Fixing some compilation stuff for Windows/MinGW
+
+2009/12/07:
+ adaptations in Makefile and updateminiupnpcstring.sh for AmigaOS
+ some fixes for Windows when using virtual ethernet adapters (it is the
+ case with VMWare installed).
+
+2009/12/04:
+ some fixes for AmigaOS compilation
+ Changed HTTP version to HTTP/1.0 for Soap too (to prevent chunked
+ transfer encoding)
+
+2009/12/03:
+ updating printIDG and testigddescparse.c for debug.
+ modifications to compile under AmigaOS
+ adding a testminiwget program
+ Changed miniwget to advertise itself as HTTP/1.0 to prevent chunked
+ transfer encoding
+
+2009/11/26:
+ fixing updateminiupnpcstrings.sh to take into account
+ which command that does not return an error code.
+
+VERSION 1.4 : released 2009/10/30
+
+2009/10/16:
+ using Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS in python module.
+
+2009/10/10:
+ Some fixes for compilation under Solaris
+ compilation fixes : http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1464
+
+2009/09/21:
+ fixing the code to ignore EINTR during connect() calls.
+
+2009/08/07:
+ Set socket timeout for connect()
+ Some cleanup in miniwget.c
+
+2009/08/04:
+ remove multiple redirections with -d in upnpc.c
+ Print textual error code in upnpc.c
+ Ignore EINTR during the connect() and poll() calls.
+
+2009/07/29:
+ fix in updateminiupnpcstrings.sh if OS name contains "/"
+ Sending a correct value for MX: field in SSDP request
+
+2009/07/20:
+ Change the Makefile to compile under Mac OS X
+ Fixed a stackoverflow in getDevicesFromMiniSSDPD()
+
+2009/07/09:
+ Compile under Haiku
+ generate miniupnpcstrings.h.in from miniupnpcstrings.h
+
+2009/06/04:
+ patching to compile under CygWin and cross compile for minGW
+
+VERSION 1.3 :
+
+2009/04/17:
+ updating python module
+ Use strtoull() when using C99
+
+2009/02/28:
+ Fixed miniwget.c for compiling under sun
+
+2008/12/18:
+ cleanup in Makefile (thanks to Paul de Weerd)
+ minissdpc.c : win32 compatibility
+ miniupnpc.c : changed xmlns prefix from 'm' to 'u'
+ Removed NDEBUG (using DEBUG)
+
+2008/10/14:
+ Added the ExternalHost argument to DeletePortMapping()
+
+2008/10/11:
+ Added the ExternalHost argument to AddPortMapping()
+ Put a correct User-Agent: header in HTTP requests.
+
+VERSION 1.2 :
+
+2008/10/07:
+ Update docs
+
+2008/09/25:
+ Integrated sameport patch from Dario Meloni : Added a "sameport"
+ argument to upnpDiscover().
+
+2008/07/18:
+ small modif to make Clang happy :)
+
+2008/07/17:
+ #define SOAPPREFIX "s" in miniupnpc.c in order to remove SOAP-ENV...
+
+2008/07/14:
+ include declspec.h in installation (to /usr/include/miniupnpc)
+
+VERSION 1.1 :
+
+2008/07/04:
+ standard options for install/ln instead of gnu-specific stuff.
+
+2008/07/03:
+ now builds a .dll and .lib with win32. (mingw32)
+
+2008/04/28:
+ make install now install the binary of the upnpc tool
+
+2008/04/27:
+ added testupnpigd.py
+ added error strings for miniupnpc "internal" errors
+ improved python module error/exception reporting.
+
+2008/04/23:
+ Completely rewrite igd_desc_parse.c in order to be compatible with
+ Linksys WAG200G
+ Added testigddescparse
+ updated python module
+
+VERSION 1.0 :
+
+2008/02/21:
+ put some #ifdef DEBUG around DisplayNameValueList()
+
+2008/02/18:
+ Improved error reporting in upnpcommands.c
+ UPNP_GetStatusInfo() returns LastConnectionError
+
+2008/02/16:
+ better error handling in minisoap.c
+ improving display of "valid IGD found" in upnpc.c
+
+2008/02/03:
+ Fixing UPNP_GetValidIGD()
+ improved make install :)
+
+2007/12/22:
+ Adding upnperrors.c/h to provide a strupnperror() function
+ used to translate UPnP error codes to string.
+
+2007/12/19:
+ Fixing getDevicesFromMiniSSDPD()
+ improved error reporting of UPnP functions
+
+2007/12/18:
+ It is now possible to specify a different location for MiniSSDPd socket.
+ working with MiniSSDPd is now more efficient.
+ python module improved.
+
+2007/12/16:
+ improving error reporting
+
+2007/12/13:
+ Try to improve compatibility by using HTTP/1.0 instead of 1.1 and
+ XML a bit different for SOAP.
+
+2007/11/25:
+ fixed select() call for linux
+
+2007/11/15:
+ Added -fPIC to CFLAG for better shared library code.
+
+2007/11/02:
+ Fixed a potential socket leak in miniwget2()
+
+2007/10/16:
+ added a parameter to upnpDiscover() in order to allow the use of another
+ interface than the default multicast interface.
+
+2007/10/12:
+ Fixed the creation of symbolic link in Makefile
+
+2007/10/08:
+ Added man page
+
+2007/10/02:
+ fixed memory bug in GetUPNPUrls()
+
+2007/10/01:
+ fixes in the Makefile
+ Added UPNP_GetIGDFromUrl() and adapted the sample program accordingly.
+ Added SONAME in the shared library to please debian :)
+ fixed MS Windows compilation (minissdpd is not available under MS Windows).
+
+2007/09/25:
+ small change to Makefile to be able to install in a different location
+ (default is /usr)
+
+2007/09/24:
+ now compiling both shared and static library
+
+2007/09/19:
+ Cosmetic changes on upnpc.c
+
+2007/09/02:
+ adapting to new miniSSDPd (release version ?)
+
+2007/08/31:
+ Usage of miniSSDPd to skip discovery process.
+
+2007/08/27:
+ fixed python module to allow compilation with Python older than Python 2.4
+
+2007/06/12:
+ Added a python module.
+
+2007/05/19:
+ Fixed compilation under MinGW
+
+2007/05/15:
+ fixed a memory leak in AddPortMapping()
+ Added testupnpreplyparse executable to check the parsing of
+ upnp soap messages
+ minixml now ignore namespace prefixes.
+
+2007/04/26:
+ upnpc now displays external ip address with -s or -l
+
+2007/04/11:
+ changed MINIUPNPC_URL_MAXSIZE to 128 to accomodate the "BT Voyager 210"
+
+2007/03/19:
+ cleanup in miniwget.c
+
+2007/03/01:
+ Small typo fix...
+
+2007/01/30:
+ Now parsing the HTTP header from SOAP responses in order to
+ get content-length value.
+
+2007/01/29:
+ Fixed the Soap Query to speedup the HTTP request.
+ added some Win32 DLL stuff...
+
+2007/01/27:
+ Fixed some WIN32 compatibility issues
+
+2006/12/14:
+ Added UPNPIGD_IsConnected() function in miniupnp.c/.h
+ Added UPNP_GetValidIGD() in miniupnp.c/.h
+ cleaned upnpc.c main(). now using UPNP_GetValidIGD()
+
+2006/12/07:
+ Version 1.0-RC1 released
+
+2006/12/03:
+ Minor changes to compile under SunOS/Solaris
+
+2006/11/30:
+ made a minixml parser validator program
+ updated minixml to handle attributes correctly
+
+2006/11/22:
+ Added a -r option to the upnpc sample thanks to Alexander Hubmann.
+
+2006/11/19:
+ Cleanup code to make it more ANSI C compliant
+
+2006/11/10:
+ detect and display local lan address.
+
+2006/11/04:
+ Packets and Bytes Sent/Received are now unsigned int.
+
+2006/11/01:
+ Bug fix thanks to Giuseppe D'Angelo
+
+2006/10/31:
+ C++ compatibility for .h files.
+ Added a way to get ip Address on the LAN used to reach the IGD.
+
+2006/10/25:
+ Added M-SEARCH to the services in the discovery process.
+
+2006/10/22:
+ updated the Makefile to use makedepend, added a "make install"
+ update Makefile
+
+2006/10/20:
+ fixing the description url parsing thanks to patch sent by
+ Wayne Dawe.
+ Fixed/translated some comments.
+ Implemented a better discover process, first looking
+ for IGD then for root devices (as some devices only reply to
+ M-SEARCH for root devices).
+
+2006/09/02:
+ added freeUPNPDevlist() function.
+
+2006/08/04:
+ More command line arguments checking
+
+2006/08/01:
+ Added the .bat file to compile under Win32 with minGW32
+
+2006/07/31:
+ Fixed the rootdesc parser (igd_desc_parse.c)
+
+2006/07/20:
+ parseMSEARCHReply() is now returning the ST: line as well
+ starting changes to detect several UPnP devices on the network
+
+2006/07/19:
+ using GetCommonLinkProperties to get down/upload bitrate
+
diff --git a/ext/miniupnpc/LICENSE b/ext/miniupnpc/LICENSE
new file mode 100644
index 00000000..cb5a0604
--- /dev/null
+++ b/ext/miniupnpc/LICENSE
@@ -0,0 +1,27 @@
+MiniUPnPc
+Copyright (c) 2005-2015, Thomas BERNARD
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/ext/miniupnpc/MANIFEST.in b/ext/miniupnpc/MANIFEST.in
new file mode 100644
index 00000000..54b86f95
--- /dev/null
+++ b/ext/miniupnpc/MANIFEST.in
@@ -0,0 +1,5 @@
+include README
+include miniupnpcmodule.c
+include setup.py
+include *.h
+include libminiupnpc.a
diff --git a/ext/miniupnpc/Makefile b/ext/miniupnpc/Makefile
new file mode 100644
index 00000000..4a4f1612
--- /dev/null
+++ b/ext/miniupnpc/Makefile
@@ -0,0 +1,379 @@
+# $Id: Makefile,v 1.132 2015/10/26 16:59:54 nanard Exp $
+# MiniUPnP Project
+# http://miniupnp.free.fr/
+# http://miniupnp.tuxfamily.org/
+# https://github.com/miniupnp/miniupnp
+# (c) 2005-2015 Thomas Bernard
+# to install use :
+# $ make DESTDIR=/tmp/dummylocation install
+# or
+# $ INSTALLPREFIX=/usr/local make install
+# or
+# $ make install (default INSTALLPREFIX is /usr)
+OS = $(shell uname -s)
+VERSION = $(shell cat VERSION)
+
+ifeq ($(OS), Darwin)
+JARSUFFIX=mac
+LIBTOOL ?= $(shell which libtool)
+endif
+ifeq ($(OS), Linux)
+JARSUFFIX=linux
+endif
+ifneq (,$(findstring NT-5.1,$(OS)))
+JARSUFFIX=win32
+endif
+
+HAVE_IPV6 ?= yes
+export HAVE_IPV6
+
+CC ?= gcc
+#AR = gar
+#CFLAGS = -O -g -DDEBUG
+CFLAGS ?= -O
+CFLAGS += -Wall
+CFLAGS += -W -Wstrict-prototypes
+CFLAGS += -fno-common
+CFLAGS += -DMINIUPNPC_SET_SOCKET_TIMEOUT
+CFLAGS += -DMINIUPNPC_GET_SRC_ADDR
+CFLAGS += -D_BSD_SOURCE
+CFLAGS += -D_DEFAULT_SOURCE
+ifneq ($(OS), FreeBSD)
+ifneq ($(OS), Darwin)
+#CFLAGS += -D_POSIX_C_SOURCE=200112L
+CFLAGS += -D_XOPEN_SOURCE=600
+endif
+endif
+#CFLAGS += -ansi
+# -DNO_GETADDRINFO
+INSTALL = install
+SH = /bin/sh
+JAVA = java
+# see http://code.google.com/p/jnaerator/
+#JNAERATOR = jnaerator-0.9.7.jar
+#JNAERATOR = jnaerator-0.9.8-shaded.jar
+#JNAERATORARGS = -library miniupnpc
+#JNAERATOR = jnaerator-0.10-shaded.jar
+#JNAERATOR = jnaerator-0.11-shaded.jar
+# https://repo1.maven.org/maven2/com/nativelibs4java/jnaerator/0.12/jnaerator-0.12-shaded.jar
+JNAERATOR = jnaerator-0.12-shaded.jar
+JNAERATORARGS = -mode StandaloneJar -runtime JNAerator -library miniupnpc
+#JNAERATORBASEURL = http://jnaerator.googlecode.com/files/
+JNAERATORBASEURL = https://repo1.maven.org/maven2/com/nativelibs4java/jnaerator/0.12
+
+ifeq (SunOS, $(OS))
+ LDFLAGS=-lsocket -lnsl -lresolv
+endif
+
+# APIVERSION is used to build SONAME
+APIVERSION = 15
+
+SRCS = igd_desc_parse.c miniupnpc.c minixml.c minisoap.c miniwget.c \
+ upnpc.c upnpcommands.c upnpreplyparse.c testminixml.c \
+ minixmlvalid.c testupnpreplyparse.c minissdpc.c \
+ upnperrors.c testigddescparse.c testminiwget.c \
+ connecthostport.c portlistingparse.c receivedata.c \
+ upnpdev.c testportlistingparse.c miniupnpcmodule.c \
+ minihttptestserver.c \
+ listdevices.c
+
+LIBOBJS = miniwget.o minixml.o igd_desc_parse.o minisoap.o \
+ miniupnpc.o upnpreplyparse.o upnpcommands.o upnperrors.o \
+ connecthostport.o portlistingparse.o receivedata.o upnpdev.o
+
+ifneq ($(OS), AmigaOS)
+CFLAGS := -fPIC $(CFLAGS)
+LIBOBJS := $(LIBOBJS) minissdpc.o
+endif
+
+OBJS = $(patsubst %.c,%.o,$(SRCS))
+
+# HEADERS to install
+HEADERS = miniupnpc.h miniwget.h upnpcommands.h igd_desc_parse.h \
+ upnpreplyparse.h upnperrors.h miniupnpctypes.h \
+ portlistingparse.h \
+ upnpdev.h \
+ miniupnpc_declspec.h
+
+# library names
+LIBRARY = libminiupnpc.a
+ifeq ($(OS), Darwin)
+ SHAREDLIBRARY = libminiupnpc.dylib
+ SONAME = $(basename $(SHAREDLIBRARY)).$(APIVERSION).dylib
+ CFLAGS := -D_DARWIN_C_SOURCE $(CFLAGS)
+else
+ifeq ($(JARSUFFIX), win32)
+ SHAREDLIBRARY = miniupnpc.dll
+else
+ # Linux/BSD/etc.
+ SHAREDLIBRARY = libminiupnpc.so
+ SONAME = $(SHAREDLIBRARY).$(APIVERSION)
+endif
+endif
+
+EXECUTABLES = upnpc-static listdevices
+EXECUTABLES_ADDTESTS = testminixml minixmlvalid testupnpreplyparse \
+ testigddescparse testminiwget testportlistingparse
+
+TESTMINIXMLOBJS = minixml.o igd_desc_parse.o testminixml.o
+
+TESTMINIWGETOBJS = miniwget.o testminiwget.o connecthostport.o receivedata.o
+
+TESTUPNPREPLYPARSE = testupnpreplyparse.o minixml.o upnpreplyparse.o
+
+TESTPORTLISTINGPARSE = testportlistingparse.o minixml.o portlistingparse.o
+
+TESTIGDDESCPARSE = testigddescparse.o igd_desc_parse.o minixml.o \
+ miniupnpc.o miniwget.o upnpcommands.o upnpreplyparse.o \
+ minisoap.o connecthostport.o receivedata.o \
+ portlistingparse.o
+
+ifneq ($(OS), AmigaOS)
+EXECUTABLES := $(EXECUTABLES) upnpc-shared
+TESTMINIWGETOBJS := $(TESTMINIWGETOBJS) minissdpc.o
+TESTIGDDESCPARSE := $(TESTIGDDESCPARSE) minissdpc.o
+endif
+
+LIBDIR ?= lib
+# install directories
+INSTALLPREFIX ?= $(PREFIX)/usr
+INSTALLDIRINC = $(INSTALLPREFIX)/include/miniupnpc
+INSTALLDIRLIB = $(INSTALLPREFIX)/$(LIBDIR)
+INSTALLDIRBIN = $(INSTALLPREFIX)/bin
+INSTALLDIRMAN = $(INSTALLPREFIX)/share/man
+
+FILESTOINSTALL = $(LIBRARY) $(EXECUTABLES)
+ifneq ($(OS), AmigaOS)
+FILESTOINSTALL := $(FILESTOINSTALL) $(SHAREDLIBRARY)
+endif
+
+
+.PHONY: install clean depend all check test everything \
+ installpythonmodule updateversion
+# validateminixml validateminiwget
+
+all: $(LIBRARY) $(EXECUTABLES)
+
+test: check
+
+check: validateminixml validateminiwget validateupnpreplyparse \
+ validateportlistingparse validateigddescparse
+
+everything: all $(EXECUTABLES_ADDTESTS)
+
+pythonmodule: $(LIBRARY) miniupnpcmodule.c setup.py
+ python setup.py build
+ touch $@
+
+installpythonmodule: pythonmodule
+ python setup.py install
+
+pythonmodule3: $(LIBRARY) miniupnpcmodule.c setup.py
+ python3 setup.py build
+ touch $@
+
+installpythonmodule3: pythonmodule3
+ python3 setup.py install
+
+validateminixml: minixmlvalid
+ @echo "minixml validation test"
+ ./minixmlvalid
+ touch $@
+
+validateminiwget: testminiwget minihttptestserver testminiwget.sh
+ @echo "miniwget validation test"
+ ./testminiwget.sh
+ touch $@
+
+validateupnpreplyparse: testupnpreplyparse testupnpreplyparse.sh
+ @echo "upnpreplyparse validation test"
+ ./testupnpreplyparse.sh
+ touch $@
+
+validateportlistingparse: testportlistingparse
+ @echo "portlistingparse validation test"
+ ./testportlistingparse
+ touch $@
+
+validateigddescparse: testigddescparse
+ @echo "igd desc parse validation test"
+ ./testigddescparse testdesc/new_LiveBox_desc.xml testdesc/new_LiveBox_desc.values
+ ./testigddescparse testdesc/linksys_WAG200G_desc.xml testdesc/linksys_WAG200G_desc.values
+ touch $@
+
+clean:
+ $(RM) $(LIBRARY) $(SHAREDLIBRARY) $(EXECUTABLES) $(OBJS) miniupnpcstrings.h
+ $(RM) $(EXECUTABLES_ADDTESTS)
+ # clean python stuff
+ $(RM) pythonmodule pythonmodule3
+ $(RM) validateminixml validateminiwget validateupnpreplyparse
+ $(RM) validateigddescparse
+ $(RM) minihttptestserver
+ $(RM) -r build/ dist/
+ #python setup.py clean
+ # clean jnaerator stuff
+ $(RM) _jnaerator.* java/miniupnpc_$(OS).jar
+
+distclean: clean
+ $(RM) $(JNAERATOR) java/*.jar java/*.class out.errors.txt
+
+updateversion: miniupnpc.h
+ cp miniupnpc.h miniupnpc.h.bak
+ sed 's/\(.*MINIUPNPC_API_VERSION\s\+\)[0-9]\+/\1$(APIVERSION)/' < miniupnpc.h.bak > miniupnpc.h
+
+install: updateversion $(FILESTOINSTALL)
+ $(INSTALL) -d $(DESTDIR)$(INSTALLDIRINC)
+ $(INSTALL) -m 644 $(HEADERS) $(DESTDIR)$(INSTALLDIRINC)
+ $(INSTALL) -d $(DESTDIR)$(INSTALLDIRLIB)
+ $(INSTALL) -m 644 $(LIBRARY) $(DESTDIR)$(INSTALLDIRLIB)
+ifneq ($(OS), AmigaOS)
+ $(INSTALL) -m 644 $(SHAREDLIBRARY) $(DESTDIR)$(INSTALLDIRLIB)/$(SONAME)
+ ln -fs $(SONAME) $(DESTDIR)$(INSTALLDIRLIB)/$(SHAREDLIBRARY)
+endif
+ $(INSTALL) -d $(DESTDIR)$(INSTALLDIRBIN)
+ifeq ($(OS), AmigaOS)
+ $(INSTALL) -m 755 upnpc-static $(DESTDIR)$(INSTALLDIRBIN)/upnpc
+else
+ $(INSTALL) -m 755 upnpc-shared $(DESTDIR)$(INSTALLDIRBIN)/upnpc
+endif
+ $(INSTALL) -m 755 external-ip.sh $(DESTDIR)$(INSTALLDIRBIN)/external-ip
+ifneq ($(OS), AmigaOS)
+ $(INSTALL) -d $(DESTDIR)$(INSTALLDIRMAN)/man3
+ $(INSTALL) -m 644 man3/miniupnpc.3 $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
+ifeq ($(OS), Linux)
+ gzip -f $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
+endif
+endif
+
+install-static: updateversion $(FILESTOINSTALL)
+ $(INSTALL) -d $(DESTDIR)$(INSTALLDIRINC)
+ $(INSTALL) -m 644 $(HEADERS) $(DESTDIR)$(INSTALLDIRINC)
+ $(INSTALL) -d $(DESTDIR)$(INSTALLDIRLIB)
+ $(INSTALL) -m 644 $(LIBRARY) $(DESTDIR)$(INSTALLDIRLIB)
+ $(INSTALL) -d $(DESTDIR)$(INSTALLDIRBIN)
+ $(INSTALL) -m 755 external-ip.sh $(DESTDIR)$(INSTALLDIRBIN)/external-ip
+
+cleaninstall:
+ $(RM) -r $(DESTDIR)$(INSTALLDIRINC)
+ $(RM) $(DESTDIR)$(INSTALLDIRLIB)/$(LIBRARY)
+ $(RM) $(DESTDIR)$(INSTALLDIRLIB)/$(SHAREDLIBRARY)
+
+depend:
+ makedepend -Y -- $(CFLAGS) -- $(SRCS) 2>/dev/null
+
+$(LIBRARY): $(LIBOBJS)
+ifeq ($(OS), Darwin)
+ $(LIBTOOL) -static -o $@ $?
+else
+ $(AR) crs $@ $?
+endif
+
+$(SHAREDLIBRARY): $(LIBOBJS)
+ifeq ($(OS), Darwin)
+# $(CC) -dynamiclib $(LDFLAGS) -Wl,-install_name,$(SONAME) -o $@ $^
+ $(CC) -dynamiclib $(LDFLAGS) -Wl,-install_name,$(INSTALLDIRLIB)/$(SONAME) -o $@ $^
+else
+ $(CC) -shared $(LDFLAGS) -Wl,-soname,$(SONAME) -o $@ $^
+endif
+
+upnpc-static: upnpc.o $(LIBRARY)
+ $(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
+
+upnpc-shared: upnpc.o $(SHAREDLIBRARY)
+ $(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
+
+listdevices: listdevices.o $(LIBRARY)
+
+testminixml: $(TESTMINIXMLOBJS)
+
+testminiwget: $(TESTMINIWGETOBJS)
+
+minixmlvalid: minixml.o minixmlvalid.o
+
+testupnpreplyparse: $(TESTUPNPREPLYPARSE)
+
+testigddescparse: $(TESTIGDDESCPARSE)
+
+testportlistingparse: $(TESTPORTLISTINGPARSE)
+
+miniupnpcstrings.h: miniupnpcstrings.h.in updateminiupnpcstrings.sh VERSION
+ $(SH) updateminiupnpcstrings.sh
+
+# ftp tool supplied with OpenBSD can download files from http.
+jnaerator-%.jar:
+ wget $(JNAERATORBASEURL)/$@ || \
+ curl -o $@ $(JNAERATORBASEURL)/$@ || \
+ ftp $(JNAERATORBASEURL)/$@
+
+jar: $(SHAREDLIBRARY) $(JNAERATOR)
+ $(JAVA) -jar $(JNAERATOR) $(JNAERATORARGS) \
+ miniupnpc.h miniupnpc_declspec.h upnpcommands.h upnpreplyparse.h \
+ igd_desc_parse.h miniwget.h upnperrors.h $(SHAREDLIBRARY) \
+ -package fr.free.miniupnp -o . -jar java/miniupnpc_$(JARSUFFIX).jar -v
+
+mvn_install:
+ mvn install:install-file -Dfile=java/miniupnpc_$(JARSUFFIX).jar \
+ -DgroupId=com.github \
+ -DartifactId=miniupnp \
+ -Dversion=$(VERSION) \
+ -Dpackaging=jar \
+ -Dclassifier=$(JARSUFFIX) \
+ -DgeneratePom=true \
+ -DcreateChecksum=true
+
+# make .deb packages
+deb: /usr/share/pyshared/stdeb all
+ (python setup.py --command-packages=stdeb.command bdist_deb)
+
+# install .deb packages
+ideb:
+ (sudo dpkg -i deb_dist/*.deb)
+
+/usr/share/pyshared/stdeb: /usr/share/doc/python-all-dev
+ (sudo apt-get install python-stdeb)
+
+/usr/share/doc/python-all-dev:
+ (sudo apt-get install python-all-dev)
+
+minihttptestserver: minihttptestserver.o
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+igd_desc_parse.o: igd_desc_parse.h
+miniupnpc.o: miniupnpc.h miniupnpc_declspec.h igd_desc_parse.h upnpdev.h
+miniupnpc.o: minissdpc.h miniwget.h minisoap.h minixml.h upnpcommands.h
+miniupnpc.o: upnpreplyparse.h portlistingparse.h miniupnpctypes.h
+miniupnpc.o: connecthostport.h
+minixml.o: minixml.h
+minisoap.o: minisoap.h miniupnpcstrings.h
+miniwget.o: miniupnpcstrings.h miniwget.h miniupnpc_declspec.h
+miniwget.o: connecthostport.h receivedata.h
+upnpc.o: miniwget.h miniupnpc_declspec.h miniupnpc.h igd_desc_parse.h
+upnpc.o: upnpdev.h upnpcommands.h upnpreplyparse.h portlistingparse.h
+upnpc.o: miniupnpctypes.h upnperrors.h miniupnpcstrings.h
+upnpcommands.o: upnpcommands.h upnpreplyparse.h portlistingparse.h
+upnpcommands.o: miniupnpc_declspec.h miniupnpctypes.h miniupnpc.h
+upnpcommands.o: igd_desc_parse.h upnpdev.h
+upnpreplyparse.o: upnpreplyparse.h minixml.h
+testminixml.o: minixml.h igd_desc_parse.h
+minixmlvalid.o: minixml.h
+testupnpreplyparse.o: upnpreplyparse.h
+minissdpc.o: minissdpc.h miniupnpc_declspec.h upnpdev.h miniupnpc.h
+minissdpc.o: igd_desc_parse.h receivedata.h codelength.h
+upnperrors.o: upnperrors.h miniupnpc_declspec.h upnpcommands.h
+upnperrors.o: upnpreplyparse.h portlistingparse.h miniupnpctypes.h
+upnperrors.o: miniupnpc.h igd_desc_parse.h upnpdev.h
+testigddescparse.o: igd_desc_parse.h minixml.h miniupnpc.h
+testigddescparse.o: miniupnpc_declspec.h upnpdev.h
+testminiwget.o: miniwget.h miniupnpc_declspec.h
+connecthostport.o: connecthostport.h
+portlistingparse.o: portlistingparse.h miniupnpc_declspec.h miniupnpctypes.h
+portlistingparse.o: minixml.h
+receivedata.o: receivedata.h
+upnpdev.o: upnpdev.h miniupnpc_declspec.h
+testportlistingparse.o: portlistingparse.h miniupnpc_declspec.h
+testportlistingparse.o: miniupnpctypes.h
+miniupnpcmodule.o: miniupnpc.h miniupnpc_declspec.h igd_desc_parse.h
+miniupnpcmodule.o: upnpdev.h upnpcommands.h upnpreplyparse.h
+miniupnpcmodule.o: portlistingparse.h miniupnpctypes.h upnperrors.h
+listdevices.o: miniupnpc.h miniupnpc_declspec.h igd_desc_parse.h upnpdev.h
diff --git a/ext/miniupnpc/Makefile.mingw b/ext/miniupnpc/Makefile.mingw
new file mode 100644
index 00000000..6de325fe
--- /dev/null
+++ b/ext/miniupnpc/Makefile.mingw
@@ -0,0 +1,98 @@
+# $Id: Makefile.mingw,v 1.22 2015/10/26 16:59:54 nanard Exp $
+# Miniupnp project.
+# http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+# (c) 2005-2015 Thomas Bernard
+# This Makefile is made for MinGW
+#
+CC ?= gcc
+#CFLAGS = -Wall -g -DDEBUG -D_WIN32_WINNT=0X501
+CFLAGS = -Wall -Os -DNDEBUG -D_WIN32_WINNT=0X501
+LDLIBS = -lws2_32 -liphlpapi
+# -lwsock32
+# -liphlpapi is needed for GetBestRoute() and GetIpAddrTable()
+PYTHON=\utils\python25\python
+OBJS=miniwget.o minixml.o igd_desc_parse.o minisoap.o \
+ minissdpc.o \
+ miniupnpc.o upnpreplyparse.o upnpcommands.o upnperrors.o \
+ connecthostport.o portlistingparse.o receivedata.o \
+ upnpdev.o
+OBJSDLL=$(addprefix dll/, $(OBJS))
+
+all: init upnpc-static upnpc-shared testminixml libminiupnpc.a miniupnpc.dll
+
+init:
+ mkdir dll
+ echo init > init
+
+clean:
+ del upnpc testminixml *.o
+ del dll\*.o
+ del *.exe
+ del miniupnpc.dll
+ del libminiupnpc.a
+
+libminiupnpc.a: $(OBJS)
+ $(AR) cr $@ $?
+
+pythonmodule: libminiupnpc.a
+ $(PYTHON) setupmingw32.py build --compiler=mingw32
+ $(PYTHON) setupmingw32.py install --skip-build
+
+miniupnpc.dll: libminiupnpc.a $(OBJSDLL)
+ dllwrap -k --driver-name gcc \
+ --def miniupnpc.def \
+ --output-def miniupnpc.dll.def \
+ --implib miniupnpc.lib -o $@ \
+ $(OBJSDLL) $(LDLIBS)
+
+miniupnpc.lib: miniupnpc.dll
+# echo $@ generated with $<
+
+dll/upnpc.o: upnpc.o
+# echo $@ generated with $<
+
+.c.o:
+ $(CC) $(CFLAGS) -DMINIUPNP_STATICLIB -c -o $@ $<
+ $(CC) $(CFLAGS) -DMINIUPNP_EXPORTS -c -o dll/$@ $<
+
+upnpc.o: upnpc.c
+ $(CC) $(CFLAGS) -DMINIUPNP_STATICLIB -c -o $@ $<
+ $(CC) $(CFLAGS) -c -o dll/$@ $<
+
+# --enable-stdcall-fixup
+upnpc-static: upnpc.o libminiupnpc.a
+ $(CC) -o $@ $^ $(LDLIBS)
+
+upnpc-shared: dll/upnpc.o miniupnpc.lib
+ $(CC) -o $@ $^ $(LDLIBS)
+
+wingenminiupnpcstrings: wingenminiupnpcstrings.o
+
+wingenminiupnpcstrings.o: wingenminiupnpcstrings.c
+
+miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings
+ wingenminiupnpcstrings $< $@
+
+minixml.o: minixml.c minixml.h
+
+upnpc.o: miniwget.h minisoap.h miniupnpc.h igd_desc_parse.h
+upnpc.o: upnpreplyparse.h upnpcommands.h upnperrors.h miniupnpcstrings.h
+
+miniwget.o: miniwget.c miniwget.h miniupnpcstrings.h connecthostport.h
+
+minisoap.o: minisoap.c minisoap.h miniupnpcstrings.h
+
+miniupnpc.o: miniupnpc.c miniupnpc.h minisoap.h miniwget.h minixml.h
+
+igd_desc_parse.o: igd_desc_parse.c igd_desc_parse.h
+
+testminixml: minixml.o igd_desc_parse.o testminixml.c
+
+upnpreplyparse.o: upnpreplyparse.c upnpreplyparse.h minixml.h
+
+upnpcommands.o: upnpcommands.c upnpcommands.h upnpreplyparse.h miniupnpc.h portlistingparse.h
+
+minissdpc.o: minissdpc.c minissdpc.h receivedata.h
+
+upnpdev.o: upnpdev.c upnpdev.h
+
diff --git a/ext/miniupnpc/README b/ext/miniupnpc/README
new file mode 100644
index 00000000..ab08de94
--- /dev/null
+++ b/ext/miniupnpc/README
@@ -0,0 +1,61 @@
+Project: miniupnp
+Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+github: https://github.com/miniupnp/miniupnp
+freecode: http://freecode.com/projects/miniupnp
+Author: Thomas Bernard
+Copyright (c) 2005-2014 Thomas Bernard
+This software is subject to the conditions detailed in the
+LICENSE file provided within this distribution.
+
+
+* miniUPnP Client - miniUPnPc *
+
+To compile, simply run 'gmake' (could be 'make' on your system).
+Under win32, to compile with MinGW, type "mingw32make.bat".
+MS Visual C solution and project files are supplied in the msvc/ subdirectory.
+
+The compilation is known to work under linux, FreeBSD,
+OpenBSD, MacOS X, AmigaOS and cygwin.
+The official AmigaOS4.1 SDK was used for AmigaOS4 and GeekGadgets for AmigaOS3.
+upx (http://upx.sourceforge.net) is used to compress the win32 .exe files.
+
+To install the library and headers on the system use :
+> su
+> make install
+> exit
+
+alternatively, to install into a specific location, use :
+> INSTALLPREFIX=/usr/local make install
+
+upnpc.c is a sample client using the libminiupnpc.
+To use the libminiupnpc in your application, link it with
+libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h,
+upnpcommands.h and miniwget.h :
+- upnpDiscover()
+- miniwget()
+- parserootdesc()
+- GetUPNPUrls()
+- UPNP_* (calling UPNP methods)
+
+Note : use #include <miniupnpc/miniupnpc.h> etc... for the includes
+and -lminiupnpc for the link
+
+Discovery process is speeded up when MiniSSDPd is running on the machine.
+
+
+* Python module *
+
+you can build a python module with 'make pythonmodule'
+and install it with 'make installpythonmodule'.
+setup.py (and setupmingw32.py) are included in the distribution.
+
+
+Feel free to contact me if you have any problem :
+e-mail : miniupnp@free.fr
+
+If you are using libminiupnpc in your application, please
+send me an email !
+
+For any question, you can use the web forum :
+http://miniupnp.tuxfamily.org/forum/
+
diff --git a/ext/miniupnpc/VERSION b/ext/miniupnpc/VERSION
new file mode 100644
index 00000000..2e0e38c6
--- /dev/null
+++ b/ext/miniupnpc/VERSION
@@ -0,0 +1 @@
+1.9
diff --git a/ext/miniupnpc/apiversions.txt b/ext/miniupnpc/apiversions.txt
new file mode 100644
index 00000000..3e9ebef2
--- /dev/null
+++ b/ext/miniupnpc/apiversions.txt
@@ -0,0 +1,167 @@
+$Id: apiversions.txt,v 1.8 2015/10/08 16:15:47 nanard Exp $
+
+Differences in API between miniUPnPc versions
+
+API version 15
+ changed "sameport" argument of upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
+ to "localport". When 0 or 1, behaviour is not changed, but it can take
+ any other value between 2 and 65535
+ Existing programs should be compatible
+ updated macro :
+ #define MINIUPNPC_API_VERSION 15
+
+API version 14
+miniupnpc.h
+ add ttl argument to upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
+ upnpDiscoverDevices()
+ getDevicesFromMiniSSDPD() :
+ connectToMiniSSDPD() / disconnectFromMiniSSDPD()
+ requestDevicesFromMiniSSDPD() / receiveDevicesFromMiniSSDPD()
+ updated macro :
+ #define MINIUPNPC_API_VERSION 14
+
+API version 13
+miniupnpc.h:
+ add searchalltype param to upnpDiscoverDevices() function
+ updated macro :
+ #define MINIUPNPC_API_VERSION 13
+
+API version 12
+miniupnpc.h :
+ add upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices()
+ functions
+ updated macros :
+ #define MINIUPNPC_API_VERSION 12
+
+API version 11
+
+upnpreplyparse.h / portlistingparse.h :
+ removed usage of sys/queue.h / bsdqueue.h
+
+miniupnpc.h:
+ updated macros :
+ #define MINIUPNPC_API_VERSION 11
+
+====================== miniUPnPc version 1.9 ======================
+API version 10
+
+upnpcommands.h:
+ added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
+
+miniupnpc.h:
+ updated macros :
+ #define MINIUPNPC_VERSION "1.9"
+ #define MINIUPNPC_API_VERSION 10
+
+====================== miniUPnPc version 1.8 ======================
+API version 9
+
+miniupnpc.h:
+ updated macros :
+ #define MINIUPNPC_VERSION "1.8"
+ #define MINIUPNPC_API_VERSION 9
+ added "unsigned int scope_id;" to struct UPNPDev
+ added scope_id argument to GetUPNPUrls()
+
+
+
+====================== miniUPnPc version 1.7 ======================
+API version 8
+
+miniupnpc.h :
+ add new macros :
+ #define MINIUPNPC_VERSION "1.7"
+ #define MINIUPNPC_API_VERSION 8
+ add rootdescURL to struct UPNPUrls
+
+
+
+====================== miniUPnPc version 1.6 ======================
+API version 8
+
+Adding support for IPv6.
+igd_desc_parse.h :
+ struct IGDdatas_service :
+ add char presentationurl[MINIUPNPC_URL_MAXSIZE];
+ struct IGDdatas :
+ add struct IGDdatas_service IPv6FC;
+miniupnpc.h :
+ new macros :
+ #define UPNPDISCOVER_SUCCESS (0)
+ #define UPNPDISCOVER_UNKNOWN_ERROR (-1)
+ #define UPNPDISCOVER_SOCKET_ERROR (-101)
+ #define UPNPDISCOVER_MEMORY_ERROR (-102)
+ simpleUPnPcommand() prototype changed (but is normaly not used by API users)
+ add arguments ipv6 and error to upnpDiscover() :
+ struct UPNPDev *
+ upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int sameport,
+ int ipv6,
+ int * error);
+ add controlURL_6FC member to struct UPNPUrls :
+ struct UPNPUrls {
+ char * controlURL;
+ char * ipcondescURL;
+ char * controlURL_CIF;
+ char * controlURL_6FC;
+ };
+
+upnpcommands.h :
+ add leaseDuration argument to UPNP_AddPortMapping()
+ add desc, enabled and leaseDuration arguments to UPNP_GetSpecificPortMappingEntry()
+ add UPNP_GetListOfPortMappings() function (IGDv2)
+ add IGDv2 IPv6 related functions :
+ UPNP_GetFirewallStatus()
+ UPNP_GetOutboundPinholeTimeout()
+ UPNP_AddPinhole()
+ UPNP_UpdatePinhole()
+ UPNP_DeletePinhole()
+ UPNP_CheckPinholeWorking()
+ UPNP_GetPinholePackets()
+
+
+
+====================== miniUPnPc version 1.5 ======================
+API version 5
+
+new function :
+int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
+new macro in upnpcommands.h :
+#define UPNPCOMMAND_HTTP_ERROR
+
+====================== miniUPnPc version 1.4 ======================
+Same API as version 1.3
+
+====================== miniUPnPc version 1.3 ======================
+API version 4
+
+Use UNSIGNED_INTEGER type for
+UPNP_GetTotalBytesSent(), UPNP_GetTotalBytesReceived(),
+UPNP_GetTotalPacketsSent(), UPNP_GetTotalPacketsReceived()
+Add remoteHost argument to UPNP_AddPortMapping() and UPNP_DeletePortMapping()
+
+====================== miniUPnPc version 1.2 ======================
+API version 3
+
+added sameport argument to upnpDiscover()
+struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int sameport);
+
+====================== miniUPnPc Version 1.1 ======================
+Same API as 1.0
+
+
+====================== miniUPnPc Version 1.0 ======================
+API version 2
+
+
+struct UPNPDev {
+ struct UPNPDev * pNext;
+ char * descURL;
+ char * st;
+ char buffer[2];
+};
+struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock);
+
diff --git a/ext/miniupnpc/codelength.h b/ext/miniupnpc/codelength.h
new file mode 100644
index 00000000..f5f8e30f
--- /dev/null
+++ b/ext/miniupnpc/codelength.h
@@ -0,0 +1,54 @@
+/* $Id: codelength.h,v 1.5 2015/07/09 12:40:18 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef CODELENGTH_H_INCLUDED
+#define CODELENGTH_H_INCLUDED
+
+/* Encode length by using 7bit per Byte :
+ * Most significant bit of each byte specifies that the
+ * following byte is part of the code */
+
+/* n : unsigned
+ * p : unsigned char *
+ */
+#define DECODELENGTH(n, p) n = 0; \
+ do { n = (n << 7) | (*p & 0x7f); } \
+ while((*(p++)&0x80) && (n<(1<<25)));
+
+/* n : unsigned
+ * READ : function/macro to read one byte (unsigned char)
+ */
+#define DECODELENGTH_READ(n, READ) \
+ n = 0; \
+ do { \
+ unsigned char c; \
+ READ(c); \
+ n = (n << 7) | (c & 0x07f); \
+ if(!(c&0x80)) break; \
+ } while(n<(1<<25));
+
+/* n : unsigned
+ * p : unsigned char *
+ * p_limit : unsigned char *
+ */
+#define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \
+ n = 0; \
+ do { \
+ if((p) >= (p_limit)) break; \
+ n = (n << 7) | (*(p) & 0x7f); \
+ } while((*((p)++)&0x80) && (n<(1<<25)));
+
+
+/* n : unsigned
+ * p : unsigned char *
+ */
+#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
+ if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
+ if(n>=16384) *(p++) = (n >> 14) | 0x80; \
+ if(n>=128) *(p++) = (n >> 7) | 0x80; \
+ *(p++) = n & 0x7f;
+
+#endif /* CODELENGTH_H_INCLUDED */
diff --git a/ext/miniupnpc/connecthostport.c b/ext/miniupnpc/connecthostport.c
new file mode 100644
index 00000000..1f2a032e
--- /dev/null
+++ b/ext/miniupnpc/connecthostport.c
@@ -0,0 +1,264 @@
+/* $Id: connecthostport.c,v 1.15 2015/10/09 16:26:19 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2010-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+/* use getaddrinfo() or gethostbyname()
+ * uncomment the following line in order to use gethostbyname() */
+#ifdef NO_GETADDRINFO
+#define USE_GETHOSTBYNAME
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define MAXHOSTNAMELEN 64
+#define snprintf _snprintf
+#define herror
+#define socklen_t int
+#else /* #ifdef _WIN32 */
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+#include <sys/time.h>
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+#include <sys/param.h>
+#include <sys/select.h>
+#include <errno.h>
+#define closesocket close
+#include <netdb.h>
+#include <netinet/in.h>
+/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
+ * during the connect() call */
+#define MINIUPNPC_IGNORE_EINTR
+#ifndef USE_GETHOSTBYNAME
+#include <sys/socket.h>
+#include <sys/select.h>
+#endif /* #ifndef USE_GETHOSTBYNAME */
+#endif /* #else _WIN32 */
+
+/* definition of PRINT_SOCKET_ERROR */
+#ifdef _WIN32
+#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#if defined(__amigaos__) || defined(__amigaos4__)
+#define herror(A) printf("%s\n", A)
+#endif
+
+#include "connecthostport.h"
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+/* connecthostport()
+ * return a socket connected (TCP) to the host and port
+ * or -1 in case of error */
+int connecthostport(const char * host, unsigned short port,
+ unsigned int scope_id)
+{
+ int s, n;
+#ifdef USE_GETHOSTBYNAME
+ struct sockaddr_in dest;
+ struct hostent *hp;
+#else /* #ifdef USE_GETHOSTBYNAME */
+ char tmp_host[MAXHOSTNAMELEN+1];
+ char port_str[8];
+ struct addrinfo *ai, *p;
+ struct addrinfo hints;
+#endif /* #ifdef USE_GETHOSTBYNAME */
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ struct timeval timeout;
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+
+#ifdef USE_GETHOSTBYNAME
+ hp = gethostbyname(host);
+ if(hp == NULL)
+ {
+ herror(host);
+ return -1;
+ }
+ memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
+ memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if(s < 0)
+ {
+ PRINT_SOCKET_ERROR("socket");
+ return -1;
+ }
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ /* setting a 3 seconds timeout for the connect() call */
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+ dest.sin_family = AF_INET;
+ dest.sin_port = htons(port);
+ n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
+#ifdef MINIUPNPC_IGNORE_EINTR
+ /* EINTR The system call was interrupted by a signal that was caught
+ * EINPROGRESS The socket is nonblocking and the connection cannot
+ * be completed immediately. */
+ while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
+ {
+ socklen_t len;
+ fd_set wset;
+ int err;
+ FD_ZERO(&wset);
+ FD_SET(s, &wset);
+ if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
+ continue;
+ /*len = 0;*/
+ /*n = getpeername(s, NULL, &len);*/
+ len = sizeof(err);
+ if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+ PRINT_SOCKET_ERROR("getsockopt");
+ closesocket(s);
+ return -1;
+ }
+ if(err != 0) {
+ errno = err;
+ n = -1;
+ }
+ }
+#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
+ if(n<0)
+ {
+ PRINT_SOCKET_ERROR("connect");
+ closesocket(s);
+ return -1;
+ }
+#else /* #ifdef USE_GETHOSTBYNAME */
+ /* use getaddrinfo() instead of gethostbyname() */
+ memset(&hints, 0, sizeof(hints));
+ /* hints.ai_flags = AI_ADDRCONFIG; */
+#ifdef AI_NUMERICSERV
+ hints.ai_flags = AI_NUMERICSERV;
+#endif
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
+ /* hints.ai_protocol = IPPROTO_TCP; */
+ snprintf(port_str, sizeof(port_str), "%hu", port);
+ if(host[0] == '[')
+ {
+ /* literal ip v6 address */
+ int i, j;
+ for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++)
+ {
+ tmp_host[i] = host[j];
+ if(0 == memcmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */
+ j+=2; /* skip "25" */
+ }
+ tmp_host[i] = '\0';
+ }
+ else
+ {
+ strncpy(tmp_host, host, MAXHOSTNAMELEN);
+ }
+ tmp_host[MAXHOSTNAMELEN] = '\0';
+ n = getaddrinfo(tmp_host, port_str, &hints, &ai);
+ if(n != 0)
+ {
+#ifdef _WIN32
+ fprintf(stderr, "getaddrinfo() error : %d\n", n);
+#else
+ fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
+#endif
+ return -1;
+ }
+ s = -1;
+ for(p = ai; p; p = p->ai_next)
+ {
+ s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if(s < 0)
+ continue;
+ if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) {
+ struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr;
+ addr6->sin6_scope_id = scope_id;
+ }
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ /* setting a 3 seconds timeout for the connect() call */
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+ n = connect(s, p->ai_addr, p->ai_addrlen);
+#ifdef MINIUPNPC_IGNORE_EINTR
+ /* EINTR The system call was interrupted by a signal that was caught
+ * EINPROGRESS The socket is nonblocking and the connection cannot
+ * be completed immediately. */
+ while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
+ {
+ socklen_t len;
+ fd_set wset;
+ int err;
+ FD_ZERO(&wset);
+ FD_SET(s, &wset);
+ if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
+ continue;
+ /*len = 0;*/
+ /*n = getpeername(s, NULL, &len);*/
+ len = sizeof(err);
+ if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+ PRINT_SOCKET_ERROR("getsockopt");
+ closesocket(s);
+ freeaddrinfo(ai);
+ return -1;
+ }
+ if(err != 0) {
+ errno = err;
+ n = -1;
+ }
+ }
+#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
+ if(n < 0)
+ {
+ closesocket(s);
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+ freeaddrinfo(ai);
+ if(s < 0)
+ {
+ PRINT_SOCKET_ERROR("socket");
+ return -1;
+ }
+ if(n < 0)
+ {
+ PRINT_SOCKET_ERROR("connect");
+ return -1;
+ }
+#endif /* #ifdef USE_GETHOSTBYNAME */
+ return s;
+}
+
diff --git a/ext/miniupnpc/connecthostport.h b/ext/miniupnpc/connecthostport.h
new file mode 100644
index 00000000..56941d6f
--- /dev/null
+++ b/ext/miniupnpc/connecthostport.h
@@ -0,0 +1,18 @@
+/* $Id: connecthostport.h,v 1.3 2012/09/27 15:42:10 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2010-2012 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef CONNECTHOSTPORT_H_INCLUDED
+#define CONNECTHOSTPORT_H_INCLUDED
+
+/* connecthostport()
+ * return a socket connected (TCP) to the host and port
+ * or -1 in case of error */
+int connecthostport(const char * host, unsigned short port,
+ unsigned int scope_id);
+
+#endif
+
diff --git a/ext/miniupnpc/external-ip.sh b/ext/miniupnpc/external-ip.sh
new file mode 100755
index 00000000..965d86b2
--- /dev/null
+++ b/ext/miniupnpc/external-ip.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+# $Id: external-ip.sh,v 1.1 2010/08/05 12:57:41 nanard Exp $
+# (c) 2010 Reuben Hawkins
+upnpc -s | grep ExternalIPAddress | sed 's/[^0-9\.]//g'
diff --git a/ext/miniupnpc/igd_desc_parse.c b/ext/miniupnpc/igd_desc_parse.c
new file mode 100644
index 00000000..d2999ad0
--- /dev/null
+++ b/ext/miniupnpc/igd_desc_parse.c
@@ -0,0 +1,123 @@
+/* $Id: igd_desc_parse.c,v 1.17 2015/09/15 13:30:04 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include "igd_desc_parse.h"
+#include <stdio.h>
+#include <string.h>
+
+/* Start element handler :
+ * update nesting level counter and copy element name */
+void IGDstartelt(void * d, const char * name, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ if(l >= MINIUPNPC_URL_MAXSIZE)
+ l = MINIUPNPC_URL_MAXSIZE-1;
+ memcpy(datas->cureltname, name, l);
+ datas->cureltname[l] = '\0';
+ datas->level++;
+ if( (l==7) && !memcmp(name, "service", l) ) {
+ datas->tmp.controlurl[0] = '\0';
+ datas->tmp.eventsuburl[0] = '\0';
+ datas->tmp.scpdurl[0] = '\0';
+ datas->tmp.servicetype[0] = '\0';
+ }
+}
+
+#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
+
+/* End element handler :
+ * update nesting level counter and update parser state if
+ * service element is parsed */
+void IGDendelt(void * d, const char * name, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ datas->level--;
+ /*printf("endelt %2d %.*s\n", datas->level, l, name);*/
+ if( (l==7) && !memcmp(name, "service", l) )
+ {
+ if(COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) {
+ memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else if(COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANIPv6FirewallControl:")) {
+ memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else if(COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANIPConnection:")
+ || COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANPPPConnection:") ) {
+ if(datas->first.servicetype[0] == '\0') {
+ memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else {
+ memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
+ }
+ }
+ }
+}
+
+/* Data handler :
+ * copy data depending on the current element name and state */
+void IGDdata(void * d, const char * data, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ char * dstmember = 0;
+ /*printf("%2d %s : %.*s\n",
+ datas->level, datas->cureltname, l, data); */
+ if( !strcmp(datas->cureltname, "URLBase") )
+ dstmember = datas->urlbase;
+ else if( !strcmp(datas->cureltname, "presentationURL") )
+ dstmember = datas->presentationurl;
+ else if( !strcmp(datas->cureltname, "serviceType") )
+ dstmember = datas->tmp.servicetype;
+ else if( !strcmp(datas->cureltname, "controlURL") )
+ dstmember = datas->tmp.controlurl;
+ else if( !strcmp(datas->cureltname, "eventSubURL") )
+ dstmember = datas->tmp.eventsuburl;
+ else if( !strcmp(datas->cureltname, "SCPDURL") )
+ dstmember = datas->tmp.scpdurl;
+/* else if( !strcmp(datas->cureltname, "deviceType") )
+ dstmember = datas->devicetype_tmp;*/
+ if(dstmember)
+ {
+ if(l>=MINIUPNPC_URL_MAXSIZE)
+ l = MINIUPNPC_URL_MAXSIZE-1;
+ memcpy(dstmember, data, l);
+ dstmember[l] = '\0';
+ }
+}
+
+#ifdef DEBUG
+void printIGD(struct IGDdatas * d)
+{
+ printf("urlbase = '%s'\n", d->urlbase);
+ printf("WAN Device (Common interface config) :\n");
+ /*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/
+ printf(" serviceType = '%s'\n", d->CIF.servicetype);
+ printf(" controlURL = '%s'\n", d->CIF.controlurl);
+ printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
+ printf("primary WAN Connection Device (IP or PPP Connection):\n");
+ /*printf(" deviceType = '%s'\n", d->first.devicetype);*/
+ printf(" servicetype = '%s'\n", d->first.servicetype);
+ printf(" controlURL = '%s'\n", d->first.controlurl);
+ printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->first.scpdurl);
+ printf("secondary WAN Connection Device (IP or PPP Connection):\n");
+ /*printf(" deviceType = '%s'\n", d->second.devicetype);*/
+ printf(" servicetype = '%s'\n", d->second.servicetype);
+ printf(" controlURL = '%s'\n", d->second.controlurl);
+ printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->second.scpdurl);
+ printf("WAN IPv6 Firewall Control :\n");
+ /*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
+ printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
+ printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
+ printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
+}
+#endif /* DEBUG */
+
diff --git a/ext/miniupnpc/igd_desc_parse.h b/ext/miniupnpc/igd_desc_parse.h
new file mode 100644
index 00000000..0de546b6
--- /dev/null
+++ b/ext/miniupnpc/igd_desc_parse.h
@@ -0,0 +1,49 @@
+/* $Id: igd_desc_parse.h,v 1.12 2014/11/17 17:19:13 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2014 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef IGD_DESC_PARSE_H_INCLUDED
+#define IGD_DESC_PARSE_H_INCLUDED
+
+/* Structure to store the result of the parsing of UPnP
+ * descriptions of Internet Gateway Devices */
+#define MINIUPNPC_URL_MAXSIZE (128)
+struct IGDdatas_service {
+ char controlurl[MINIUPNPC_URL_MAXSIZE];
+ char eventsuburl[MINIUPNPC_URL_MAXSIZE];
+ char scpdurl[MINIUPNPC_URL_MAXSIZE];
+ char servicetype[MINIUPNPC_URL_MAXSIZE];
+ /*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
+};
+
+struct IGDdatas {
+ char cureltname[MINIUPNPC_URL_MAXSIZE];
+ char urlbase[MINIUPNPC_URL_MAXSIZE];
+ char presentationurl[MINIUPNPC_URL_MAXSIZE];
+ int level;
+ /*int state;*/
+ /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
+ struct IGDdatas_service CIF;
+ /* "urn:schemas-upnp-org:service:WANIPConnection:1"
+ * "urn:schemas-upnp-org:service:WANPPPConnection:1" */
+ struct IGDdatas_service first;
+ /* if both WANIPConnection and WANPPPConnection are present */
+ struct IGDdatas_service second;
+ /* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
+ struct IGDdatas_service IPv6FC;
+ /* tmp */
+ struct IGDdatas_service tmp;
+};
+
+void IGDstartelt(void *, const char *, int);
+void IGDendelt(void *, const char *, int);
+void IGDdata(void *, const char *, int);
+#ifdef DEBUG
+void printIGD(struct IGDdatas *);
+#endif /* DEBUG */
+
+#endif /* IGD_DESC_PARSE_H_INCLUDED */
diff --git a/ext/miniupnpc/java/JavaBridgeTest.java b/ext/miniupnpc/java/JavaBridgeTest.java
new file mode 100644
index 00000000..c658c599
--- /dev/null
+++ b/ext/miniupnpc/java/JavaBridgeTest.java
@@ -0,0 +1,97 @@
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+
+import fr.free.miniupnp.*;
+
+/**
+ *
+ * @author syuu
+ */
+public class JavaBridgeTest {
+ public static void main(String[] args) {
+ int UPNP_DELAY = 2000;
+ MiniupnpcLibrary miniupnpc = MiniupnpcLibrary.INSTANCE;
+ UPNPDev devlist = null;
+ UPNPUrls urls = new UPNPUrls();
+ IGDdatas data = new IGDdatas();
+ ByteBuffer lanaddr = ByteBuffer.allocate(16);
+ ByteBuffer intClient = ByteBuffer.allocate(16);
+ ByteBuffer intPort = ByteBuffer.allocate(6);
+ ByteBuffer desc = ByteBuffer.allocate(80);
+ ByteBuffer enabled = ByteBuffer.allocate(4);
+ ByteBuffer leaseDuration = ByteBuffer.allocate(16);
+ int ret;
+ int i;
+
+ if(args.length < 2) {
+ System.err.println("Usage : java [...] JavaBridgeTest port protocol");
+ System.out.println(" port is numeric, protocol is TCP or UDP");
+ return;
+ }
+
+ devlist = miniupnpc.upnpDiscover(UPNP_DELAY, (String) null, (String) null, 0, 0, (byte)2, IntBuffer.allocate(1));
+ if (devlist != null) {
+ System.out.println("List of UPNP devices found on the network :");
+ for (UPNPDev device = devlist; device != null; device = device.pNext) {
+ System.out.println("desc: " + device.descURL.getString(0) + " st: " + device.st.getString(0));
+ }
+ if ((i = miniupnpc.UPNP_GetValidIGD(devlist, urls, data, lanaddr, 16)) != 0) {
+ switch (i) {
+ case 1:
+ System.out.println("Found valid IGD : " + urls.controlURL.getString(0));
+ break;
+ case 2:
+ System.out.println("Found a (not connected?) IGD : " + urls.controlURL.getString(0));
+ System.out.println("Trying to continue anyway");
+ break;
+ case 3:
+ System.out.println("UPnP device found. Is it an IGD ? : " + urls.controlURL.getString(0));
+ System.out.println("Trying to continue anyway");
+ break;
+ default:
+ System.out.println("Found device (igd ?) : " + urls.controlURL.getString(0));
+ System.out.println("Trying to continue anyway");
+
+ }
+ System.out.println("Local LAN ip address : " + new String(lanaddr.array()));
+ ByteBuffer externalAddress = ByteBuffer.allocate(16);
+ miniupnpc.UPNP_GetExternalIPAddress(urls.controlURL.getString(0),
+ new String(data.first.servicetype), externalAddress);
+ System.out.println("ExternalIPAddress = " + new String(externalAddress.array()));
+ ret = miniupnpc.UPNP_AddPortMapping(
+ urls.controlURL.getString(0), // controlURL
+ new String(data.first.servicetype), // servicetype
+ args[0], // external Port
+ args[0], // internal Port
+ new String(lanaddr.array()), // internal client
+ "added via miniupnpc/JAVA !", // description
+ args[1], // protocol UDP or TCP
+ null, // remote host (useless)
+ "0"); // leaseDuration
+ if (ret != MiniupnpcLibrary.UPNPCOMMAND_SUCCESS)
+ System.out.println("AddPortMapping() failed with code " + ret);
+ ret = miniupnpc.UPNP_GetSpecificPortMappingEntry(
+ urls.controlURL.getString(0), new String(data.first.servicetype),
+ args[0], args[1], null, intClient, intPort,
+ desc, enabled, leaseDuration);
+ if (ret != MiniupnpcLibrary.UPNPCOMMAND_SUCCESS)
+ System.out.println("GetSpecificPortMappingEntry() failed with code " + ret);
+ System.out.println("InternalIP:Port = " +
+ new String(intClient.array()) + ":" + new String(intPort.array()) +
+ " (" + new String(desc.array()) + ")");
+ ret = miniupnpc.UPNP_DeletePortMapping(
+ urls.controlURL.getString(0),
+ new String(data.first.servicetype),
+ args[0], args[1], null);
+ if (ret != MiniupnpcLibrary.UPNPCOMMAND_SUCCESS)
+ System.out.println("DelPortMapping() failed with code " + ret);
+ miniupnpc.FreeUPNPUrls(urls);
+ } else {
+ System.out.println("No valid UPNP Internet Gateway Device found.");
+ }
+ miniupnpc.freeUPNPDevlist(devlist);
+ } else {
+ System.out.println("No IGD UPnP Device found on the network !\n");
+ }
+ }
+}
diff --git a/ext/miniupnpc/java/testjava.bat b/ext/miniupnpc/java/testjava.bat
new file mode 100755
index 00000000..b836da14
--- /dev/null
+++ b/ext/miniupnpc/java/testjava.bat
@@ -0,0 +1,8 @@
+@echo off
+set JAVA=java
+set JAVAC=javac
+REM notice the semicolon for Windows. Write once, run ... oh nevermind
+set CP=miniupnpc_win32.jar;.
+
+%JAVAC% -cp "%CP%" JavaBridgeTest.java || exit 1
+%JAVA% -cp "%CP%" JavaBridgeTest 12345 UDP || exit 1
diff --git a/ext/miniupnpc/java/testjava.sh b/ext/miniupnpc/java/testjava.sh
new file mode 100755
index 00000000..9880523a
--- /dev/null
+++ b/ext/miniupnpc/java/testjava.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+JAVA=java
+JAVAC=javac
+CP=$(for i in *.jar; do echo -n $i:; done).
+
+$JAVAC -cp $CP JavaBridgeTest.java || exit 1
+$JAVA -cp $CP JavaBridgeTest 12345 UDP || exit 1
diff --git a/ext/miniupnpc/listdevices.c b/ext/miniupnpc/listdevices.c
new file mode 100644
index 00000000..a93c29ff
--- /dev/null
+++ b/ext/miniupnpc/listdevices.c
@@ -0,0 +1,110 @@
+/* $Id: listdevices.c,v 1.7 2015/10/08 16:15:47 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2013-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#endif /* _WIN32 */
+#include "miniupnpc.h"
+
+int main(int argc, char * * argv)
+{
+ const char * searched_device = NULL;
+ const char * * searched_devices = NULL;
+ const char * multicastif = 0;
+ const char * minissdpdpath = 0;
+ int ipv6 = 0;
+ unsigned char ttl = 2;
+ int error = 0;
+ struct UPNPDev * devlist = 0;
+ struct UPNPDev * dev;
+ int i;
+
+#ifdef _WIN32
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+ if(nResult != NO_ERROR)
+ {
+ fprintf(stderr, "WSAStartup() failed.\n");
+ return -1;
+ }
+#endif
+
+ for(i = 1; i < argc; i++) {
+ if(strcmp(argv[i], "-6") == 0)
+ ipv6 = 1;
+ else if(strcmp(argv[i], "-d") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "%s option needs one argument\n", "-d");
+ return 1;
+ }
+ searched_device = argv[i];
+ } else if(strcmp(argv[i], "-t") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "%s option needs one argument\n", "-t");
+ return 1;
+ }
+ ttl = (unsigned char)atoi(argv[i]);
+ } else if(strcmp(argv[i], "-l") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "-l option needs at least one argument\n");
+ return 1;
+ }
+ searched_devices = (const char * *)(argv + i);
+ break;
+ } else if(strcmp(argv[i], "-m") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "-m option needs one argument\n");
+ return 1;
+ }
+ multicastif = argv[i];
+ } else {
+ printf("usage : %s [options] [-l <device1> <device2> ...]\n", argv[0]);
+ printf("options :\n");
+ printf(" -6 : use IPv6\n");
+ printf(" -m address/ifname : network interface to use for multicast\n");
+ printf(" -d <device string> : search only for this type of device\n");
+ printf(" -l <device1> <device2> ... : search only for theses types of device\n");
+ printf(" -t ttl : set multicast TTL. Default value is 2.\n");
+ printf(" -h : this help\n");
+ return 1;
+ }
+ }
+
+ if(searched_device) {
+ printf("searching UPnP device type %s\n", searched_device);
+ devlist = upnpDiscoverDevice(searched_device,
+ 2000, multicastif, minissdpdpath,
+ 0/*localport*/, ipv6, ttl, &error);
+ } else if(searched_devices) {
+ printf("searching UPnP device types :\n");
+ for(i = 0; searched_devices[i]; i++)
+ printf("\t%s\n", searched_devices[i]);
+ devlist = upnpDiscoverDevices(searched_devices,
+ 2000, multicastif, minissdpdpath,
+ 0/*localport*/, ipv6, ttl, &error, 1);
+ } else {
+ printf("searching all UPnP devices\n");
+ devlist = upnpDiscoverAll(2000, multicastif, minissdpdpath,
+ 0/*localport*/, ipv6, ttl, &error);
+ }
+ if(devlist) {
+ for(dev = devlist, i = 1; dev != NULL; dev = dev->pNext, i++) {
+ printf("%3d: %-48s\n", i, dev->st);
+ printf(" %s\n", dev->descURL);
+ printf(" %s\n", dev->usn);
+ }
+ freeUPNPDevlist(devlist);
+ } else {
+ printf("no device found.\n");
+ }
+
+ return 0;
+}
+
diff --git a/ext/miniupnpc/man3/miniupnpc.3 b/ext/miniupnpc/man3/miniupnpc.3
new file mode 100644
index 00000000..7b997d47
--- /dev/null
+++ b/ext/miniupnpc/man3/miniupnpc.3
@@ -0,0 +1,55 @@
+.TH MINIUPNPC 3
+.SH NAME
+miniupnpc \- UPnP client library
+.SH SYNOPSIS
+.SH DESCRIPTION
+The miniupnpc library implement the UPnP protocol defined
+to dialog with Internet Gateway Devices. It also has
+the ability to use data gathered by minissdpd(1) about
+UPnP devices up on the network in order to skip the
+long UPnP device discovery process.
+.PP
+At first, upnpDiscover(3) has to be used to discover UPnP IGD present
+on the network. Then UPNP_GetValidIGD(3) to select the right one.
+Alternatively, UPNP_GetIGDFromUrl(3) could be used to bypass discovery
+process if the root description url of the device to use is known.
+Then all the UPNP_* functions can be used, such as
+UPNP_GetConnectionTypeInfo(3), UPNP_AddPortMapping(3), etc...
+.SH "HEADER FILES"
+.IP miniupnpc.h
+That's the main header file for the miniupnpc library API.
+It contains all the functions and structures related to device discovery.
+.IP upnpcommands.h
+This header file contain the UPnP IGD methods that are accessible
+through the miniupnpc API. The name of the C functions are matching
+the UPnP methods names. ie: GetGenericPortMappingEntry is
+UPNP_GetGenericPortMappingEntry.
+.SH "API FUNCTIONS"
+.IP "struct UPNPDev * upnpDiscover(int delay, const char * multicastif, const char * minissdpdsock, int localport, int ipv6, int * error);"
+execute the discovery process.
+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.
+If ipv6 is not 0, IPv6 is used instead of IPv4 for the discovery process.
+.IP "void freeUPNPDevlist(struct UPNPDev * devlist);"
+free the list returned by upnpDiscover().
+.IP "int UPNP_GetValidIGD(struct UPNPDev * devlist, struct UPNPUrls * urls, struct IGDdatas * data, char * lanaddr, int lanaddrlen);"
+browse the list of device returned by upnpDiscover(), find
+a live UPnP internet gateway device and fill structures passed as arguments
+with data used for UPNP methods invokation.
+.IP "int UPNP_GetIGDFromUrl(const char * rootdescurl, struct UPNPUrls * urls, struct IGDdatas * data, char * lanaddr, int lanaddrlen);"
+permit to bypass the upnpDiscover() call if the xml root description
+URL of the UPnP IGD is known.
+Fill structures passed as arguments
+with data used for UPNP methods invokation.
+.IP "void GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, const char *);"
+.IP "void FreeUPNPUrls(struct UPNPUrls *);"
+
+.SH "SEE ALSO"
+minissdpd(1)
+.SH BUGS
diff --git a/ext/miniupnpc/mingw32make.bat b/ext/miniupnpc/mingw32make.bat
new file mode 100644
index 00000000..c5d3cc4f
--- /dev/null
+++ b/ext/miniupnpc/mingw32make.bat
@@ -0,0 +1,8 @@
+@mingw32-make -f Makefile.mingw %1
+@if errorlevel 1 goto end
+@if not exist upnpc-static.exe goto end
+@strip upnpc-static.exe
+@upx --best upnpc-static.exe
+@strip upnpc-shared.exe
+@upx --best upnpc-shared.exe
+:end
diff --git a/ext/miniupnpc/minihttptestserver.c b/ext/miniupnpc/minihttptestserver.c
new file mode 100644
index 00000000..32e3cb78
--- /dev/null
+++ b/ext/miniupnpc/minihttptestserver.c
@@ -0,0 +1,655 @@
+/* $Id: minihttptestserver.c,v 1.18 2015/07/15 12:41:15 nanard Exp $ */
+/* Project : miniUPnP
+ * Author : Thomas Bernard
+ * Copyright (c) 2011-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+
+#define CRAP_LENGTH (2048)
+
+volatile sig_atomic_t quit = 0;
+volatile sig_atomic_t child_to_wait_for = 0;
+
+/**
+ * signal handler for SIGCHLD (child status has changed)
+ */
+void handle_signal_chld(int sig)
+{
+ (void)sig;
+ /* printf("handle_signal_chld(%d)\n", sig); */
+ ++child_to_wait_for;
+}
+
+/**
+ * signal handler for SIGINT (CRTL C)
+ */
+void handle_signal_int(int sig)
+{
+ (void)sig;
+ /* printf("handle_signal_int(%d)\n", sig); */
+ quit = 1;
+}
+
+/**
+ * build a text/plain content of the specified length
+ */
+void build_content(char * p, int n)
+{
+ char line_buffer[80];
+ int k;
+ int i = 0;
+
+ while(n > 0) {
+ k = snprintf(line_buffer, sizeof(line_buffer),
+ "%04d_ABCDEFGHIJKL_This_line_is_64_bytes_long_ABCDEFGHIJKL_%04d\r\n",
+ i, i);
+ if(k != 64) {
+ fprintf(stderr, "snprintf() returned %d in build_content()\n", k);
+ }
+ ++i;
+ if(n >= 64) {
+ memcpy(p, line_buffer, 64);
+ p += 64;
+ n -= 64;
+ } else {
+ memcpy(p, line_buffer, n);
+ p += n;
+ n = 0;
+ }
+ }
+}
+
+/**
+ * build crappy content
+ */
+void build_crap(char * p, int n)
+{
+ static const char crap[] = "_CRAP_\r\n";
+ int i;
+
+ while(n > 0) {
+ i = sizeof(crap) - 1;
+ if(i > n)
+ i = n;
+ memcpy(p, crap, i);
+ p += i;
+ n -= i;
+ }
+}
+
+/**
+ * build chunked response.
+ * return a malloc'ed buffer
+ */
+char * build_chunked_response(int content_length, int * response_len)
+{
+ char * response_buffer;
+ char * content_buffer;
+ int buffer_length;
+ int i, n;
+
+ /* allocate to have some margin */
+ buffer_length = 256 + content_length + (content_length >> 4);
+ response_buffer = malloc(buffer_length);
+ if(response_buffer == NULL)
+ return NULL;
+ *response_len = snprintf(response_buffer, buffer_length,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/plain\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "\r\n");
+
+ /* build the content */
+ content_buffer = malloc(content_length);
+ if(content_buffer == NULL) {
+ free(response_buffer);
+ return NULL;
+ }
+ build_content(content_buffer, content_length);
+
+ /* chunk it */
+ i = 0;
+ while(i < content_length) {
+ n = (rand() % 199) + 1;
+ if(i + n > content_length) {
+ n = content_length - i;
+ }
+ /* TODO : check buffer size ! */
+ *response_len += snprintf(response_buffer + *response_len,
+ buffer_length - *response_len,
+ "%x\r\n", n);
+ memcpy(response_buffer + *response_len, content_buffer + i, n);
+ *response_len += n;
+ i += n;
+ response_buffer[(*response_len)++] = '\r';
+ response_buffer[(*response_len)++] = '\n';
+ }
+ /* the last chunk : "0\r\n" a empty body and then
+ * the final "\r\n" */
+ memcpy(response_buffer + *response_len, "0\r\n\r\n", 5);
+ *response_len += 5;
+ free(content_buffer);
+
+ printf("resp_length=%d buffer_length=%d content_length=%d\n",
+ *response_len, buffer_length, content_length);
+ return response_buffer;
+}
+
+/* favicon.ico generator */
+#ifdef OLD_HEADER
+#define FAVICON_LENGTH (6 + 16 + 12 + 8 + 32 * 4)
+#else
+#define FAVICON_LENGTH (6 + 16 + 40 + 8 + 32 * 4)
+#endif
+void build_favicon_content(char * p, int n)
+{
+ int i;
+ if(n < FAVICON_LENGTH)
+ return;
+ /* header : 6 bytes */
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 1; /* type : ICO */
+ *p++ = 0;
+ *p++ = 1; /* number of images in file */
+ *p++ = 0;
+ /* image directory (1 entry) : 16 bytes */
+ *p++ = 16; /* width */
+ *p++ = 16; /* height */
+ *p++ = 2; /* number of colors in the palette. 0 = no palette */
+ *p++ = 0; /* reserved */
+ *p++ = 1; /* color planes */
+ *p++ = 0; /* " */
+ *p++ = 1; /* bpp */
+ *p++ = 0; /* " */
+#ifdef OLD_HEADER
+ *p++ = 12 + 8 + 32 * 4; /* bmp size */
+#else
+ *p++ = 40 + 8 + 32 * 4; /* bmp size */
+#endif
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ *p++ = 6 + 16; /* bmp offset */
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ /* BMP */
+#ifdef OLD_HEADER
+ /* BITMAPCOREHEADER */
+ *p++ = 12; /* size of this header */
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ *p++ = 16; /* width */
+ *p++ = 0; /* " */
+ *p++ = 16 * 2; /* height x 2 ! */
+ *p++ = 0; /* " */
+ *p++ = 1; /* color planes */
+ *p++ = 0; /* " */
+ *p++ = 1; /* bpp */
+ *p++ = 0; /* " */
+#else
+ /* BITMAPINFOHEADER */
+ *p++ = 40; /* size of this header */
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ *p++ = 16; /* width */
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ *p++ = 16 * 2; /* height x 2 ! */
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ *p++ = 0; /* " */
+ *p++ = 1; /* color planes */
+ *p++ = 0; /* " */
+ *p++ = 1; /* bpp */
+ *p++ = 0; /* " */
+ /* compression method, image size, ppm x, ppm y */
+ /* colors in the palette ? */
+ /* important colors */
+ for(i = 4 * 6; i > 0; --i)
+ *p++ = 0;
+#endif
+ /* palette */
+ *p++ = 0; /* b */
+ *p++ = 0; /* g */
+ *p++ = 0; /* r */
+ *p++ = 0; /* reserved */
+ *p++ = 255; /* b */
+ *p++ = 255; /* g */
+ *p++ = 255; /* r */
+ *p++ = 0; /* reserved */
+ /* pixel data */
+ for(i = 16; i > 0; --i) {
+ if(i & 1) {
+ *p++ = 0125;
+ *p++ = 0125;
+ } else {
+ *p++ = 0252;
+ *p++ = 0252;
+ }
+ *p++ = 0;
+ *p++ = 0;
+ }
+ /* Opacity MASK */
+ for(i = 16 * 4; i > 0; --i) {
+ *p++ = 0;
+ }
+}
+
+enum modes {
+ MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL, MODE_FAVICON
+};
+
+const struct {
+ const enum modes mode;
+ const char * text;
+} modes_array[] = {
+ {MODE_CHUNKED, "chunked"},
+ {MODE_ADDCRAP, "addcrap"},
+ {MODE_NORMAL, "normal"},
+ {MODE_FAVICON, "favicon.ico"},
+ {MODE_INVALID, NULL}
+};
+
+/**
+ * write the response with random behaviour !
+ */
+void send_response(int c, const char * buffer, int len)
+{
+ int n;
+ while(len > 0) {
+ n = (rand() % 99) + 1;
+ if(n > len)
+ n = len;
+ n = write(c, buffer, n);
+ if(n < 0) {
+ if(errno != EINTR) {
+ perror("write");
+ return;
+ }
+ /* if errno == EINTR, try again */
+ } else {
+ len -= n;
+ buffer += n;
+ }
+ usleep(10000); /* 10ms */
+ }
+}
+
+/**
+ * handle the HTTP connection
+ */
+void handle_http_connection(int c)
+{
+ char request_buffer[2048];
+ int request_len = 0;
+ int headers_found = 0;
+ int n, i;
+ char request_method[16];
+ char request_uri[256];
+ char http_version[16];
+ char * p;
+ char * response_buffer;
+ int response_len;
+ enum modes mode;
+ int content_length = 16*1024;
+
+ /* read the request */
+ while(request_len < (int)sizeof(request_buffer) && !headers_found) {
+ n = read(c,
+ request_buffer + request_len,
+ sizeof(request_buffer) - request_len);
+ if(n < 0) {
+ if(errno == EINTR)
+ continue;
+ perror("read");
+ return;
+ } else if(n==0) {
+ /* remote host closed the connection */
+ break;
+ } else {
+ request_len += n;
+ for(i = 0; i < request_len - 3; i++) {
+ if(0 == memcmp(request_buffer + i, "\r\n\r\n", 4)) {
+ /* found the end of headers */
+ headers_found = 1;
+ break;
+ }
+ }
+ }
+ }
+ if(!headers_found) {
+ /* error */
+ printf("no HTTP header found in the request\n");
+ return;
+ }
+ printf("headers :\n%.*s", request_len, request_buffer);
+ /* the request have been received, now parse the request line */
+ p = request_buffer;
+ for(i = 0; i < (int)sizeof(request_method) - 1; i++) {
+ if(*p == ' ' || *p == '\r')
+ break;
+ request_method[i] = *p;
+ ++p;
+ }
+ request_method[i] = '\0';
+ while(*p == ' ')
+ p++;
+ for(i = 0; i < (int)sizeof(request_uri) - 1; i++) {
+ if(*p == ' ' || *p == '\r')
+ break;
+ request_uri[i] = *p;
+ ++p;
+ }
+ request_uri[i] = '\0';
+ while(*p == ' ')
+ p++;
+ for(i = 0; i < (int)sizeof(http_version) - 1; i++) {
+ if(*p == ' ' || *p == '\r')
+ break;
+ http_version[i] = *p;
+ ++p;
+ }
+ http_version[i] = '\0';
+ printf("Method = %s, URI = %s, %s\n",
+ request_method, request_uri, http_version);
+ /* check if the request method is allowed */
+ if(0 != strcmp(request_method, "GET")) {
+ const char response405[] = "HTTP/1.1 405 Method Not Allowed\r\n"
+ "Allow: GET\r\n\r\n";
+ const char * pc;
+ /* 405 Method Not Allowed */
+ /* The response MUST include an Allow header containing a list
+ * of valid methods for the requested resource. */
+ n = sizeof(response405) - 1;
+ pc = response405;
+ while(n > 0) {
+ i = write(c, pc, n);
+ if(i<0) {
+ if(errno != EINTR) {
+ perror("write");
+ return;
+ }
+ } else {
+ n -= i;
+ pc += i;
+ }
+ }
+ return;
+ }
+
+ mode = MODE_INVALID;
+ /* use the request URI to know what to do */
+ for(i = 0; modes_array[i].mode != MODE_INVALID; i++) {
+ if(strstr(request_uri, modes_array[i].text)) {
+ mode = modes_array[i].mode; /* found */
+ break;
+ }
+ }
+
+ switch(mode) {
+ case MODE_CHUNKED:
+ response_buffer = build_chunked_response(content_length, &response_len);
+ break;
+ case MODE_ADDCRAP:
+ response_len = content_length+256;
+ response_buffer = malloc(response_len);
+ if(!response_buffer)
+ break;
+ n = snprintf(response_buffer, response_len,
+ "HTTP/1.1 200 OK\r\n"
+ "Server: minihttptestserver\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n", content_length);
+ response_len = content_length+n+CRAP_LENGTH;
+ p = realloc(response_buffer, response_len);
+ if(p == NULL) {
+ /* error 500 */
+ free(response_buffer);
+ response_buffer = NULL;
+ break;
+ }
+ response_buffer = p;
+ build_content(response_buffer + n, content_length);
+ build_crap(response_buffer + n + content_length, CRAP_LENGTH);
+ break;
+ case MODE_FAVICON:
+ content_length = FAVICON_LENGTH;
+ response_len = content_length + 256;
+ response_buffer = malloc(response_len);
+ if(!response_buffer)
+ break;
+ n = snprintf(response_buffer, response_len,
+ "HTTP/1.1 200 OK\r\n"
+ "Server: minihttptestserver\r\n"
+ "Content-Type: image/vnd.microsoft.icon\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n", content_length);
+ /* image/x-icon */
+ build_favicon_content(response_buffer + n, content_length);
+ response_len = content_length + n;
+ break;
+ default:
+ response_len = content_length+256;
+ response_buffer = malloc(response_len);
+ if(!response_buffer)
+ break;
+ n = snprintf(response_buffer, response_len,
+ "HTTP/1.1 200 OK\r\n"
+ "Server: minihttptestserver\r\n"
+ "Content-Type: text/plain\r\n"
+ "\r\n");
+ response_len = content_length+n;
+ p = realloc(response_buffer, response_len);
+ if(p == NULL) {
+ /* Error 500 */
+ free(response_buffer);
+ response_buffer = NULL;
+ break;
+ }
+ response_buffer = p;
+ build_content(response_buffer + n, response_len - n);
+ }
+
+ if(response_buffer) {
+ send_response(c, response_buffer, response_len);
+ free(response_buffer);
+ } else {
+ /* Error 500 */
+ }
+}
+
+/**
+ */
+int main(int argc, char * * argv) {
+ int ipv6 = 0;
+ int s, c, i;
+ unsigned short port = 0;
+ struct sockaddr_storage server_addr;
+ socklen_t server_addrlen;
+ struct sockaddr_storage client_addr;
+ socklen_t client_addrlen;
+ pid_t pid;
+ int child = 0;
+ int status;
+ const char * expected_file_name = NULL;
+ struct sigaction sa;
+
+ for(i = 1; i < argc; i++) {
+ if(argv[i][0] == '-') {
+ switch(argv[i][1]) {
+ case '6':
+ ipv6 = 1;
+ break;
+ case 'e':
+ /* write expected file ! */
+ expected_file_name = argv[++i];
+ break;
+ case 'p':
+ /* port */
+ if(++i < argc) {
+ port = (unsigned short)atoi(argv[i]);
+ }
+ break;
+ default:
+ fprintf(stderr, "unknown command line switch '%s'\n", argv[i]);
+ }
+ } else {
+ fprintf(stderr, "unkown command line argument '%s'\n", argv[i]);
+ }
+ }
+
+ srand(time(NULL));
+
+ memset(&sa, 0, sizeof(struct sigaction));
+
+ /*signal(SIGCHLD, handle_signal_chld);*/
+ sa.sa_handler = handle_signal_chld;
+ if(sigaction(SIGCHLD, &sa, NULL) < 0) {
+ perror("sigaction");
+ return 1;
+ }
+ /*signal(SIGINT, handle_signal_int);*/
+ sa.sa_handler = handle_signal_int;
+ if(sigaction(SIGINT, &sa, NULL) < 0) {
+ perror("sigaction");
+ return 1;
+ }
+
+ s = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
+ if(s < 0) {
+ perror("socket");
+ return 1;
+ }
+ memset(&server_addr, 0, sizeof(struct sockaddr_storage));
+ memset(&client_addr, 0, sizeof(struct sockaddr_storage));
+ if(ipv6) {
+ struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
+ addr->sin6_family = AF_INET6;
+ addr->sin6_port = htons(port);
+ addr->sin6_addr = in6addr_loopback;
+ } else {
+ struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(port);
+ addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ }
+ if(bind(s, (struct sockaddr *)&server_addr,
+ ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) < 0) {
+ perror("bind");
+ return 1;
+ }
+ if(listen(s, 5) < 0) {
+ perror("listen");
+ }
+ if(port == 0) {
+ server_addrlen = sizeof(struct sockaddr_storage);
+ if(getsockname(s, (struct sockaddr *)&server_addr, &server_addrlen) < 0) {
+ perror("getsockname");
+ return 1;
+ }
+ if(ipv6) {
+ struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
+ port = ntohs(addr->sin6_port);
+ } else {
+ struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
+ port = ntohs(addr->sin_port);
+ }
+ printf("Listening on port %hu\n", port);
+ fflush(stdout);
+ }
+
+ /* write expected file */
+ if(expected_file_name) {
+ FILE * f;
+ f = fopen(expected_file_name, "wb");
+ if(f) {
+ char * buffer;
+ buffer = malloc(16*1024);
+ if(buffer == NULL) {
+ fprintf(stderr, "memory allocation error\n");
+ } else {
+ build_content(buffer, 16*1024);
+ i = fwrite(buffer, 1, 16*1024, f);
+ if(i != 16*1024) {
+ fprintf(stderr, "error writing to file %s : %dbytes written (out of %d)\n", expected_file_name, i, 16*1024);
+ }
+ free(buffer);
+ }
+ fclose(f);
+ } else {
+ fprintf(stderr, "error opening file %s for writing\n", expected_file_name);
+ }
+ }
+
+ /* fork() loop */
+ while(!child && !quit) {
+ while(child_to_wait_for > 0) {
+ pid = wait(&status);
+ if(pid < 0) {
+ perror("wait");
+ } else {
+ printf("child(%d) terminated with status %d\n", pid, status);
+ }
+ --child_to_wait_for;
+ }
+ client_addrlen = sizeof(struct sockaddr_storage);
+ c = accept(s, (struct sockaddr *)&client_addr,
+ &client_addrlen);
+ if(c < 0) {
+ if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ continue;
+ perror("accept");
+ return 1;
+ }
+ printf("accept...\n");
+ pid = fork();
+ if(pid < 0) {
+ perror("fork");
+ return 1;
+ } else if(pid == 0) {
+ /* child */
+ child = 1;
+ close(s);
+ s = -1;
+ handle_http_connection(c);
+ }
+ close(c);
+ }
+ if(s >= 0) {
+ close(s);
+ s = -1;
+ }
+ if(!child) {
+ while(child_to_wait_for > 0) {
+ pid = wait(&status);
+ if(pid < 0) {
+ perror("wait");
+ } else {
+ printf("child(%d) terminated with status %d\n", pid, status);
+ }
+ --child_to_wait_for;
+ }
+ printf("Bye...\n");
+ }
+ return 0;
+}
+
diff --git a/ext/miniupnpc/minisoap.c b/ext/miniupnpc/minisoap.c
new file mode 100644
index 00000000..0b56558e
--- /dev/null
+++ b/ext/miniupnpc/minisoap.c
@@ -0,0 +1,123 @@
+/* $Id: minisoap.c,v 1.24 2015/10/26 17:05:07 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ *
+ * Minimal SOAP implementation for UPnP protocol.
+ */
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <io.h>
+#include <winsock2.h>
+#define snprintf _snprintf
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+#include "minisoap.h"
+#include "miniupnpcstrings.h"
+
+/* only for malloc */
+#include <stdlib.h>
+
+#ifdef _WIN32
+#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+/* httpWrite sends the headers and the body to the socket
+ * and returns the number of bytes sent */
+static int
+httpWrite(int fd, const char * body, int bodysize,
+ const char * headers, int headerssize)
+{
+ int n = 0;
+ /*n = write(fd, headers, headerssize);*/
+ /*if(bodysize>0)
+ n += write(fd, body, bodysize);*/
+ /* Note : my old linksys router only took into account
+ * soap request that are sent into only one packet */
+ char * p;
+ /* TODO: AVOID MALLOC, we could use writev() for that */
+ p = malloc(headerssize+bodysize);
+ if(!p)
+ return -1;
+ memcpy(p, headers, headerssize);
+ memcpy(p+headerssize, body, bodysize);
+ /*n = write(fd, p, headerssize+bodysize);*/
+ n = send(fd, p, headerssize+bodysize, 0);
+ if(n<0) {
+ PRINT_SOCKET_ERROR("send");
+ }
+ /* disable send on the socket */
+ /* draytek routers dont seems to like that... */
+#if 0
+#ifdef _WIN32
+ if(shutdown(fd, SD_SEND)<0) {
+#else
+ if(shutdown(fd, SHUT_WR)<0) { /*SD_SEND*/
+#endif
+ PRINT_SOCKET_ERROR("shutdown");
+ }
+#endif
+ free(p);
+ return n;
+}
+
+/* self explanatory */
+int soapPostSubmit(int fd,
+ const char * url,
+ const char * host,
+ unsigned short port,
+ const char * action,
+ const char * body,
+ const char * httpversion)
+{
+ int bodysize;
+ char headerbuf[512];
+ int headerssize;
+ char portstr[8];
+ bodysize = (int)strlen(body);
+ /* We are not using keep-alive HTTP connections.
+ * HTTP/1.1 needs the header Connection: close to do that.
+ * This is the default with HTTP/1.0
+ * Using HTTP/1.1 means we need to support chunked transfer-encoding :
+ * When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked
+ * transfer encoding. */
+ /* Connection: Close is normally there only in HTTP/1.1 but who knows */
+ portstr[0] = '\0';
+ if(port != 80)
+ snprintf(portstr, sizeof(portstr), ":%hu", port);
+ headerssize = snprintf(headerbuf, sizeof(headerbuf),
+ "POST %s HTTP/%s\r\n"
+ "Host: %s%s\r\n"
+ "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
+ "Content-Length: %d\r\n"
+ "Content-Type: text/xml\r\n"
+ "SOAPAction: \"%s\"\r\n"
+ "Connection: Close\r\n"
+ "Cache-Control: no-cache\r\n" /* ??? */
+ "Pragma: no-cache\r\n"
+ "\r\n",
+ url, httpversion, host, portstr, bodysize, action);
+ if ((unsigned int)headerssize >= sizeof(headerbuf))
+ return -1;
+#ifdef DEBUG
+ /*printf("SOAP request : headersize=%d bodysize=%d\n",
+ headerssize, bodysize);
+ */
+ printf("SOAP request : POST %s HTTP/%s - Host: %s%s\n",
+ url, httpversion, host, portstr);
+ printf("SOAPAction: \"%s\" - Content-Length: %d\n", action, bodysize);
+ printf("Headers :\n%s", headerbuf);
+ printf("Body :\n%s\n", body);
+#endif
+ return httpWrite(fd, body, bodysize, headerbuf, headerssize);
+}
+
+
diff --git a/ext/miniupnpc/minisoap.h b/ext/miniupnpc/minisoap.h
new file mode 100644
index 00000000..14c859d1
--- /dev/null
+++ b/ext/miniupnpc/minisoap.h
@@ -0,0 +1,15 @@
+/* $Id: minisoap.h,v 1.5 2012/09/27 15:42:10 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+#ifndef MINISOAP_H_INCLUDED
+#define MINISOAP_H_INCLUDED
+
+/*int httpWrite(int, const char *, int, const char *);*/
+int soapPostSubmit(int, const char *, const char *, unsigned short,
+ const char *, const char *, const char *);
+
+#endif
+
diff --git a/ext/miniupnpc/minissdpc.c b/ext/miniupnpc/minissdpc.c
new file mode 100644
index 00000000..5a426739
--- /dev/null
+++ b/ext/miniupnpc/minissdpc.c
@@ -0,0 +1,849 @@
+/* $Id: minissdpc.c,v 1.30 2015/10/26 17:05:07 nanard Exp $ */
+/* Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+/*#include <syslog.h>*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#include <iphlpapi.h>
+#include <winsock.h>
+#define snprintf _snprintf
+#if !defined(_MSC_VER)
+#include <stdint.h>
+#else /* !defined(_MSC_VER) */
+typedef unsigned short uint16_t;
+#endif /* !defined(_MSC_VER) */
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#endif /* _WIN32 */
+#if defined(__amigaos__) || defined(__amigaos4__)
+#include <sys/socket.h>
+#endif /* defined(__amigaos__) || defined(__amigaos4__) */
+#if defined(__amigaos__)
+#define uint16_t unsigned short
+#endif /* defined(__amigaos__) */
+/* Hack */
+#define UNIX_PATH_LEN 108
+struct sockaddr_un {
+ uint16_t sun_family;
+ char sun_path[UNIX_PATH_LEN];
+};
+#else /* defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) */
+#include <strings.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+#define closesocket close
+#endif
+
+#ifdef _WIN32
+#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__)
+#define HAS_IP_MREQN
+#endif
+
+#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
+/* Several versions of glibc don't define this structure,
+ * define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
+struct ip_mreqn
+{
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_address; /* local IP address of interface */
+ int imr_ifindex; /* Interface index */
+};
+#endif
+
+#if defined(__amigaos__) || defined(__amigaos4__)
+/* Amiga OS specific stuff */
+#define TIMEVAL struct timeval
+#endif
+
+#include "minissdpc.h"
+#include "miniupnpc.h"
+#include "receivedata.h"
+
+#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
+
+#include "codelength.h"
+
+struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error)
+{
+ struct UPNPDev * devlist = NULL;
+ int s;
+ int res;
+
+ s = connectToMiniSSDPD(socketpath);
+ if (s < 0) {
+ if (error)
+ *error = s;
+ return NULL;
+ }
+ res = requestDevicesFromMiniSSDPD(s, devtype);
+ if (res < 0) {
+ if (error)
+ *error = res;
+ } else {
+ devlist = receiveDevicesFromMiniSSDPD(s, error);
+ }
+ disconnectFromMiniSSDPD(s);
+ return devlist;
+}
+
+/* macros used to read from unix socket */
+#define READ_BYTE_BUFFER(c) \
+ if((int)bufferindex >= n) { \
+ n = read(s, buffer, sizeof(buffer)); \
+ if(n<=0) break; \
+ bufferindex = 0; \
+ } \
+ c = buffer[bufferindex++];
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+
+#define READ_COPY_BUFFER(dst, len) \
+ for(l = len, p = (unsigned char *)dst; l > 0; ) { \
+ unsigned int lcopy; \
+ if((int)bufferindex >= n) { \
+ n = read(s, buffer, sizeof(buffer)); \
+ if(n<=0) break; \
+ bufferindex = 0; \
+ } \
+ lcopy = MIN(l, (n - bufferindex)); \
+ memcpy(p, buffer + bufferindex, lcopy); \
+ l -= lcopy; \
+ p += lcopy; \
+ bufferindex += lcopy; \
+ }
+
+#define READ_DISCARD_BUFFER(len) \
+ for(l = len; l > 0; ) { \
+ unsigned int lcopy; \
+ if(bufferindex >= n) { \
+ n = read(s, buffer, sizeof(buffer)); \
+ if(n<=0) break; \
+ bufferindex = 0; \
+ } \
+ lcopy = MIN(l, (n - bufferindex)); \
+ l -= lcopy; \
+ bufferindex += lcopy; \
+ }
+
+int
+connectToMiniSSDPD(const char * socketpath)
+{
+ int s;
+ struct sockaddr_un addr;
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ struct timeval timeout;
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(s < 0)
+ {
+ /*syslog(LOG_ERR, "socket(unix): %m");*/
+ perror("socket(unix)");
+ return MINISSDPC_SOCKET_ERROR;
+ }
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ /* setting a 3 seconds timeout */
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ perror("setsockopt");
+ }
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ perror("setsockopt");
+ }
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+ if(!socketpath)
+ socketpath = "/var/run/minissdpd.sock";
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
+ /* TODO : check if we need to handle the EINTR */
+ if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
+ {
+ /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/
+ close(s);
+ return MINISSDPC_SOCKET_ERROR;
+ }
+ return s;
+}
+
+int
+disconnectFromMiniSSDPD(int s)
+{
+ if (close(s) < 0)
+ return MINISSDPC_SOCKET_ERROR;
+ return MINISSDPC_SUCCESS;
+}
+
+int
+requestDevicesFromMiniSSDPD(int s, const char * devtype)
+{
+ unsigned char buffer[256];
+ unsigned char * p;
+ unsigned int stsize, l;
+
+ stsize = strlen(devtype);
+ if(stsize == 8 && 0 == memcmp(devtype, "ssdp:all", 8))
+ {
+ buffer[0] = 3; /* request type 3 : everything */
+ }
+ else
+ {
+ buffer[0] = 1; /* request type 1 : request devices/services by type */
+ }
+ p = buffer + 1;
+ l = stsize; CODELENGTH(l, p);
+ if(p + stsize > buffer + sizeof(buffer))
+ {
+ /* devtype is too long ! */
+#ifdef DEBUG
+ fprintf(stderr, "devtype is too long ! stsize=%u sizeof(buffer)=%u\n",
+ stsize, (unsigned)sizeof(buffer));
+#endif /* DEBUG */
+ return MINISSDPC_INVALID_INPUT;
+ }
+ memcpy(p, devtype, stsize);
+ p += stsize;
+ if(write(s, buffer, p - buffer) < 0)
+ {
+ /*syslog(LOG_ERR, "write(): %m");*/
+ perror("minissdpc.c: write()");
+ return MINISSDPC_SOCKET_ERROR;
+ }
+ return MINISSDPC_SUCCESS;
+}
+
+struct UPNPDev *
+receiveDevicesFromMiniSSDPD(int s, int * error)
+{
+ struct UPNPDev * tmp;
+ struct UPNPDev * devlist = NULL;
+ unsigned char buffer[256];
+ ssize_t n;
+ unsigned char * p;
+ unsigned char * url;
+ unsigned char * st;
+ unsigned int bufferindex;
+ unsigned int i, ndev;
+ unsigned int urlsize, stsize, usnsize, l;
+
+ n = read(s, buffer, sizeof(buffer));
+ if(n<=0)
+ {
+ perror("minissdpc.c: read()");
+ if (error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ return NULL;
+ }
+ ndev = buffer[0];
+ bufferindex = 1;
+ for(i = 0; i < ndev; i++)
+ {
+ DECODELENGTH_READ(urlsize, READ_BYTE_BUFFER);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ return devlist;
+ }
+#ifdef DEBUG
+ printf(" urlsize=%u", urlsize);
+#endif /* DEBUG */
+ url = malloc(urlsize);
+ if(url == NULL) {
+ if (error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ return devlist;
+ }
+ READ_COPY_BUFFER(url, urlsize);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_return;
+ }
+ DECODELENGTH_READ(stsize, READ_BYTE_BUFFER);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_return;
+ }
+#ifdef DEBUG
+ printf(" stsize=%u", stsize);
+#endif /* DEBUG */
+ st = malloc(stsize);
+ if (st == NULL) {
+ if (error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto free_url_and_return;
+ }
+ READ_COPY_BUFFER(st, stsize);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_st_and_return;
+ }
+ DECODELENGTH_READ(usnsize, READ_BYTE_BUFFER);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_st_and_return;
+ }
+#ifdef DEBUG
+ printf(" usnsize=%u\n", usnsize);
+#endif /* DEBUG */
+ tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
+ if(tmp == NULL) {
+ if (error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto free_url_and_st_and_return;
+ }
+ tmp->pNext = devlist;
+ tmp->descURL = tmp->buffer;
+ tmp->st = tmp->buffer + 1 + urlsize;
+ memcpy(tmp->buffer, url, urlsize);
+ tmp->buffer[urlsize] = '\0';
+ memcpy(tmp->st, st, stsize);
+ tmp->buffer[urlsize+1+stsize] = '\0';
+ free(url);
+ free(st);
+ url = NULL;
+ st = NULL;
+ tmp->usn = tmp->buffer + 1 + urlsize + 1 + stsize;
+ READ_COPY_BUFFER(tmp->usn, usnsize);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_tmp_and_return;
+ }
+ tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
+ tmp->scope_id = 0; /* default value. scope_id is not available with MiniSSDPd */
+ devlist = tmp;
+ }
+ if (error)
+ *error = MINISSDPC_SUCCESS;
+ return devlist;
+
+free_url_and_st_and_return:
+ free(st);
+free_url_and_return:
+ free(url);
+ return devlist;
+
+free_tmp_and_return:
+ free(tmp);
+ return devlist;
+}
+
+#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
+
+/* parseMSEARCHReply()
+ * the last 4 arguments are filled during the parsing :
+ * - location/locationsize : "location:" field of the SSDP reply packet
+ * - st/stsize : "st:" field of the SSDP reply packet.
+ * The strings are NOT null terminated */
+static void
+parseMSEARCHReply(const char * reply, int size,
+ const char * * location, int * locationsize,
+ const char * * st, int * stsize,
+ const char * * usn, int * usnsize)
+{
+ int a, b, i;
+ i = 0;
+ a = i; /* start of the line */
+ b = 0; /* end of the "header" (position of the colon) */
+ while(i<size)
+ {
+ switch(reply[i])
+ {
+ case ':':
+ if(b==0)
+ {
+ b = i; /* end of the "header" */
+ /*for(j=a; j<b; j++)
+ {
+ putchar(reply[j]);
+ }
+ */
+ }
+ break;
+ case '\x0a':
+ case '\x0d':
+ if(b!=0)
+ {
+ /*for(j=b+1; j<i; j++)
+ {
+ putchar(reply[j]);
+ }
+ putchar('\n');*/
+ /* skip the colon and white spaces */
+ do { b++; } while(reply[b]==' ');
+ if(0==strncasecmp(reply+a, "location", 8))
+ {
+ *location = reply+b;
+ *locationsize = i-b;
+ }
+ else if(0==strncasecmp(reply+a, "st", 2))
+ {
+ *st = reply+b;
+ *stsize = i-b;
+ }
+ else if(0==strncasecmp(reply+a, "usn", 3))
+ {
+ *usn = reply+b;
+ *usnsize = i-b;
+ }
+ b = 0;
+ }
+ a = i+1;
+ break;
+ default:
+ break;
+ }
+ i++;
+ }
+}
+
+/* port upnp discover : SSDP protocol */
+#define SSDP_PORT 1900
+#define XSTR(s) STR(s)
+#define STR(s) #s
+#define UPNP_MCAST_ADDR "239.255.255.250"
+/* for IPv6 */
+#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
+#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
+
+/* direct discovery if minissdpd responses are not sufficient */
+/* ssdpDiscoverDevices() :
+ * return a chained list of all devices found or NULL if
+ * no devices was found.
+ * It is up to the caller to free the chained list
+ * delay is in millisecond (poll).
+ * UDA v1.1 says :
+ * The TTL for the IP packet SHOULD default to 2 and
+ * SHOULD be configurable. */
+struct UPNPDev *
+ssdpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes)
+{
+ struct UPNPDev * tmp;
+ struct UPNPDev * devlist = 0;
+ unsigned int scope_id = 0;
+ int opt = 1;
+ static const char MSearchMsgFmt[] =
+ "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: %s:" XSTR(SSDP_PORT) "\r\n"
+ "ST: %s\r\n"
+ "MAN: \"ssdp:discover\"\r\n"
+ "MX: %u\r\n"
+ "\r\n";
+ int deviceIndex;
+ char bufr[1536]; /* reception and emission buffer */
+ int sudp;
+ int n;
+ struct sockaddr_storage sockudp_r;
+ unsigned int mx;
+#ifdef NO_GETADDRINFO
+ struct sockaddr_storage sockudp_w;
+#else
+ int rv;
+ struct addrinfo hints, *servinfo, *p;
+#endif
+#ifdef _WIN32
+ MIB_IPFORWARDROW ip_forward;
+ unsigned long _ttl = (unsigned long)ttl;
+#endif
+ int linklocal = 1;
+
+ if(error)
+ *error = MINISSDPC_UNKNOWN_ERROR;
+
+ if(localport==UPNP_LOCAL_PORT_SAME)
+ localport = SSDP_PORT;
+
+#ifdef _WIN32
+ sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+#else
+ sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
+#endif
+ if(sudp < 0)
+ {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("socket");
+ return NULL;
+ }
+ /* reception */
+ memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
+ if(ipv6) {
+ struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
+ p->sin6_family = AF_INET6;
+ if(localport > 0 && localport < 65536)
+ p->sin6_port = htons((unsigned short)localport);
+ p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
+ } else {
+ struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
+ p->sin_family = AF_INET;
+ if(localport > 0 && localport < 65536)
+ p->sin_port = htons((unsigned short)localport);
+ p->sin_addr.s_addr = INADDR_ANY;
+ }
+#ifdef _WIN32
+/* This code could help us to use the right Network interface for
+ * SSDP multicast traffic */
+/* Get IP associated with the index given in the ip_forward struct
+ * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
+ if(!ipv6
+ && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
+ DWORD dwRetVal = 0;
+ PMIB_IPADDRTABLE pIPAddrTable;
+ DWORD dwSize = 0;
+#ifdef DEBUG
+ IN_ADDR IPAddr;
+#endif
+ int i;
+#ifdef DEBUG
+ printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
+#endif
+ pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
+ if(pIPAddrTable) {
+ if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
+ free(pIPAddrTable);
+ pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
+ }
+ }
+ if(pIPAddrTable) {
+ dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
+ if (dwRetVal == NO_ERROR) {
+#ifdef DEBUG
+ printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
+#endif
+ for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
+#ifdef DEBUG
+ printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
+ printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
+ printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
+ printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
+ printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
+ printf("\tType and State[%d]:", i);
+ printf("\n");
+#endif
+ if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
+ /* Set the address of this interface to be used */
+ struct in_addr mc_if;
+ memset(&mc_if, 0, sizeof(mc_if));
+ mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
+#ifndef DEBUG
+ break;
+#endif
+ }
+ }
+ }
+ free(pIPAddrTable);
+ pIPAddrTable = NULL;
+ }
+ }
+#endif /* _WIN32 */
+
+#ifdef _WIN32
+ if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
+#else
+ if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
+#endif
+ {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("setsockopt(SO_REUSEADDR,...)");
+ return NULL;
+ }
+
+#ifdef _WIN32
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
+#else /* _WIN32 */
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
+#endif /* _WIN32 */
+ {
+ /* not a fatal error */
+ PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
+ }
+
+ if(multicastif)
+ {
+ if(ipv6) {
+#if !defined(_WIN32)
+ /* according to MSDN, if_nametoindex() is supported since
+ * MS Windows Vista and MS Windows Server 2008.
+ * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
+ unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+#else
+#ifdef DEBUG
+ printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
+#endif
+#endif
+ } else {
+ struct in_addr mc_if;
+ mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
+ if(mc_if.s_addr != INADDR_NONE)
+ {
+ ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ } else {
+#ifdef HAS_IP_MREQN
+ /* was not an ip address, try with an interface name */
+ struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
+ memset(&reqn, 0, sizeof(struct ip_mreqn));
+ reqn.imr_ifindex = if_nametoindex(multicastif);
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+#else
+#ifdef DEBUG
+ printf("Setting of multicast interface not supported with interface name.\n");
+#endif
+#endif
+ }
+ }
+ }
+
+ /* Before sending the packed, we first "bind" in order to be able
+ * to receive the response */
+ if (bind(sudp, (const struct sockaddr *)&sockudp_r,
+ ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
+ {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("bind");
+ closesocket(sudp);
+ return NULL;
+ }
+
+ if(error)
+ *error = MINISSDPC_SUCCESS;
+ /* Calculating maximum response time in seconds */
+ mx = ((unsigned int)delay) / 1000u;
+ if(mx == 0) {
+ mx = 1;
+ delay = 1000;
+ }
+ /* receiving SSDP response packet */
+ for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
+ /* sending the SSDP M-SEARCH packet */
+ n = snprintf(bufr, sizeof(bufr),
+ MSearchMsgFmt,
+ ipv6 ?
+ (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
+ : UPNP_MCAST_ADDR,
+ deviceTypes[deviceIndex], mx);
+ if ((unsigned int)n >= sizeof(bufr)) {
+ if(error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto error;
+ }
+#ifdef DEBUG
+ /*printf("Sending %s", bufr);*/
+ printf("Sending M-SEARCH request to %s with ST: %s\n",
+ ipv6 ?
+ (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
+ : UPNP_MCAST_ADDR,
+ deviceTypes[deviceIndex]);
+#endif
+#ifdef NO_GETADDRINFO
+ /* the following code is not using getaddrinfo */
+ /* emission */
+ memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
+ if(ipv6) {
+ struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
+ p->sin6_family = AF_INET6;
+ p->sin6_port = htons(SSDP_PORT);
+ inet_pton(AF_INET6,
+ linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
+ &(p->sin6_addr));
+ } else {
+ struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
+ p->sin_family = AF_INET;
+ p->sin_port = htons(SSDP_PORT);
+ p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
+ }
+ n = sendto(sudp, bufr, n, 0, &sockudp_w,
+ ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
+ if (n < 0) {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("sendto");
+ break;
+ }
+#else /* #ifdef NO_GETADDRINFO */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */
+ hints.ai_socktype = SOCK_DGRAM;
+ /*hints.ai_flags = */
+ if ((rv = getaddrinfo(ipv6
+ ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
+ : UPNP_MCAST_ADDR,
+ XSTR(SSDP_PORT), &hints, &servinfo)) != 0) {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+#ifdef _WIN32
+ fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
+#else
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+#endif
+ break;
+ }
+ for(p = servinfo; p; p = p->ai_next) {
+ n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
+ if (n < 0) {
+#ifdef DEBUG
+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+ if (getnameinfo(p->ai_addr, p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
+ sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
+ fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
+ }
+#endif
+ PRINT_SOCKET_ERROR("sendto");
+ continue;
+ }
+ }
+ freeaddrinfo(servinfo);
+ if(n < 0) {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ break;
+ }
+#endif /* #ifdef NO_GETADDRINFO */
+ /* Waiting for SSDP REPLY packet to M-SEARCH
+ * if searchalltypes is set, enter the loop only
+ * when the last deviceType is reached */
+ if(!searchalltypes || !deviceTypes[deviceIndex + 1]) do {
+ n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
+ if (n < 0) {
+ /* error */
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ goto error;
+ } else if (n == 0) {
+ /* no data or Time Out */
+#ifdef DEBUG
+ printf("NODATA or TIMEOUT\n");
+#endif /* DEBUG */
+ if (devlist && !searchalltypes) {
+ /* found some devices, stop now*/
+ if(error)
+ *error = MINISSDPC_SUCCESS;
+ goto error;
+ }
+ } else {
+ const char * descURL=NULL;
+ int urlsize=0;
+ const char * st=NULL;
+ int stsize=0;
+ const char * usn=NULL;
+ int usnsize=0;
+ parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize);
+ if(st&&descURL) {
+#ifdef DEBUG
+ printf("M-SEARCH Reply:\n ST: %.*s\n USN: %.*s\n Location: %.*s\n",
+ stsize, st, usnsize, (usn?usn:""), urlsize, descURL);
+#endif /* DEBUG */
+ for(tmp=devlist; tmp; tmp = tmp->pNext) {
+ if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
+ tmp->descURL[urlsize] == '\0' &&
+ memcmp(tmp->st, st, stsize) == 0 &&
+ tmp->st[stsize] == '\0' &&
+ (usnsize == 0 || memcmp(tmp->usn, usn, usnsize) == 0) &&
+ tmp->usn[usnsize] == '\0')
+ break;
+ }
+ /* at the exit of the loop above, tmp is null if
+ * no duplicate device was found */
+ if(tmp)
+ continue;
+ tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
+ if(!tmp) {
+ /* memory allocation error */
+ if(error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto error;
+ }
+ tmp->pNext = devlist;
+ tmp->descURL = tmp->buffer;
+ tmp->st = tmp->buffer + 1 + urlsize;
+ tmp->usn = tmp->st + 1 + stsize;
+ memcpy(tmp->buffer, descURL, urlsize);
+ tmp->buffer[urlsize] = '\0';
+ memcpy(tmp->st, st, stsize);
+ tmp->buffer[urlsize+1+stsize] = '\0';
+ if(usn != NULL)
+ memcpy(tmp->usn, usn, usnsize);
+ tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
+ tmp->scope_id = scope_id;
+ devlist = tmp;
+ }
+ }
+ } while(n > 0);
+ if(ipv6) {
+ /* switch linklocal flag */
+ if(linklocal) {
+ linklocal = 0;
+ --deviceIndex;
+ } else {
+ linklocal = 1;
+ }
+ }
+ }
+error:
+ closesocket(sudp);
+ return devlist;
+}
+
diff --git a/ext/miniupnpc/minissdpc.h b/ext/miniupnpc/minissdpc.h
new file mode 100644
index 00000000..a5c622b2
--- /dev/null
+++ b/ext/miniupnpc/minissdpc.h
@@ -0,0 +1,58 @@
+/* $Id: minissdpc.h,v 1.7 2015/10/08 16:15:47 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef MINISSDPC_H_INCLUDED
+#define MINISSDPC_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "upnpdev.h"
+
+/* error codes : */
+#define MINISSDPC_SUCCESS (0)
+#define MINISSDPC_UNKNOWN_ERROR (-1)
+#define MINISSDPC_SOCKET_ERROR (-101)
+#define MINISSDPC_MEMORY_ERROR (-102)
+#define MINISSDPC_INVALID_INPUT (-103)
+#define MINISSDPC_INVALID_SERVER_REPLY (-104)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error);
+
+MINIUPNP_LIBSPEC int
+connectToMiniSSDPD(const char * socketpath);
+
+MINIUPNP_LIBSPEC int
+disconnectFromMiniSSDPD(int fd);
+
+MINIUPNP_LIBSPEC int
+requestDevicesFromMiniSSDPD(int fd, const char * devtype);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+receiveDevicesFromMiniSSDPD(int fd, int * error);
+
+#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+ssdpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/ext/miniupnpc/miniupnpc.c b/ext/miniupnpc/miniupnpc.c
new file mode 100644
index 00000000..4837fe7d
--- /dev/null
+++ b/ext/miniupnpc/miniupnpc.c
@@ -0,0 +1,684 @@
+/* $Id: miniupnpc.c,v 1.141 2015/10/26 17:05:07 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab */
+/* Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENSE file. */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+/* Win32 Specific includes and defines */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#include <iphlpapi.h>
+#define snprintf _snprintf
+#define strdup _strdup
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#define MAXHOSTNAMELEN 64
+#else /* #ifdef _WIN32 */
+/* Standard POSIX includes */
+#include <unistd.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+/* Amiga OS 3 specific stuff */
+#define socklen_t int
+#else
+#include <sys/select.h>
+#endif
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+#if !defined(__amigaos__) && !defined(__amigaos4__)
+#include <poll.h>
+#endif
+#include <strings.h>
+#include <errno.h>
+#define closesocket close
+#endif /* #else _WIN32 */
+#ifdef __GNU__
+#define MAXHOSTNAMELEN 64
+#endif
+
+
+#include "miniupnpc.h"
+#include "minissdpc.h"
+#include "miniwget.h"
+#include "minisoap.h"
+#include "minixml.h"
+#include "upnpcommands.h"
+#include "connecthostport.h"
+
+/* compare the begining of a string with a constant string */
+#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define SOAPPREFIX "s"
+#define SERVICEPREFIX "u"
+#define SERVICEPREFIX2 'u'
+
+/* root description parsing */
+MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
+{
+ struct xmlparser parser;
+ /* xmlparser object */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = data;
+ parser.starteltfunc = IGDstartelt;
+ parser.endeltfunc = IGDendelt;
+ parser.datafunc = IGDdata;
+ parser.attfunc = 0;
+ parsexml(&parser);
+#ifdef DEBUG
+ printIGD(data);
+#endif
+}
+
+/* simpleUPnPcommand2 :
+ * not so simple !
+ * return values :
+ * pointer - OK
+ * NULL - error */
+char * simpleUPnPcommand2(int s, const char * url, const char * service,
+ const char * action, struct UPNParg * args,
+ int * bufsize, const char * httpversion)
+{
+ char hostname[MAXHOSTNAMELEN+1];
+ unsigned short port = 0;
+ char * path;
+ char soapact[128];
+ char soapbody[2048];
+ int soapbodylen;
+ char * buf;
+ int n;
+
+ *bufsize = 0;
+ snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
+ if(args==NULL)
+ {
+ soapbodylen = snprintf(soapbody, sizeof(soapbody),
+ "<?xml version=\"1.0\"?>\r\n"
+ "<" SOAPPREFIX ":Envelope "
+ "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<" SOAPPREFIX ":Body>"
+ "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
+ "</" SERVICEPREFIX ":%s>"
+ "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
+ "\r\n", action, service, action);
+ if ((unsigned int)soapbodylen >= sizeof(soapbody))
+ return NULL;
+ }
+ else
+ {
+ char * p;
+ const char * pe, * pv;
+ const char * const pend = soapbody + sizeof(soapbody);
+ soapbodylen = snprintf(soapbody, sizeof(soapbody),
+ "<?xml version=\"1.0\"?>\r\n"
+ "<" SOAPPREFIX ":Envelope "
+ "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<" SOAPPREFIX ":Body>"
+ "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
+ action, service);
+ if ((unsigned int)soapbodylen >= sizeof(soapbody))
+ return NULL;
+ p = soapbody + soapbodylen;
+ while(args->elt)
+ {
+ if(p >= pend) /* check for space to write next byte */
+ return NULL;
+ *(p++) = '<';
+
+ pe = args->elt;
+ while(p < pend && *pe)
+ *(p++) = *(pe++);
+
+ if(p >= pend) /* check for space to write next byte */
+ return NULL;
+ *(p++) = '>';
+
+ if((pv = args->val))
+ {
+ while(p < pend && *pv)
+ *(p++) = *(pv++);
+ }
+
+ if((p+2) > pend) /* check for space to write next 2 bytes */
+ return NULL;
+ *(p++) = '<';
+ *(p++) = '/';
+
+ pe = args->elt;
+ while(p < pend && *pe)
+ *(p++) = *(pe++);
+
+ if(p >= pend) /* check for space to write next byte */
+ return NULL;
+ *(p++) = '>';
+
+ args++;
+ }
+ if((p+4) > pend) /* check for space to write next 4 bytes */
+ return NULL;
+ *(p++) = '<';
+ *(p++) = '/';
+ *(p++) = SERVICEPREFIX2;
+ *(p++) = ':';
+
+ pe = action;
+ while(p < pend && *pe)
+ *(p++) = *(pe++);
+
+ strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
+ pend - p);
+ if(soapbody[sizeof(soapbody)-1]) /* strncpy pads buffer with 0s, so if it doesn't end in 0, could not fit full string */
+ return NULL;
+ }
+ if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
+ if(s < 0) {
+ s = connecthostport(hostname, port, 0);
+ if(s < 0) {
+ /* failed to connect */
+ return NULL;
+ }
+ }
+
+ n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
+ if(n<=0) {
+#ifdef DEBUG
+ printf("Error sending SOAP request\n");
+#endif
+ closesocket(s);
+ return NULL;
+ }
+
+ buf = getHTTPResponse(s, bufsize);
+#ifdef DEBUG
+ if(*bufsize > 0 && buf)
+ {
+ printf("SOAP Response :\n%.*s\n", *bufsize, buf);
+ }
+#endif
+ closesocket(s);
+ return buf;
+}
+
+/* simpleUPnPcommand :
+ * not so simple !
+ * return values :
+ * pointer - OK
+ * NULL - error */
+char * simpleUPnPcommand(int s, const char * url, const char * service,
+ const char * action, struct UPNParg * args,
+ int * bufsize)
+{
+ char * buf;
+
+#if 1
+ buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
+#else
+ buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0");
+ if (!buf || *bufsize == 0)
+ {
+#if DEBUG
+ printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
+#endif
+ buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
+ }
+#endif
+ return buf;
+}
+
+/* upnpDiscoverDevices() :
+ * return a chained list of all devices found or NULL if
+ * no devices was found.
+ * It is up to the caller to free the chained list
+ * delay is in millisecond (poll).
+ * UDA v1.1 says :
+ * The TTL for the IP packet SHOULD default to 2 and
+ * SHOULD be configurable. */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes)
+{
+ struct UPNPDev * tmp;
+ struct UPNPDev * devlist = 0;
+#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+ int deviceIndex;
+#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+
+ if(error)
+ *error = UPNPDISCOVER_UNKNOWN_ERROR;
+#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+ /* first try to get infos from minissdpd ! */
+ if(!minissdpdsock)
+ minissdpdsock = "/var/run/minissdpd.sock";
+ for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
+ struct UPNPDev * minissdpd_devlist;
+ int only_rootdevice = 1;
+ minissdpd_devlist = getDevicesFromMiniSSDPD(deviceTypes[deviceIndex],
+ minissdpdsock, 0);
+ if(minissdpd_devlist) {
+#ifdef DEBUG
+ printf("returned by MiniSSDPD: %s\t%s\n",
+ minissdpd_devlist->st, minissdpd_devlist->descURL);
+#endif /* DEBUG */
+ if(!strstr(minissdpd_devlist->st, "rootdevice"))
+ only_rootdevice = 0;
+ for(tmp = minissdpd_devlist; tmp->pNext != NULL; tmp = tmp->pNext) {
+#ifdef DEBUG
+ printf("returned by MiniSSDPD: %s\t%s\n",
+ tmp->pNext->st, tmp->pNext->descURL);
+#endif /* DEBUG */
+ if(!strstr(tmp->st, "rootdevice"))
+ only_rootdevice = 0;
+ }
+ tmp->pNext = devlist;
+ devlist = minissdpd_devlist;
+ if(!searchalltypes && !only_rootdevice)
+ break;
+ }
+ }
+ for(tmp = devlist; tmp != NULL; tmp = tmp->pNext) {
+ /* We return what we have found if it was not only a rootdevice */
+ if(!strstr(tmp->st, "rootdevice")) {
+ if(error)
+ *error = UPNPDISCOVER_SUCCESS;
+ return devlist;
+ }
+ }
+#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+
+ /* direct discovery if minissdpd responses are not sufficient */
+ {
+ struct UPNPDev * discovered_devlist;
+ discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, localport,
+ ipv6, ttl, error, searchalltypes);
+ if(devlist == NULL)
+ devlist = discovered_devlist;
+ else {
+ for(tmp = devlist; tmp->pNext != NULL; tmp = tmp->pNext);
+ tmp->pNext = discovered_devlist;
+ }
+ }
+ return devlist;
+}
+
+/* upnpDiscover() Discover IGD device */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error)
+{
+ static const char * const deviceList[] = {
+#if 0
+ "urn:schemas-upnp-org:device:InternetGatewayDevice:2",
+ "urn:schemas-upnp-org:service:WANIPConnection:2",
+#endif
+ "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
+ "urn:schemas-upnp-org:service:WANIPConnection:1",
+ "urn:schemas-upnp-org:service:WANPPPConnection:1",
+ "upnp:rootdevice",
+ /*"ssdp:all",*/
+ 0
+ };
+ return upnpDiscoverDevices(deviceList,
+ delay, multicastif, minissdpdsock, localport,
+ ipv6, ttl, error, 0);
+}
+
+/* upnpDiscoverAll() Discover all UPnP devices */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverAll(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error)
+{
+ static const char * const deviceList[] = {
+ /*"upnp:rootdevice",*/
+ "ssdp:all",
+ 0
+ };
+ return upnpDiscoverDevices(deviceList,
+ delay, multicastif, minissdpdsock, localport,
+ ipv6, ttl, error, 0);
+}
+
+/* upnpDiscoverDevice() Discover a specific device */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error)
+{
+ const char * const deviceList[] = {
+ device,
+ 0
+ };
+ return upnpDiscoverDevices(deviceList,
+ delay, multicastif, minissdpdsock, localport,
+ ipv6, ttl, error, 0);
+}
+
+static char *
+build_absolute_url(const char * baseurl, const char * descURL,
+ const char * url, unsigned int scope_id)
+{
+ int l, n;
+ char * s;
+ const char * base;
+ char * p;
+#if defined(IF_NAMESIZE) && !defined(_WIN32)
+ char ifname[IF_NAMESIZE];
+#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ char scope_str[8];
+#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+
+ if( (url[0] == 'h')
+ &&(url[1] == 't')
+ &&(url[2] == 't')
+ &&(url[3] == 'p')
+ &&(url[4] == ':')
+ &&(url[5] == '/')
+ &&(url[6] == '/'))
+ return strdup(url);
+ base = (baseurl[0] == '\0') ? descURL : baseurl;
+ n = strlen(base);
+ if(n > 7) {
+ p = strchr(base + 7, '/');
+ if(p)
+ n = p - base;
+ }
+ l = n + strlen(url) + 1;
+ if(url[0] != '/')
+ l++;
+ if(scope_id != 0) {
+#if defined(IF_NAMESIZE) && !defined(_WIN32)
+ if(if_indextoname(scope_id, ifname)) {
+ l += 3 + strlen(ifname); /* 3 == strlen(%25) */
+ }
+#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ /* under windows, scope is numerical */
+ l += 3 + snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
+#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ }
+ s = malloc(l);
+ if(s == NULL) return NULL;
+ memcpy(s, base, n);
+ if(scope_id != 0) {
+ s[n] = '\0';
+ if(0 == memcmp(s, "http://[fe80:", 13)) {
+ /* this is a linklocal IPv6 address */
+ p = strchr(s, ']');
+ if(p) {
+ /* insert %25<scope> into URL */
+#if defined(IF_NAMESIZE) && !defined(_WIN32)
+ memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
+ memcpy(p, "%25", 3);
+ memcpy(p + 3, ifname, strlen(ifname));
+ n += 3 + strlen(ifname);
+#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
+ memcpy(p, "%25", 3);
+ memcpy(p + 3, scope_str, strlen(scope_str));
+ n += 3 + strlen(scope_str);
+#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ }
+ }
+ }
+ if(url[0] != '/')
+ s[n++] = '/';
+ memcpy(s + n, url, l - n);
+ return s;
+}
+
+/* Prepare the Urls for usage...
+ */
+MINIUPNP_LIBSPEC void
+GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * descURL, unsigned int scope_id)
+{
+ /* strdup descURL */
+ urls->rootdescURL = strdup(descURL);
+
+ /* get description of WANIPConnection */
+ urls->ipcondescURL = build_absolute_url(data->urlbase, descURL,
+ data->first.scpdurl, scope_id);
+ urls->controlURL = build_absolute_url(data->urlbase, descURL,
+ data->first.controlurl, scope_id);
+ urls->controlURL_CIF = build_absolute_url(data->urlbase, descURL,
+ data->CIF.controlurl, scope_id);
+ urls->controlURL_6FC = build_absolute_url(data->urlbase, descURL,
+ data->IPv6FC.controlurl, scope_id);
+
+#ifdef DEBUG
+ printf("urls->ipcondescURL='%s'\n", urls->ipcondescURL);
+ printf("urls->controlURL='%s'\n", urls->controlURL);
+ printf("urls->controlURL_CIF='%s'\n", urls->controlURL_CIF);
+ printf("urls->controlURL_6FC='%s'\n", urls->controlURL_6FC);
+#endif
+}
+
+MINIUPNP_LIBSPEC void
+FreeUPNPUrls(struct UPNPUrls * urls)
+{
+ if(!urls)
+ return;
+ free(urls->controlURL);
+ urls->controlURL = 0;
+ free(urls->ipcondescURL);
+ urls->ipcondescURL = 0;
+ free(urls->controlURL_CIF);
+ urls->controlURL_CIF = 0;
+ free(urls->controlURL_6FC);
+ urls->controlURL_6FC = 0;
+ free(urls->rootdescURL);
+ urls->rootdescURL = 0;
+}
+
+int
+UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
+{
+ char status[64];
+ unsigned int uptime;
+ status[0] = '\0';
+ UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
+ status, &uptime, NULL);
+ if(0 == strcmp("Connected", status))
+ return 1;
+ else if(0 == strcmp("Up", status)) /* Also accept "Up" */
+ return 1;
+ else
+ return 0;
+}
+
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ * -1 = Internal error
+ * 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 positive non zero return case, the urls and data structures
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen)
+{
+ struct xml_desc {
+ char * xml;
+ int size;
+ int is_igd;
+ } * desc = NULL;
+ struct UPNPDev * dev;
+ int ndev = 0;
+ int i;
+ int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
+ int n_igd = 0;
+ char extIpAddr[16];
+ if(!devlist)
+ {
+#ifdef DEBUG
+ printf("Empty devlist\n");
+#endif
+ return 0;
+ }
+ /* counting total number of devices in the list */
+ for(dev = devlist; dev; dev = dev->pNext)
+ ndev++;
+ if(ndev > 0)
+ {
+ desc = calloc(ndev, sizeof(struct xml_desc));
+ if(!desc)
+ return -1; /* memory allocation error */
+ }
+ /* Step 1 : downloading descriptions and testing type */
+ for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
+ {
+ /* we should choose an internet gateway device.
+ * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
+ desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
+ lanaddr, lanaddrlen,
+ dev->scope_id);
+#ifdef DEBUG
+ if(!desc[i].xml)
+ {
+ printf("error getting XML description %s\n", dev->descURL);
+ }
+#endif
+ if(desc[i].xml)
+ {
+ memset(data, 0, sizeof(struct IGDdatas));
+ memset(urls, 0, sizeof(struct UPNPUrls));
+ parserootdesc(desc[i].xml, desc[i].size, data);
+ if(COMPARE(data->CIF.servicetype,
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:"))
+ {
+ desc[i].is_igd = 1;
+ n_igd++;
+ }
+ }
+ }
+ /* iterate the list to find a device depending on state */
+ for(state = 1; state <= 3; state++)
+ {
+ for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
+ {
+ if(desc[i].xml)
+ {
+ memset(data, 0, sizeof(struct IGDdatas));
+ memset(urls, 0, sizeof(struct UPNPUrls));
+ parserootdesc(desc[i].xml, desc[i].size, data);
+ if(desc[i].is_igd || state >= 3 )
+ {
+ GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
+
+ /* in state 2 and 3 we dont test if device is connected ! */
+ if(state >= 2)
+ goto free_and_return;
+#ifdef DEBUG
+ printf("UPNPIGD_IsConnected(%s) = %d\n",
+ urls->controlURL,
+ UPNPIGD_IsConnected(urls, data));
+#endif
+ /* checks that status is connected AND there is a external IP address assigned */
+ if(UPNPIGD_IsConnected(urls, data)
+ && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0))
+ goto free_and_return;
+ FreeUPNPUrls(urls);
+ if(data->second.servicetype[0] != '\0') {
+#ifdef DEBUG
+ printf("We tried %s, now we try %s !\n",
+ data->first.servicetype, data->second.servicetype);
+#endif
+ /* swaping WANPPPConnection and WANIPConnection ! */
+ memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
+ memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
+ memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
+ GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
+#ifdef DEBUG
+ printf("UPNPIGD_IsConnected(%s) = %d\n",
+ urls->controlURL,
+ UPNPIGD_IsConnected(urls, data));
+#endif
+ if(UPNPIGD_IsConnected(urls, data)
+ && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0))
+ goto free_and_return;
+ FreeUPNPUrls(urls);
+ }
+ }
+ memset(data, 0, sizeof(struct IGDdatas));
+ }
+ }
+ }
+ state = 0;
+free_and_return:
+ if(desc) {
+ for(i = 0; i < ndev; i++) {
+ if(desc[i].xml) {
+ free(desc[i].xml);
+ }
+ }
+ free(desc);
+ }
+ return state;
+}
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * return value :
+ * 0 - Not ok
+ * 1 - OK */
+int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen)
+{
+ char * descXML;
+ int descXMLsize = 0;
+ descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
+ lanaddr, lanaddrlen, 0);
+ if(descXML) {
+ memset(data, 0, sizeof(struct IGDdatas));
+ memset(urls, 0, sizeof(struct UPNPUrls));
+ parserootdesc(descXML, descXMLsize, data);
+ free(descXML);
+ descXML = NULL;
+ GetUPNPUrls(urls, data, rootdescurl, 0);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
diff --git a/ext/miniupnpc/miniupnpc.def b/ext/miniupnpc/miniupnpc.def
new file mode 100644
index 00000000..60e0bbe4
--- /dev/null
+++ b/ext/miniupnpc/miniupnpc.def
@@ -0,0 +1,45 @@
+LIBRARY
+; miniupnpc library
+ miniupnpc
+
+EXPORTS
+; miniupnpc
+ upnpDiscover
+ freeUPNPDevlist
+ parserootdesc
+ UPNP_GetValidIGD
+ UPNP_GetIGDFromUrl
+ GetUPNPUrls
+ FreeUPNPUrls
+; miniwget
+ miniwget
+ miniwget_getaddr
+; upnpcommands
+ UPNP_GetTotalBytesSent
+ UPNP_GetTotalBytesReceived
+ UPNP_GetTotalPacketsSent
+ UPNP_GetTotalPacketsReceived
+ UPNP_GetStatusInfo
+ UPNP_GetConnectionTypeInfo
+ UPNP_GetExternalIPAddress
+ UPNP_GetLinkLayerMaxBitRates
+ UPNP_AddPortMapping
+ UPNP_AddAnyPortMapping
+ UPNP_DeletePortMapping
+ UPNP_DeletePortMappingRange
+ UPNP_GetPortMappingNumberOfEntries
+ UPNP_GetSpecificPortMappingEntry
+ UPNP_GetGenericPortMappingEntry
+ UPNP_GetListOfPortMappings
+ UPNP_AddPinhole
+ UPNP_CheckPinholeWorking
+ UPNP_UpdatePinhole
+ UPNP_GetPinholePackets
+ UPNP_DeletePinhole
+ UPNP_GetFirewallStatus
+ UPNP_GetOutboundPinholeTimeout
+; upnperrors
+ strupnperror
+; portlistingparse
+ ParsePortListing
+ FreePortListing
diff --git a/ext/miniupnpc/miniupnpc.h b/ext/miniupnpc/miniupnpc.h
new file mode 100644
index 00000000..dfbfa01f
--- /dev/null
+++ b/ext/miniupnpc/miniupnpc.h
@@ -0,0 +1,152 @@
+/* $Id: miniupnpc.h,v 1.48 2015/10/08 16:19:40 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef MINIUPNPC_H_INCLUDED
+#define MINIUPNPC_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "igd_desc_parse.h"
+#include "upnpdev.h"
+
+/* error codes : */
+#define UPNPDISCOVER_SUCCESS (0)
+#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
+#define UPNPDISCOVER_SOCKET_ERROR (-101)
+#define UPNPDISCOVER_MEMORY_ERROR (-102)
+
+/* versions : */
+#define MINIUPNPC_VERSION "1.9.20151026"
+#define MINIUPNPC_API_VERSION 15
+
+/* Source port:
+ Using "1" as an alias for 1900 for backwards compatability
+ (presuming one would have used that for the "sameport" parameter) */
+#define UPNP_LOCAL_PORT_ANY 0
+#define UPNP_LOCAL_PORT_SAME 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structures definitions : */
+struct UPNParg { const char * elt; const char * val; };
+
+char *
+simpleUPnPcommand(int, const char *, const char *,
+ const char *, struct UPNParg *,
+ int *);
+
+/* 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. */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverAll(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes);
+
+/* parserootdesc() :
+ * parse root XML description of a UPnP device and fill the IGDdatas
+ * structure. */
+MINIUPNP_LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *);
+
+/* 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
+ */
+struct UPNPUrls {
+ char * controlURL;
+ char * ipcondescURL;
+ char * controlURL_CIF;
+ char * controlURL_6FC;
+ char * rootdescURL;
+};
+
+/* 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. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * When succeding, urls, data, and lanaddr arguments are set.
+ * return value :
+ * 0 - Not ok
+ * 1 - OK */
+MINIUPNP_LIBSPEC int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+MINIUPNP_LIBSPEC void
+GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *,
+ const char *, unsigned int);
+
+MINIUPNP_LIBSPEC void
+FreeUPNPUrls(struct UPNPUrls *);
+
+/* return 0 or 1 */
+MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/ext/miniupnpc/miniupnpc_declspec.h b/ext/miniupnpc/miniupnpc_declspec.h
new file mode 100644
index 00000000..40adb922
--- /dev/null
+++ b/ext/miniupnpc/miniupnpc_declspec.h
@@ -0,0 +1,21 @@
+#ifndef MINIUPNPC_DECLSPEC_H_INCLUDED
+#define MINIUPNPC_DECLSPEC_H_INCLUDED
+
+#if defined(_WIN32) && !defined(MINIUPNP_STATICLIB)
+ /* for windows dll */
+ #ifdef MINIUPNP_EXPORTS
+ #define MINIUPNP_LIBSPEC __declspec(dllexport)
+ #else
+ #define MINIUPNP_LIBSPEC __declspec(dllimport)
+ #endif
+#else
+ #if defined(__GNUC__) && __GNUC__ >= 4
+ /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */
+ #define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default")))
+ #else
+ #define MINIUPNP_LIBSPEC
+ #endif
+#endif
+
+#endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */
+
diff --git a/ext/miniupnpc/miniupnpcmodule.c b/ext/miniupnpc/miniupnpcmodule.c
new file mode 100644
index 00000000..a5bdce44
--- /dev/null
+++ b/ext/miniupnpc/miniupnpcmodule.c
@@ -0,0 +1,695 @@
+/* $Id: miniupnpcmodule.c,v 1.29 2015/10/26 17:01:30 nanard Exp $*/
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * website : http://miniupnp.tuxfamily.org/
+ * copyright (c) 2007-2014 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#include <Python.h>
+#define MINIUPNP_STATICLIB
+#include "structmember.h"
+#include "miniupnpc.h"
+#include "upnpcommands.h"
+#include "upnperrors.h"
+
+/* for compatibility with Python < 2.4 */
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+#ifndef Py_RETURN_TRUE
+#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
+#endif
+
+#ifndef Py_RETURN_FALSE
+#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
+#endif
+
+/* for compatibility with Python < 3.0 */
+#ifndef PyVarObject_HEAD_INIT
+#define PyVarObject_HEAD_INIT(type, size) \
+ PyObject_HEAD_INIT(type) size,
+#endif
+
+#ifndef Py_TYPE
+#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
+
+typedef struct {
+ PyObject_HEAD
+ /* Type-specific fields go here. */
+ struct UPNPDev * devlist;
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ unsigned int discoverdelay; /* value passed to upnpDiscover() */
+ unsigned int localport; /* value passed to upnpDiscover() */
+ char lanaddr[40]; /* our ip address on the LAN */
+ char * multicastif;
+ char * minissdpdsocket;
+} UPnPObject;
+
+static PyMemberDef UPnP_members[] = {
+ {"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr),
+ READONLY, "ip address on the LAN"
+ },
+ {"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay),
+ 0/*READWRITE*/, "value in ms used to wait for SSDP responses"
+ },
+ {"localport", T_UINT, offsetof(UPnPObject, localport),
+ 0/*READWRITE*/,
+ "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"
+ },
+ /* T_STRING is allways readonly :( */
+ {"multicastif", T_STRING, offsetof(UPnPObject, multicastif),
+ 0, "IP of the network interface to be used for multicast operations"
+ },
+ {"minissdpdsocket", T_STRING, offsetof(UPnPObject, minissdpdsocket),
+ 0, "path of the MiniSSDPd unix socket"
+ },
+ {NULL}
+};
+
+
+static int UPnP_init(UPnPObject *self, PyObject *args, PyObject *kwds)
+{
+ char* multicastif = NULL;
+ char* minissdpdsocket = NULL;
+ static char *kwlist[] = {
+ "multicastif", "minissdpdsocket", "discoverdelay",
+ "localport", NULL
+ };
+
+ if(!PyArg_ParseTupleAndKeywords(args, kwds, "|zzII", kwlist,
+ &multicastif,
+ &minissdpdsocket,
+ &self->discoverdelay,
+ &self->localport))
+ return -1;
+
+ if(self->localport>1 &&
+ (self->localport>65534||self->localport<1024)) {
+ PyErr_SetString(PyExc_Exception, "Invalid localport value");
+ return -1;
+ }
+ if(multicastif)
+ self->multicastif = strdup(multicastif);
+ if(minissdpdsocket)
+ self->minissdpdsocket = strdup(minissdpdsocket);
+
+ return 0;
+}
+
+static void
+UPnPObject_dealloc(UPnPObject *self)
+{
+ freeUPNPDevlist(self->devlist);
+ FreeUPNPUrls(&self->urls);
+ free(self->multicastif);
+ free(self->minissdpdsocket);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static PyObject *
+UPnP_discover(UPnPObject *self)
+{
+ struct UPNPDev * dev;
+ int i;
+ PyObject *res = NULL;
+ if(self->devlist)
+ {
+ freeUPNPDevlist(self->devlist);
+ self->devlist = 0;
+ }
+ Py_BEGIN_ALLOW_THREADS
+ self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/,
+ self->multicastif,
+ self->minissdpdsocket,
+ (int)self->localport,
+ 0/*ip v6*/,
+ 2/* TTL */,
+ 0/*error */);
+ Py_END_ALLOW_THREADS
+ /* Py_RETURN_NONE ??? */
+ for(dev = self->devlist, i = 0; dev; dev = dev->pNext)
+ i++;
+ res = Py_BuildValue("i", i);
+ return res;
+}
+
+static PyObject *
+UPnP_selectigd(UPnPObject *self)
+{
+ int r;
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data,
+ self->lanaddr, sizeof(self->lanaddr));
+Py_END_ALLOW_THREADS
+ if(r)
+ {
+ return Py_BuildValue("s", self->urls.controlURL);
+ }
+ else
+ {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, "No UPnP device discovered");
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_totalbytesent(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_totalbytereceived(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_totalpacketsent(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_totalpacketreceived(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_statusinfo(UPnPObject *self)
+{
+ char status[64];
+ char lastconnerror[64];
+ unsigned int uptime = 0;
+ int r;
+ status[0] = '\0';
+ lastconnerror[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetStatusInfo(self->urls.controlURL, self->data.first.servicetype,
+ status, &uptime, lastconnerror);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror);
+#else
+ return Py_BuildValue("(s,i,s)", status, (int)uptime, lastconnerror);
+#endif
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_connectiontype(UPnPObject *self)
+{
+ char connectionType[64];
+ int r;
+ connectionType[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetConnectionTypeInfo(self->urls.controlURL,
+ self->data.first.servicetype,
+ connectionType);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ return Py_BuildValue("s", connectionType);
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_externalipaddress(UPnPObject *self)
+{
+ char externalIPAddress[40];
+ int r;
+ externalIPAddress[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetExternalIPAddress(self->urls.controlURL,
+ self->data.first.servicetype,
+ externalIPAddress);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ return Py_BuildValue("s", externalIPAddress);
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc,
+ * remoteHost)
+ * protocol is 'UDP' or 'TCP' */
+static PyObject *
+UPnP_addportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ char inPort[6];
+ unsigned short iPort;
+ const char * proto;
+ const char * host;
+ const char * desc;
+ const char * remoteHost;
+ const char * leaseDuration = "0";
+ int r;
+ if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto,
+ &host, &iPort, &desc, &remoteHost))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ sprintf(inPort, "%hu", iPort);
+ r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype,
+ extPort, inPort, host, desc, proto,
+ remoteHost, leaseDuration);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS)
+ {
+ Py_RETURN_TRUE;
+ }
+ else
+ {
+ // TODO: RAISE an Exception. See upnpcommands.h for errors codes.
+ // upnperrors.c
+ //Py_RETURN_FALSE;
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* AddAnyPortMapping(externalPort, protocol, internalHost, internalPort, desc,
+ * remoteHost)
+ * protocol is 'UDP' or 'TCP' */
+static PyObject *
+UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ char inPort[6];
+ unsigned short iPort;
+ char reservedPort[6];
+ const char * proto;
+ const char * host;
+ const char * desc;
+ const char * remoteHost;
+ const char * leaseDuration = "0";
+ int r;
+ if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ sprintf(inPort, "%hu", iPort);
+ r = UPNP_AddAnyPortMapping(self->urls.controlURL, self->data.first.servicetype,
+ extPort, inPort, host, desc, proto,
+ remoteHost, leaseDuration, reservedPort);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ return Py_BuildValue("i", atoi(reservedPort));
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+
+/* DeletePortMapping(extPort, proto, removeHost='')
+ * proto = 'UDP', 'TCP' */
+static PyObject *
+UPnP_deleteportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ const char * proto;
+ const char * remoteHost = "";
+ int r;
+ if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ r = UPNP_DeletePortMapping(self->urls.controlURL, self->data.first.servicetype,
+ extPort, proto, remoteHost);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ Py_RETURN_TRUE;
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* DeletePortMappingRange(extPort, proto, removeHost='')
+ * proto = 'UDP', 'TCP' */
+static PyObject *
+UPnP_deleteportmappingrange(UPnPObject *self, PyObject *args)
+{
+ char extPortStart[6];
+ unsigned short ePortStart;
+ char extPortEnd[6];
+ unsigned short ePortEnd;
+ const char * proto;
+ unsigned char manage;
+ char manageStr[1];
+ int r;
+ if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPortStart, "%hu", ePortStart);
+ sprintf(extPortEnd, "%hu", ePortEnd);
+ sprintf(manageStr, "%hhu", manage);
+ r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype,
+ extPortStart, extPortEnd, proto, manageStr);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ Py_RETURN_TRUE;
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_getportmappingnumberofentries(UPnPObject *self)
+{
+ unsigned int n = 0;
+ int r;
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetPortMappingNumberOfEntries(self->urls.controlURL,
+ self->data.first.servicetype,
+ &n);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", n);
+#else
+ return Py_BuildValue("i", (int)n);
+#endif
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* GetSpecificPortMapping(ePort, proto, remoteHost='')
+ * proto = 'UDP' or 'TCP' */
+static PyObject *
+UPnP_getspecificportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ const char * proto;
+ const char * remoteHost = "";
+ char intClient[40];
+ char intPort[6];
+ unsigned short iPort;
+ char desc[80];
+ char enabled[4];
+ char leaseDuration[16];
+ if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
+ return NULL;
+ extPort[0] = '\0'; intClient[0] = '\0'; intPort[0] = '\0';
+ desc[0] = '\0'; enabled[0] = '\0'; leaseDuration[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ UPNP_GetSpecificPortMappingEntry(self->urls.controlURL,
+ self->data.first.servicetype,
+ extPort, proto, remoteHost,
+ intClient, intPort,
+ desc, enabled, leaseDuration);
+Py_END_ALLOW_THREADS
+ if(intClient[0])
+ {
+ iPort = (unsigned short)atoi(intPort);
+ return Py_BuildValue("(s,H,s,O,i)",
+ intClient, iPort, desc,
+ PyBool_FromLong(atoi(enabled)),
+ atoi(leaseDuration));
+ }
+ else
+ {
+ Py_RETURN_NONE;
+ }
+}
+
+/* GetGenericPortMapping(index) */
+static PyObject *
+UPnP_getgenericportmapping(UPnPObject *self, PyObject *args)
+{
+ int i, r;
+ char index[8];
+ char intClient[40];
+ char intPort[6];
+ unsigned short iPort;
+ char extPort[6];
+ unsigned short ePort;
+ char protocol[4];
+ char desc[80];
+ char enabled[6];
+ char rHost[64];
+ char duration[16]; /* lease duration */
+ unsigned int dur;
+ if(!PyArg_ParseTuple(args, "i", &i))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ snprintf(index, sizeof(index), "%d", i);
+ rHost[0] = '\0'; enabled[0] = '\0';
+ duration[0] = '\0'; desc[0] = '\0';
+ extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
+ r = UPNP_GetGenericPortMappingEntry(self->urls.controlURL,
+ self->data.first.servicetype,
+ index,
+ extPort, intClient, intPort,
+ protocol, desc, enabled, rHost,
+ duration);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS)
+ {
+ ePort = (unsigned short)atoi(extPort);
+ iPort = (unsigned short)atoi(intPort);
+ dur = (unsigned int)strtoul(duration, 0, 0);
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("(H,s,(s,H),s,s,s,I)",
+ ePort, protocol, intClient, iPort,
+ desc, enabled, rHost, dur);
+#else
+ return Py_BuildValue("(i,s,(s,i),s,s,s,i)",
+ (int)ePort, protocol, intClient, (int)iPort,
+ desc, enabled, rHost, (int)dur);
+#endif
+ }
+ else
+ {
+ Py_RETURN_NONE;
+ }
+}
+
+/* miniupnpc.UPnP object Method Table */
+static PyMethodDef UPnP_methods[] = {
+ {"discover", (PyCFunction)UPnP_discover, METH_NOARGS,
+ "discover UPnP IGD devices on the network"
+ },
+ {"selectigd", (PyCFunction)UPnP_selectigd, METH_NOARGS,
+ "select a valid UPnP IGD among discovered devices"
+ },
+ {"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS,
+ "return the total number of bytes sent by UPnP IGD"
+ },
+ {"totalbytereceived", (PyCFunction)UPnP_totalbytereceived, METH_NOARGS,
+ "return the total number of bytes received by UPnP IGD"
+ },
+ {"totalpacketsent", (PyCFunction)UPnP_totalpacketsent, METH_NOARGS,
+ "return the total number of packets sent by UPnP IGD"
+ },
+ {"totalpacketreceived", (PyCFunction)UPnP_totalpacketreceived, METH_NOARGS,
+ "return the total number of packets received by UPnP IGD"
+ },
+ {"statusinfo", (PyCFunction)UPnP_statusinfo, METH_NOARGS,
+ "return status and uptime"
+ },
+ {"connectiontype", (PyCFunction)UPnP_connectiontype, METH_NOARGS,
+ "return IGD WAN connection type"
+ },
+ {"externalipaddress", (PyCFunction)UPnP_externalipaddress, METH_NOARGS,
+ "return external IP address"
+ },
+ {"addportmapping", (PyCFunction)UPnP_addportmapping, METH_VARARGS,
+ "add a port mapping"
+ },
+ {"addanyportmapping", (PyCFunction)UPnP_addanyportmapping, METH_VARARGS,
+ "add a port mapping, IGD to select alternative if necessary"
+ },
+ {"deleteportmapping", (PyCFunction)UPnP_deleteportmapping, METH_VARARGS,
+ "delete a port mapping"
+ },
+ {"deleteportmappingrange", (PyCFunction)UPnP_deleteportmappingrange, METH_VARARGS,
+ "delete a range of port mappings"
+ },
+ {"getportmappingnumberofentries", (PyCFunction)UPnP_getportmappingnumberofentries, METH_NOARGS,
+ "-- non standard --"
+ },
+ {"getspecificportmapping", (PyCFunction)UPnP_getspecificportmapping, METH_VARARGS,
+ "get details about a specific port mapping entry"
+ },
+ {"getgenericportmapping", (PyCFunction)UPnP_getgenericportmapping, METH_VARARGS,
+ "get all details about the port mapping at index"
+ },
+ {NULL} /* Sentinel */
+};
+
+static PyTypeObject UPnPType = {
+ PyVarObject_HEAD_INIT(NULL,
+ 0) /*ob_size*/
+ "miniupnpc.UPnP", /*tp_name*/
+ sizeof(UPnPObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)UPnPObject_dealloc,/*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "UPnP objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ UPnP_methods, /* tp_methods */
+ UPnP_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)UPnP_init, /* tp_init */
+ 0, /* tp_alloc */
+#ifndef _WIN32
+ PyType_GenericNew,/*UPnP_new,*/ /* tp_new */
+#else
+ 0,
+#endif
+};
+
+/* module methods */
+static PyMethodDef miniupnpc_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "miniupnpc", /* m_name */
+ "miniupnpc module.", /* m_doc */
+ -1, /* m_size */
+ miniupnpc_methods, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+#endif
+
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+
+PyMODINIT_FUNC
+#if PY_MAJOR_VERSION >= 3
+PyInit_miniupnpc(void)
+#else
+initminiupnpc(void)
+#endif
+{
+ PyObject* m;
+
+#ifdef _WIN32
+ UPnPType.tp_new = PyType_GenericNew;
+#endif
+ if (PyType_Ready(&UPnPType) < 0)
+#if PY_MAJOR_VERSION >= 3
+ return 0;
+#else
+ return;
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+ m = PyModule_Create(&moduledef);
+#else
+ m = Py_InitModule3("miniupnpc", miniupnpc_methods,
+ "miniupnpc module.");
+#endif
+
+ Py_INCREF(&UPnPType);
+ PyModule_AddObject(m, "UPnP", (PyObject *)&UPnPType);
+
+#if PY_MAJOR_VERSION >= 3
+ return m;
+#endif
+}
+
diff --git a/ext/miniupnpc/miniupnpcstrings.h.cmake b/ext/miniupnpc/miniupnpcstrings.h.cmake
new file mode 100644
index 00000000..78c8fe9c
--- /dev/null
+++ b/ext/miniupnpc/miniupnpcstrings.h.cmake
@@ -0,0 +1,15 @@
+#ifndef MINIUPNPCSTRINGS_H_INCLUDED
+#define MINIUPNPCSTRINGS_H_INCLUDED
+
+#define OS_STRING "${CMAKE_SYSTEM_NAME}"
+#define MINIUPNPC_VERSION_STRING "${MINIUPNPC_VERSION}"
+
+#if 0
+/* according to "UPnP Device Architecture 1.0" */
+#define UPNP_VERSION_STRING "UPnP/1.0"
+#else
+/* according to "UPnP Device Architecture 1.1" */
+#define UPNP_VERSION_STRING "UPnP/1.1"
+#endif
+
+#endif
diff --git a/ext/miniupnpc/miniupnpcstrings.h.in b/ext/miniupnpc/miniupnpcstrings.h.in
new file mode 100644
index 00000000..68bf4293
--- /dev/null
+++ b/ext/miniupnpc/miniupnpcstrings.h.in
@@ -0,0 +1,23 @@
+/* $Id: miniupnpcstrings.h.in,v 1.6 2014/11/04 22:31:55 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2014 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef MINIUPNPCSTRINGS_H_INCLUDED
+#define MINIUPNPCSTRINGS_H_INCLUDED
+
+#define OS_STRING "OS/version"
+#define MINIUPNPC_VERSION_STRING "version"
+
+#if 0
+/* according to "UPnP Device Architecture 1.0" */
+#define UPNP_VERSION_STRING "UPnP/1.0"
+#else
+/* according to "UPnP Device Architecture 1.1" */
+#define UPNP_VERSION_STRING "UPnP/1.1"
+#endif
+
+#endif
+
diff --git a/ext/miniupnpc/miniupnpctypes.h b/ext/miniupnpc/miniupnpctypes.h
new file mode 100644
index 00000000..591c32fb
--- /dev/null
+++ b/ext/miniupnpc/miniupnpctypes.h
@@ -0,0 +1,19 @@
+/* $Id: miniupnpctypes.h,v 1.2 2012/09/27 15:42:10 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org
+ * Author : Thomas Bernard
+ * Copyright (c) 2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef MINIUPNPCTYPES_H_INCLUDED
+#define MINIUPNPCTYPES_H_INCLUDED
+
+#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+#define UNSIGNED_INTEGER unsigned long long
+#define STRTOUI strtoull
+#else
+#define UNSIGNED_INTEGER unsigned int
+#define STRTOUI strtoul
+#endif
+
+#endif
+
diff --git a/ext/miniupnpc/miniwget.c b/ext/miniupnpc/miniwget.c
new file mode 100644
index 00000000..40aa7180
--- /dev/null
+++ b/ext/miniupnpc/miniwget.c
@@ -0,0 +1,626 @@
+/* $Id: miniwget.c,v 1.72 2015/10/26 17:05:08 nanard Exp $ */
+/* Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define MAXHOSTNAMELEN 64
+#define snprintf _snprintf
+#define socklen_t int
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#else /* #ifdef _WIN32 */
+#include <unistd.h>
+#include <sys/param.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+#define socklen_t int
+#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/select.h>
+#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netdb.h>
+#define closesocket close
+#include <strings.h>
+#endif /* #else _WIN32 */
+#ifdef __GNU__
+#define MAXHOSTNAMELEN 64
+#endif /* __GNU__ */
+
+#ifndef MIN
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#endif /* MIN */
+
+
+#include "miniupnpcstrings.h"
+#include "miniwget.h"
+#include "connecthostport.h"
+#include "receivedata.h"
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+/*
+ * Read a HTTP response from a socket.
+ * Process Content-Length and Transfer-encoding headers.
+ * return a pointer to the content buffer, which length is saved
+ * to the length parameter.
+ */
+void *
+getHTTPResponse(int s, int * size)
+{
+ char buf[2048];
+ int n;
+ int endofheaders = 0;
+ int chunked = 0;
+ int content_length = -1;
+ unsigned int chunksize = 0;
+ unsigned int bytestocopy = 0;
+ /* buffers : */
+ char * header_buf;
+ unsigned int header_buf_len = 2048;
+ unsigned int header_buf_used = 0;
+ char * content_buf;
+ unsigned int content_buf_len = 2048;
+ unsigned int content_buf_used = 0;
+ char chunksize_buf[32];
+ unsigned int chunksize_buf_index;
+
+ header_buf = malloc(header_buf_len);
+ if(header_buf == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
+#endif /* DEBUG */
+ *size = -1;
+ return NULL;
+ }
+ content_buf = malloc(content_buf_len);
+ if(content_buf == NULL)
+ {
+ free(header_buf);
+#ifdef DEBUG
+ fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
+#endif /* DEBUG */
+ *size = -1;
+ return NULL;
+ }
+ chunksize_buf[0] = '\0';
+ chunksize_buf_index = 0;
+
+ while((n = receivedata(s, buf, 2048, 5000, NULL)) > 0)
+ {
+ if(endofheaders == 0)
+ {
+ int i;
+ int linestart=0;
+ int colon=0;
+ int valuestart=0;
+ if(header_buf_used + n > header_buf_len) {
+ char * tmp = realloc(header_buf, header_buf_used + n);
+ if(tmp == NULL) {
+ /* memory allocation error */
+ free(header_buf);
+ free(content_buf);
+ *size = -1;
+ return NULL;
+ }
+ header_buf = tmp;
+ header_buf_len = header_buf_used + n;
+ }
+ memcpy(header_buf + header_buf_used, buf, n);
+ header_buf_used += n;
+ /* search for CR LF CR LF (end of headers)
+ * recognize also LF LF */
+ i = 0;
+ while(i < ((int)header_buf_used-1) && (endofheaders == 0)) {
+ if(header_buf[i] == '\r') {
+ i++;
+ if(header_buf[i] == '\n') {
+ i++;
+ if(i < (int)header_buf_used && header_buf[i] == '\r') {
+ i++;
+ if(i < (int)header_buf_used && header_buf[i] == '\n') {
+ endofheaders = i+1;
+ }
+ }
+ }
+ } else if(header_buf[i] == '\n') {
+ i++;
+ if(header_buf[i] == '\n') {
+ endofheaders = i+1;
+ }
+ }
+ i++;
+ }
+ if(endofheaders == 0)
+ continue;
+ /* parse header lines */
+ for(i = 0; i < endofheaders - 1; i++) {
+ if(colon <= linestart && header_buf[i]==':')
+ {
+ colon = i;
+ while(i < (endofheaders-1)
+ && (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
+ i++;
+ valuestart = i + 1;
+ }
+ /* detecting end of line */
+ else if(header_buf[i]=='\r' || header_buf[i]=='\n')
+ {
+ if(colon > linestart && valuestart > colon)
+ {
+#ifdef DEBUG
+ printf("header='%.*s', value='%.*s'\n",
+ colon-linestart, header_buf+linestart,
+ i-valuestart, header_buf+valuestart);
+#endif
+ if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
+ {
+ content_length = atoi(header_buf+valuestart);
+#ifdef DEBUG
+ printf("Content-Length: %d\n", content_length);
+#endif
+ }
+ else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
+ && 0==strncasecmp(header_buf+valuestart, "chunked", 7))
+ {
+#ifdef DEBUG
+ printf("chunked transfer-encoding!\n");
+#endif
+ chunked = 1;
+ }
+ }
+ while((i < (int)header_buf_used) && (header_buf[i]=='\r' || header_buf[i] == '\n'))
+ i++;
+ linestart = i;
+ colon = linestart;
+ valuestart = 0;
+ }
+ }
+ /* copy the remaining of the received data back to buf */
+ n = header_buf_used - endofheaders;
+ memcpy(buf, header_buf + endofheaders, n);
+ /* if(headers) */
+ }
+ if(endofheaders)
+ {
+ /* content */
+ if(chunked)
+ {
+ int i = 0;
+ while(i < n)
+ {
+ if(chunksize == 0)
+ {
+ /* reading chunk size */
+ if(chunksize_buf_index == 0) {
+ /* skipping any leading CR LF */
+ if(i<n && buf[i] == '\r') i++;
+ if(i<n && buf[i] == '\n') i++;
+ }
+ while(i<n && isxdigit(buf[i])
+ && chunksize_buf_index < (sizeof(chunksize_buf)-1))
+ {
+ chunksize_buf[chunksize_buf_index++] = buf[i];
+ chunksize_buf[chunksize_buf_index] = '\0';
+ i++;
+ }
+ while(i<n && buf[i] != '\r' && buf[i] != '\n')
+ i++; /* discarding chunk-extension */
+ if(i<n && buf[i] == '\r') i++;
+ if(i<n && buf[i] == '\n') {
+ unsigned int j;
+ for(j = 0; j < chunksize_buf_index; j++) {
+ if(chunksize_buf[j] >= '0'
+ && chunksize_buf[j] <= '9')
+ chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
+ else
+ chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
+ }
+ chunksize_buf[0] = '\0';
+ chunksize_buf_index = 0;
+ i++;
+ } else {
+ /* not finished to get chunksize */
+ continue;
+ }
+#ifdef DEBUG
+ printf("chunksize = %u (%x)\n", chunksize, chunksize);
+#endif
+ if(chunksize == 0)
+ {
+#ifdef DEBUG
+ printf("end of HTTP content - %d %d\n", i, n);
+ /*printf("'%.*s'\n", n-i, buf+i);*/
+#endif
+ goto end_of_stream;
+ }
+ }
+ bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i);
+ if((content_buf_used + bytestocopy) > content_buf_len)
+ {
+ char * tmp;
+ if(content_length >= (int)(content_buf_used + bytestocopy)) {
+ content_buf_len = content_length;
+ } else {
+ content_buf_len = content_buf_used + bytestocopy;
+ }
+ tmp = realloc(content_buf, content_buf_len);
+ if(tmp == NULL) {
+ /* memory allocation error */
+ free(content_buf);
+ free(header_buf);
+ *size = -1;
+ return NULL;
+ }
+ content_buf = tmp;
+ }
+ memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
+ content_buf_used += bytestocopy;
+ i += bytestocopy;
+ chunksize -= bytestocopy;
+ }
+ }
+ else
+ {
+ /* not chunked */
+ if(content_length > 0
+ && (int)(content_buf_used + n) > content_length) {
+ /* skipping additional bytes */
+ n = content_length - content_buf_used;
+ }
+ if(content_buf_used + n > content_buf_len)
+ {
+ char * tmp;
+ if(content_length >= (int)(content_buf_used + n)) {
+ content_buf_len = content_length;
+ } else {
+ content_buf_len = content_buf_used + n;
+ }
+ tmp = realloc(content_buf, content_buf_len);
+ if(tmp == NULL) {
+ /* memory allocation error */
+ free(content_buf);
+ free(header_buf);
+ *size = -1;
+ return NULL;
+ }
+ content_buf = tmp;
+ }
+ memcpy(content_buf + content_buf_used, buf, n);
+ content_buf_used += n;
+ }
+ }
+ /* use the Content-Length header value if available */
+ if(content_length > 0 && (int)content_buf_used >= content_length)
+ {
+#ifdef DEBUG
+ printf("End of HTTP content\n");
+#endif
+ break;
+ }
+ }
+end_of_stream:
+ free(header_buf); header_buf = NULL;
+ *size = content_buf_used;
+ if(content_buf_used == 0)
+ {
+ free(content_buf);
+ content_buf = NULL;
+ }
+ return content_buf;
+}
+
+/* miniwget3() :
+ * do all the work.
+ * Return NULL if something failed. */
+static void *
+miniwget3(const char * host,
+ unsigned short port, const char * path,
+ int * size, char * addr_str, int addr_str_len,
+ const char * httpversion, unsigned int scope_id)
+{
+ char buf[2048];
+ int s;
+ int n;
+ int len;
+ int sent;
+ void * content;
+
+ *size = 0;
+ s = connecthostport(host, port, scope_id);
+ if(s < 0)
+ return NULL;
+
+ /* get address for caller ! */
+ if(addr_str)
+ {
+ struct sockaddr_storage saddr;
+ socklen_t saddrlen;
+
+ saddrlen = sizeof(saddr);
+ if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
+ {
+ perror("getsockname");
+ }
+ else
+ {
+#if defined(__amigaos__) && !defined(__amigaos4__)
+ /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
+ * But his function make a string with the port : nn.nn.nn.nn:port */
+/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
+ NULL, addr_str, (DWORD *)&addr_str_len))
+ {
+ printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
+ }*/
+ /* the following code is only compatible with ip v4 addresses */
+ strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
+#else
+#if 0
+ if(saddr.sa_family == AF_INET6) {
+ inet_ntop(AF_INET6,
+ &(((struct sockaddr_in6 *)&saddr)->sin6_addr),
+ addr_str, addr_str_len);
+ } else {
+ inet_ntop(AF_INET,
+ &(((struct sockaddr_in *)&saddr)->sin_addr),
+ addr_str, addr_str_len);
+ }
+#endif
+ /* getnameinfo return ip v6 address with the scope identifier
+ * such as : 2a01:e35:8b2b:7330::%4281128194 */
+ n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
+ addr_str, addr_str_len,
+ NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if(n != 0) {
+#ifdef _WIN32
+ fprintf(stderr, "getnameinfo() failed : %d\n", n);
+#else
+ fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
+#endif
+ }
+#endif
+ }
+#ifdef DEBUG
+ printf("address miniwget : %s\n", addr_str);
+#endif
+ }
+
+ len = snprintf(buf, sizeof(buf),
+ "GET %s HTTP/%s\r\n"
+ "Host: %s:%d\r\n"
+ "Connection: Close\r\n"
+ "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
+
+ "\r\n",
+ path, httpversion, host, port);
+ if ((unsigned int)len >= sizeof(buf))
+ {
+ closesocket(s);
+ return NULL;
+ }
+ sent = 0;
+ /* sending the HTTP request */
+ while(sent < len)
+ {
+ n = send(s, buf+sent, len-sent, 0);
+ if(n < 0)
+ {
+ perror("send");
+ closesocket(s);
+ return NULL;
+ }
+ else
+ {
+ sent += n;
+ }
+ }
+ content = getHTTPResponse(s, size);
+ closesocket(s);
+ return content;
+}
+
+/* miniwget2() :
+ * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
+static void *
+miniwget2(const char * host,
+ unsigned short port, const char * path,
+ int * size, char * addr_str, int addr_str_len,
+ unsigned int scope_id)
+{
+ char * respbuffer;
+
+#if 1
+ respbuffer = miniwget3(host, port, path, size,
+ addr_str, addr_str_len, "1.1", scope_id);
+#else
+ respbuffer = miniwget3(host, port, path, size,
+ addr_str, addr_str_len, "1.0", scope_id);
+ if (*size == 0)
+ {
+#ifdef DEBUG
+ printf("Retrying with HTTP/1.1\n");
+#endif
+ free(respbuffer);
+ respbuffer = miniwget3(host, port, path, size,
+ addr_str, addr_str_len, "1.1", scope_id);
+ }
+#endif
+ return respbuffer;
+}
+
+
+
+
+/* parseURL()
+ * arguments :
+ * url : source string not modified
+ * hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
+ * port : port (destination)
+ * path : pointer to the path part of the URL
+ *
+ * Return values :
+ * 0 - Failure
+ * 1 - Success */
+int
+parseURL(const char * url,
+ char * hostname, unsigned short * port,
+ char * * path, unsigned int * scope_id)
+{
+ char * p1, *p2, *p3;
+ if(!url)
+ return 0;
+ p1 = strstr(url, "://");
+ if(!p1)
+ return 0;
+ p1 += 3;
+ if( (url[0]!='h') || (url[1]!='t')
+ ||(url[2]!='t') || (url[3]!='p'))
+ return 0;
+ memset(hostname, 0, MAXHOSTNAMELEN + 1);
+ if(*p1 == '[')
+ {
+ /* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
+ char * scope;
+ scope = strchr(p1, '%');
+ p2 = strchr(p1, ']');
+ if(p2 && scope && scope < p2 && scope_id) {
+ /* parse scope */
+#ifdef IF_NAMESIZE
+ char tmp[IF_NAMESIZE];
+ int l;
+ scope++;
+ /* "%25" is just '%' in URL encoding */
+ if(scope[0] == '2' && scope[1] == '5')
+ scope += 2; /* skip "25" */
+ l = p2 - scope;
+ if(l >= IF_NAMESIZE)
+ l = IF_NAMESIZE - 1;
+ memcpy(tmp, scope, l);
+ tmp[l] = '\0';
+ *scope_id = if_nametoindex(tmp);
+ if(*scope_id == 0) {
+ *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
+ }
+#else
+ /* under windows, scope is numerical */
+ char tmp[8];
+ int l;
+ scope++;
+ /* "%25" is just '%' in URL encoding */
+ if(scope[0] == '2' && scope[1] == '5')
+ scope += 2; /* skip "25" */
+ l = p2 - scope;
+ if(l >= sizeof(tmp))
+ l = sizeof(tmp) - 1;
+ memcpy(tmp, scope, l);
+ tmp[l] = '\0';
+ *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
+#endif
+ }
+ p3 = strchr(p1, '/');
+ if(p2 && p3)
+ {
+ p2++;
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
+ if(*p2 == ':')
+ {
+ *port = 0;
+ p2++;
+ while( (*p2 >= '0') && (*p2 <= '9'))
+ {
+ *port *= 10;
+ *port += (unsigned short)(*p2 - '0');
+ p2++;
+ }
+ }
+ else
+ {
+ *port = 80;
+ }
+ *path = p3;
+ return 1;
+ }
+ }
+ p2 = strchr(p1, ':');
+ p3 = strchr(p1, '/');
+ if(!p3)
+ return 0;
+ if(!p2 || (p2>p3))
+ {
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
+ *port = 80;
+ }
+ else
+ {
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
+ *port = 0;
+ p2++;
+ while( (*p2 >= '0') && (*p2 <= '9'))
+ {
+ *port *= 10;
+ *port += (unsigned short)(*p2 - '0');
+ p2++;
+ }
+ }
+ *path = p3;
+ return 1;
+}
+
+void *
+miniwget(const char * url, int * size, unsigned int scope_id)
+{
+ unsigned short port;
+ char * path;
+ /* protocol://host:port/chemin */
+ char hostname[MAXHOSTNAMELEN+1];
+ *size = 0;
+ if(!parseURL(url, hostname, &port, &path, &scope_id))
+ return NULL;
+#ifdef DEBUG
+ printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
+ hostname, port, path, scope_id);
+#endif
+ return miniwget2(hostname, port, path, size, 0, 0, scope_id);
+}
+
+void *
+miniwget_getaddr(const char * url, int * size,
+ char * addr, int addrlen, unsigned int scope_id)
+{
+ unsigned short port;
+ char * path;
+ /* protocol://host:port/path */
+ char hostname[MAXHOSTNAMELEN+1];
+ *size = 0;
+ if(addr)
+ addr[0] = '\0';
+ if(!parseURL(url, hostname, &port, &path, &scope_id))
+ return NULL;
+#ifdef DEBUG
+ printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
+ hostname, port, path, scope_id);
+#endif
+ return miniwget2(hostname, port, path, size, addr, addrlen, scope_id);
+}
+
diff --git a/ext/miniupnpc/miniwget.h b/ext/miniupnpc/miniwget.h
new file mode 100644
index 00000000..d6db71a8
--- /dev/null
+++ b/ext/miniupnpc/miniwget.h
@@ -0,0 +1,30 @@
+/* $Id: miniwget.h,v 1.10 2015/07/21 13:16:55 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef MINIWGET_H_INCLUDED
+#define MINIWGET_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MINIUPNP_LIBSPEC void * getHTTPResponse(int s, int * size);
+
+MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int);
+
+MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int);
+
+int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/ext/miniupnpc/minixml.c b/ext/miniupnpc/minixml.c
new file mode 100644
index 00000000..3e201ec2
--- /dev/null
+++ b/ext/miniupnpc/minixml.c
@@ -0,0 +1,229 @@
+/* $Id: minixml.c,v 1.11 2014/02/03 15:54:12 nanard Exp $ */
+/* minixml.c : the minimum size a xml parser can be ! */
+/* Project : miniupnp
+ * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author : Thomas Bernard
+
+Copyright (c) 2005-2014, Thomas BERNARD
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <string.h>
+#include "minixml.h"
+
+/* parseatt : used to parse the argument list
+ * return 0 (false) in case of success and -1 (true) if the end
+ * of the xmlbuffer is reached. */
+static int parseatt(struct xmlparser * p)
+{
+ const char * attname;
+ int attnamelen;
+ const char * attvalue;
+ int attvaluelen;
+ while(p->xml < p->xmlend)
+ {
+ if(*p->xml=='/' || *p->xml=='>')
+ return 0;
+ if( !IS_WHITE_SPACE(*p->xml) )
+ {
+ char sep;
+ attname = p->xml;
+ attnamelen = 0;
+ while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
+ {
+ attnamelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ while(*(p->xml++) != '=')
+ {
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ while(IS_WHITE_SPACE(*p->xml))
+ {
+ p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ sep = *p->xml;
+ if(sep=='\'' || sep=='\"')
+ {
+ p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ attvalue = p->xml;
+ attvaluelen = 0;
+ while(*p->xml != sep)
+ {
+ attvaluelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ }
+ else
+ {
+ attvalue = p->xml;
+ attvaluelen = 0;
+ while( !IS_WHITE_SPACE(*p->xml)
+ && *p->xml != '>' && *p->xml != '/')
+ {
+ attvaluelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ }
+ /*printf("%.*s='%.*s'\n",
+ attnamelen, attname, attvaluelen, attvalue);*/
+ if(p->attfunc)
+ p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
+ }
+ p->xml++;
+ }
+ return -1;
+}
+
+/* parseelt parse the xml stream and
+ * call the callback functions when needed... */
+static void parseelt(struct xmlparser * p)
+{
+ int i;
+ const char * elementname;
+ while(p->xml < (p->xmlend - 1))
+ {
+ if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "<!--", 4)))
+ {
+ p->xml += 3;
+ /* ignore comments */
+ do
+ {
+ p->xml++;
+ if ((p->xml + 3) >= p->xmlend)
+ return;
+ }
+ while(memcmp(p->xml, "-->", 3) != 0);
+ p->xml += 3;
+ }
+ else if((p->xml)[0]=='<' && (p->xml)[1]!='?')
+ {
+ i = 0; elementname = ++p->xml;
+ while( !IS_WHITE_SPACE(*p->xml)
+ && (*p->xml!='>') && (*p->xml!='/')
+ )
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ /* to ignore namespace : */
+ if(*p->xml==':')
+ {
+ i = 0;
+ elementname = ++p->xml;
+ }
+ }
+ if(i>0)
+ {
+ if(p->starteltfunc)
+ p->starteltfunc(p->data, elementname, i);
+ if(parseatt(p))
+ return;
+ if(*p->xml!='/')
+ {
+ const char * data;
+ i = 0; data = ++p->xml;
+ if (p->xml >= p->xmlend)
+ return;
+ while( IS_WHITE_SPACE(*p->xml) )
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ if(memcmp(p->xml, "<![CDATA[", 9) == 0)
+ {
+ /* CDATA handling */
+ p->xml += 9;
+ data = p->xml;
+ i = 0;
+ while(memcmp(p->xml, "]]>", 3) != 0)
+ {
+ i++; p->xml++;
+ if ((p->xml + 3) >= p->xmlend)
+ return;
+ }
+ if(i>0 && p->datafunc)
+ p->datafunc(p->data, data, i);
+ while(*p->xml!='<')
+ {
+ p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ }
+ else
+ {
+ while(*p->xml!='<')
+ {
+ i++; p->xml++;
+ if ((p->xml + 1) >= p->xmlend)
+ return;
+ }
+ if(i>0 && p->datafunc && *(p->xml + 1) == '/')
+ p->datafunc(p->data, data, i);
+ }
+ }
+ }
+ else if(*p->xml == '/')
+ {
+ i = 0; elementname = ++p->xml;
+ if (p->xml >= p->xmlend)
+ return;
+ while((*p->xml != '>'))
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ if(p->endeltfunc)
+ p->endeltfunc(p->data, elementname, i);
+ p->xml++;
+ }
+ }
+ else
+ {
+ p->xml++;
+ }
+ }
+}
+
+/* the parser must be initialized before calling this function */
+void parsexml(struct xmlparser * parser)
+{
+ parser->xml = parser->xmlstart;
+ parser->xmlend = parser->xmlstart + parser->xmlsize;
+ parseelt(parser);
+}
+
+
diff --git a/ext/miniupnpc/minixml.h b/ext/miniupnpc/minixml.h
new file mode 100644
index 00000000..9f43aa48
--- /dev/null
+++ b/ext/miniupnpc/minixml.h
@@ -0,0 +1,37 @@
+/* $Id: minixml.h,v 1.7 2012/09/27 15:42:10 nanard Exp $ */
+/* minimal xml parser
+ *
+ * Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef MINIXML_H_INCLUDED
+#define MINIXML_H_INCLUDED
+#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
+
+/* if a callback function pointer is set to NULL,
+ * the function is not called */
+struct xmlparser {
+ const char *xmlstart;
+ const char *xmlend;
+ const char *xml; /* pointer to current character */
+ int xmlsize;
+ void * data;
+ void (*starteltfunc) (void *, const char *, int);
+ void (*endeltfunc) (void *, const char *, int);
+ void (*datafunc) (void *, const char *, int);
+ void (*attfunc) (void *, const char *, int, const char *, int);
+};
+
+/* parsexml()
+ * the xmlparser structure must be initialized before the call
+ * the following structure members have to be initialized :
+ * xmlstart, xmlsize, data, *func
+ * xml is for internal usage, xmlend is computed automatically */
+void parsexml(struct xmlparser *);
+
+#endif
+
diff --git a/ext/miniupnpc/minixmlvalid.c b/ext/miniupnpc/minixmlvalid.c
new file mode 100644
index 00000000..dad14881
--- /dev/null
+++ b/ext/miniupnpc/minixmlvalid.c
@@ -0,0 +1,163 @@
+/* $Id: minixmlvalid.c,v 1.7 2015/07/15 12:41:15 nanard Exp $ */
+/* MiniUPnP Project
+ * http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
+ * minixmlvalid.c :
+ * validation program for the minixml parser
+ *
+ * (c) 2006-2011 Thomas Bernard */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "minixml.h"
+
+/* xml event structure */
+struct event {
+ enum { ELTSTART, ELTEND, ATT, CHARDATA } type;
+ const char * data;
+ int len;
+};
+
+struct eventlist {
+ int n;
+ struct event * events;
+};
+
+/* compare 2 xml event lists
+ * return 0 if the two lists are equals */
+int evtlistcmp(struct eventlist * a, struct eventlist * b)
+{
+ int i;
+ struct event * ae, * be;
+ if(a->n != b->n)
+ {
+ printf("event number not matching : %d != %d\n", a->n, b->n);
+ /*return 1;*/
+ }
+ for(i=0; i<a->n; i++)
+ {
+ ae = a->events + i;
+ be = b->events + i;
+ if( (ae->type != be->type)
+ ||(ae->len != be->len)
+ ||memcmp(ae->data, be->data, ae->len))
+ {
+ printf("Found a difference : %d '%.*s' != %d '%.*s'\n",
+ ae->type, ae->len, ae->data,
+ be->type, be->len, be->data);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Test data */
+static const char xmldata[] =
+"<xmlroot>\n"
+" <elt1 att1=\"attvalue1\" att2=\"attvalue2\">"
+"character data"
+"</elt1> \n \t"
+"<elt1b/>"
+"<elt1>\n<![CDATA[ <html>stuff !\n ]]> \n</elt1>\n"
+"<elt2a> \t<elt2b>chardata1</elt2b><elt2b> chardata2 </elt2b></elt2a>"
+"</xmlroot>";
+
+static const struct event evtref[] =
+{
+ {ELTSTART, "xmlroot", 7},
+ {ELTSTART, "elt1", 4},
+ /* attributes */
+ {CHARDATA, "character data", 14},
+ {ELTEND, "elt1", 4},
+ {ELTSTART, "elt1b", 5},
+ {ELTSTART, "elt1", 4},
+ {CHARDATA, " <html>stuff !\n ", 16},
+ {ELTEND, "elt1", 4},
+ {ELTSTART, "elt2a", 5},
+ {ELTSTART, "elt2b", 5},
+ {CHARDATA, "chardata1", 9},
+ {ELTEND, "elt2b", 5},
+ {ELTSTART, "elt2b", 5},
+ {CHARDATA, " chardata2 ", 11},
+ {ELTEND, "elt2b", 5},
+ {ELTEND, "elt2a", 5},
+ {ELTEND, "xmlroot", 7}
+};
+
+void startelt(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("startelt : %.*s\n", l, p);*/
+ evt->type = ELTSTART;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+void endelt(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("endelt : %.*s\n", l, p);*/
+ evt->type = ELTEND;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+void chardata(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("chardata : '%.*s'\n", l, p);*/
+ evt->type = CHARDATA;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+int testxmlparser(const char * xml, int size)
+{
+ int r;
+ struct eventlist evtlist;
+ struct eventlist evtlistref;
+ struct xmlparser parser;
+ evtlist.n = 0;
+ evtlist.events = malloc(sizeof(struct event)*100);
+ if(evtlist.events == NULL)
+ {
+ fprintf(stderr, "Memory allocation error.\n");
+ return -1;
+ }
+ memset(&parser, 0, sizeof(parser));
+ parser.xmlstart = xml;
+ parser.xmlsize = size;
+ parser.data = &evtlist;
+ parser.starteltfunc = startelt;
+ parser.endeltfunc = endelt;
+ parser.datafunc = chardata;
+ parsexml(&parser);
+ printf("%d events\n", evtlist.n);
+ /* compare */
+ evtlistref.n = sizeof(evtref)/sizeof(struct event);
+ evtlistref.events = (struct event *)evtref;
+ r = evtlistcmp(&evtlistref, &evtlist);
+ free(evtlist.events);
+ return r;
+}
+
+int main(int argc, char * * argv)
+{
+ int r;
+ (void)argc; (void)argv;
+
+ r = testxmlparser(xmldata, sizeof(xmldata)-1);
+ if(r)
+ printf("minixml validation test failed\n");
+ return r;
+}
+
diff --git a/ext/miniupnpc/msvc/miniupnpc.sln b/ext/miniupnpc/msvc/miniupnpc.sln
new file mode 100644
index 00000000..b3da1919
--- /dev/null
+++ b/ext/miniupnpc/msvc/miniupnpc.sln
@@ -0,0 +1,29 @@
+ï»ż
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual C++ Express 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc", "miniupnpc.vcproj", "{D28CE435-CB33-4BAE-8A52-C6EF915956F5}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "upnpc-static", "upnpc-static.vcproj", "{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D28CE435-CB33-4BAE-8A52-C6EF915956F5} = {D28CE435-CB33-4BAE-8A52-C6EF915956F5}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.Build.0 = Debug|Win32
+ {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.ActiveCfg = Release|Win32
+ {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.Build.0 = Release|Win32
+ {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.ActiveCfg = Debug|Win32
+ {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.Build.0 = Debug|Win32
+ {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.ActiveCfg = Release|Win32
+ {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/ext/miniupnpc/msvc/miniupnpc.vcproj b/ext/miniupnpc/msvc/miniupnpc.vcproj
new file mode 100644
index 00000000..fb301e3b
--- /dev/null
+++ b/ext/miniupnpc/msvc/miniupnpc.vcproj
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="miniupnpc"
+ ProjectGUID="{D28CE435-CB33-4BAE-8A52-C6EF915956F5}"
+ RootNamespace="miniupnpc"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;DEBUG"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ PreprocessorDefinitions="_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Fichiers sources"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\connecthostport.c"
+ >
+ </File>
+ <File
+ RelativePath="..\igd_desc_parse.c"
+ >
+ </File>
+ <File
+ RelativePath="..\minisoap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\minissdpc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\miniupnpc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\miniwget.c"
+ >
+ </File>
+ <File
+ RelativePath="..\minixml.c"
+ >
+ </File>
+ <File
+ RelativePath="..\portlistingparse.c"
+ >
+ </File>
+ <File
+ RelativePath="..\receivedata.c"
+ >
+ </File>
+ <File
+ RelativePath="..\upnpcommands.c"
+ >
+ </File>
+ <File
+ RelativePath="..\upnpdev.c"
+ >
+ </File>
+ <File
+ RelativePath="..\upnperrors.c"
+ >
+ </File>
+ <File
+ RelativePath="..\upnpreplyparse.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Fichiers d&apos;en-tête"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\connecthostport.h"
+ >
+ </File>
+ <File
+ RelativePath="..\declspec.h"
+ >
+ </File>
+ <File
+ RelativePath="..\igd_desc_parse.h"
+ >
+ </File>
+ <File
+ RelativePath="..\minisoap.h"
+ >
+ </File>
+ <File
+ RelativePath="..\minissdpc.h"
+ >
+ </File>
+ <File
+ RelativePath="..\miniupnpc.h"
+ >
+ </File>
+ <File
+ RelativePath="..\miniupnpcstrings.h"
+ >
+ </File>
+ <File
+ RelativePath="..\miniupnpctypes.h"
+ >
+ </File>
+ <File
+ RelativePath="..\miniwget.h"
+ >
+ </File>
+ <File
+ RelativePath="..\minixml.h"
+ >
+ </File>
+ <File
+ RelativePath="..\portlistingparse.h"
+ >
+ </File>
+ <File
+ RelativePath="..\receivedata.h"
+ >
+ </File>
+ <File
+ RelativePath="..\upnpcommands.h"
+ >
+ </File>
+ <File
+ RelativePath="..\upnpdev.h"
+ >
+ </File>
+ <File
+ RelativePath="..\upnperrors.h"
+ >
+ </File>
+ <File
+ RelativePath="..\upnpreplyparse.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Fichiers de ressources"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/ext/miniupnpc/msvc/upnpc-static.vcproj b/ext/miniupnpc/msvc/upnpc-static.vcproj
new file mode 100644
index 00000000..c88c9a6e
--- /dev/null
+++ b/ext/miniupnpc/msvc/upnpc-static.vcproj
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="upnpc-static"
+ ProjectGUID="{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}"
+ RootNamespace="upnpcstatic"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="_DEBUG;_CONSOLE;MINIUPNP_STATICLIB;DEBUG;_CRT_SECURE_NO_WARNINGS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib IPHlpApi.Lib Debug\miniupnpc.lib"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ PreprocessorDefinitions="NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib IPHlpApi.Lib Release\miniupnpc.lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Fichiers sources"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\upnpc.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Fichiers d&apos;en-tête"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Fichiers de ressources"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/ext/miniupnpc/portlistingparse.c b/ext/miniupnpc/portlistingparse.c
new file mode 100644
index 00000000..0e092780
--- /dev/null
+++ b/ext/miniupnpc/portlistingparse.c
@@ -0,0 +1,172 @@
+/* $Id: portlistingparse.c,v 1.9 2015/07/15 12:41:13 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2011-2015 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#include <string.h>
+#include <stdlib.h>
+#ifdef DEBUG
+#include <stdio.h>
+#endif /* DEBUG */
+#include "portlistingparse.h"
+#include "minixml.h"
+
+/* list of the elements */
+static const struct {
+ const portMappingElt code;
+ const char * const str;
+} elements[] = {
+ { PortMappingEntry, "PortMappingEntry"},
+ { NewRemoteHost, "NewRemoteHost"},
+ { NewExternalPort, "NewExternalPort"},
+ { NewProtocol, "NewProtocol"},
+ { NewInternalPort, "NewInternalPort"},
+ { NewInternalClient, "NewInternalClient"},
+ { NewEnabled, "NewEnabled"},
+ { NewDescription, "NewDescription"},
+ { NewLeaseTime, "NewLeaseTime"},
+ { PortMappingEltNone, NULL}
+};
+
+/* Helper function */
+static UNSIGNED_INTEGER
+atoui(const char * p, int l)
+{
+ UNSIGNED_INTEGER r = 0;
+ while(l > 0 && *p)
+ {
+ if(*p >= '0' && *p <= '9')
+ r = r*10 + (*p - '0');
+ else
+ break;
+ p++;
+ l--;
+ }
+ return r;
+}
+
+/* Start element handler */
+static void
+startelt(void * d, const char * name, int l)
+{
+ int i;
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ pdata->curelt = PortMappingEltNone;
+ for(i = 0; elements[i].str; i++)
+ {
+ if(memcmp(name, elements[i].str, l) == 0)
+ {
+ pdata->curelt = elements[i].code;
+ break;
+ }
+ }
+ if(pdata->curelt == PortMappingEntry)
+ {
+ struct PortMapping * pm;
+ pm = calloc(1, sizeof(struct PortMapping));
+ if(pm == NULL)
+ {
+ /* malloc error */
+#ifdef DEBUG
+ fprintf(stderr, "%s: error allocating memory",
+ "startelt");
+#endif /* DEBUG */
+ return;
+ }
+ pm->l_next = pdata->l_head; /* insert in list */
+ pdata->l_head = pm;
+ }
+}
+
+/* End element handler */
+static void
+endelt(void * d, const char * name, int l)
+{
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ (void)name;
+ (void)l;
+ pdata->curelt = PortMappingEltNone;
+}
+
+/* Data handler */
+static void
+data(void * d, const char * data, int l)
+{
+ struct PortMapping * pm;
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ pm = pdata->l_head;
+ if(!pm)
+ return;
+ if(l > 63)
+ l = 63;
+ switch(pdata->curelt)
+ {
+ case NewRemoteHost:
+ memcpy(pm->remoteHost, data, l);
+ pm->remoteHost[l] = '\0';
+ break;
+ case NewExternalPort:
+ pm->externalPort = (unsigned short)atoui(data, l);
+ break;
+ case NewProtocol:
+ if(l > 3)
+ l = 3;
+ memcpy(pm->protocol, data, l);
+ pm->protocol[l] = '\0';
+ break;
+ case NewInternalPort:
+ pm->internalPort = (unsigned short)atoui(data, l);
+ break;
+ case NewInternalClient:
+ memcpy(pm->internalClient, data, l);
+ pm->internalClient[l] = '\0';
+ break;
+ case NewEnabled:
+ pm->enabled = (unsigned char)atoui(data, l);
+ break;
+ case NewDescription:
+ memcpy(pm->description, data, l);
+ pm->description[l] = '\0';
+ break;
+ case NewLeaseTime:
+ pm->leaseTime = atoui(data, l);
+ break;
+ default:
+ break;
+ }
+}
+
+
+/* Parse the PortMappingList XML document for IGD version 2
+ */
+void
+ParsePortListing(const char * buffer, int bufsize,
+ struct PortMappingParserData * pdata)
+{
+ struct xmlparser parser;
+
+ memset(pdata, 0, sizeof(struct PortMappingParserData));
+ /* init xmlparser */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = pdata;
+ parser.starteltfunc = startelt;
+ parser.endeltfunc = endelt;
+ parser.datafunc = data;
+ parser.attfunc = 0;
+ parsexml(&parser);
+}
+
+void
+FreePortListing(struct PortMappingParserData * pdata)
+{
+ struct PortMapping * pm;
+ while((pm = pdata->l_head) != NULL)
+ {
+ /* remove from list */
+ pdata->l_head = pm->l_next;
+ free(pm);
+ }
+}
+
diff --git a/ext/miniupnpc/portlistingparse.h b/ext/miniupnpc/portlistingparse.h
new file mode 100644
index 00000000..661ad1fa
--- /dev/null
+++ b/ext/miniupnpc/portlistingparse.h
@@ -0,0 +1,65 @@
+/* $Id: portlistingparse.h,v 1.11 2015/07/21 13:16:55 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2011-2015 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#ifndef PORTLISTINGPARSE_H_INCLUDED
+#define PORTLISTINGPARSE_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+/* for the definition of UNSIGNED_INTEGER */
+#include "miniupnpctypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* sample of PortMappingEntry :
+ <p:PortMappingEntry>
+ <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
+ <p:NewExternalPort>2345</p:NewExternalPort>
+ <p:NewProtocol>TCP</p:NewProtocol>
+ <p:NewInternalPort>2345</p:NewInternalPort>
+ <p:NewInternalClient>192.168.1.137</p:NewInternalClient>
+ <p:NewEnabled>1</p:NewEnabled>
+ <p:NewDescription>dooom</p:NewDescription>
+ <p:NewLeaseTime>345</p:NewLeaseTime>
+ </p:PortMappingEntry>
+ */
+typedef enum { PortMappingEltNone,
+ PortMappingEntry, NewRemoteHost,
+ NewExternalPort, NewProtocol,
+ NewInternalPort, NewInternalClient,
+ NewEnabled, NewDescription,
+ NewLeaseTime } portMappingElt;
+
+struct PortMapping {
+ struct PortMapping * l_next; /* list next element */
+ UNSIGNED_INTEGER leaseTime;
+ unsigned short externalPort;
+ unsigned short internalPort;
+ char remoteHost[64];
+ char internalClient[64];
+ char description[64];
+ char protocol[4];
+ unsigned char enabled;
+};
+
+struct PortMappingParserData {
+ struct PortMapping * l_head; /* list head */
+ portMappingElt curelt;
+};
+
+MINIUPNP_LIBSPEC void
+ParsePortListing(const char * buffer, int bufsize,
+ struct PortMappingParserData * pdata);
+
+MINIUPNP_LIBSPEC void
+FreePortListing(struct PortMappingParserData * pdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/ext/miniupnpc/pymoduletest.py b/ext/miniupnpc/pymoduletest.py
new file mode 100644
index 00000000..9fddd9c2
--- /dev/null
+++ b/ext/miniupnpc/pymoduletest.py
@@ -0,0 +1,88 @@
+#! /usr/bin/python
+# vim: tabstop=2 shiftwidth=2 expandtab
+# MiniUPnP project
+# Author : Thomas Bernard
+# This Sample code is public domain.
+# website : http://miniupnp.tuxfamily.org/
+
+# import the python miniupnpc module
+import miniupnpc
+import sys
+
+try:
+ import argparse
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-m', '--multicastif')
+ parser.add_argument('-p', '--minissdpdsocket')
+ parser.add_argument('-d', '--discoverdelay', type=int, default=200)
+ parser.add_argument('-z', '--localport', type=int, default=0)
+ # create the object
+ u = miniupnpc.UPnP(**vars(parser.parse_args()))
+except:
+ print 'argparse not available'
+ i = 1
+ multicastif = None
+ minissdpdsocket = None
+ discoverdelay = 200
+ localport = 0
+ while i < len(sys.argv):
+ print sys.argv[i]
+ if sys.argv[i] == '-m' or sys.argv[i] == '--multicastif':
+ multicastif = sys.argv[i+1]
+ elif sys.argv[i] == '-p' or sys.argv[i] == '--minissdpdsocket':
+ minissdpdsocket = sys.argv[i+1]
+ elif sys.argv[i] == '-d' or sys.argv[i] == '--discoverdelay':
+ discoverdelay = int(sys.argv[i+1])
+ elif sys.argv[i] == '-z' or sys.argv[i] == '--localport':
+ localport = int(sys.argv[i+1])
+ else:
+ raise Exception('invalid argument %s' % sys.argv[i])
+ i += 2
+ # create the object
+ u = miniupnpc.UPnP(multicastif, minissdpdsocket, discoverdelay, localport)
+
+print 'inital(default) values :'
+print ' discoverdelay', u.discoverdelay
+print ' lanaddr', u.lanaddr
+print ' multicastif', u.multicastif
+print ' minissdpdsocket', u.minissdpdsocket
+#u.minissdpdsocket = '../minissdpd/minissdpd.sock'
+# discovery process, it usualy takes several seconds (2 seconds or more)
+print 'Discovering... delay=%ums' % u.discoverdelay
+print u.discover(), 'device(s) detected'
+# select an igd
+try:
+ u.selectigd()
+except Exception, e:
+ print 'Exception :', e
+ sys.exit(1)
+# display information about the IGD and the internet connection
+print 'local ip address :', u.lanaddr
+print 'external ip address :', u.externalipaddress()
+print u.statusinfo(), u.connectiontype()
+print 'total bytes : sent', u.totalbytesent(), 'received', u.totalbytereceived()
+print 'total packets : sent', u.totalpacketsent(), 'received', u.totalpacketreceived()
+
+#print u.addportmapping(64000, 'TCP',
+# '192.168.1.166', 63000, 'port mapping test', '')
+#print u.deleteportmapping(64000, 'TCP')
+
+port = 0
+proto = 'UDP'
+# list the redirections :
+i = 0
+while True:
+ p = u.getgenericportmapping(i)
+ if p==None:
+ break
+ print i, p
+ (port, proto, (ihost,iport), desc, c, d, e) = p
+ #print port, desc
+ i = i + 1
+
+print u.getspecificportmapping(port, proto)
+try:
+ print u.getportmappingnumberofentries()
+except Exception, e:
+ print 'GetPortMappingNumberOfEntries() is not supported :', e
+
diff --git a/ext/miniupnpc/receivedata.c b/ext/miniupnpc/receivedata.c
new file mode 100644
index 00000000..fb05e09d
--- /dev/null
+++ b/ext/miniupnpc/receivedata.c
@@ -0,0 +1,105 @@
+/* $Id: receivedata.c,v 1.6 2014/11/13 13:51:52 nanard Exp $ */
+/* Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2011-2014 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else /* _WIN32 */
+#include <unistd.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+#define socklen_t int
+#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/select.h>
+#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#if !defined(__amigaos__) && !defined(__amigaos4__)
+#include <poll.h>
+#endif /* !defined(__amigaos__) && !defined(__amigaos4__) */
+#include <errno.h>
+#define MINIUPNPC_IGNORE_EINTR
+#endif /* _WIN32 */
+
+#ifdef _WIN32
+#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#include "receivedata.h"
+
+int
+receivedata(int socket,
+ char * data, int length,
+ int timeout, unsigned int * scope_id)
+{
+#if MINIUPNPC_GET_SRC_ADDR
+ struct sockaddr_storage src_addr;
+ socklen_t src_addr_len = sizeof(src_addr);
+#endif /* MINIUPNPC_GET_SRC_ADDR */
+ int n;
+#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+ /* using poll */
+ struct pollfd fds[1]; /* for the poll */
+#ifdef MINIUPNPC_IGNORE_EINTR
+ do {
+#endif /* MINIUPNPC_IGNORE_EINTR */
+ fds[0].fd = socket;
+ fds[0].events = POLLIN;
+ n = poll(fds, 1, timeout);
+#ifdef MINIUPNPC_IGNORE_EINTR
+ } while(n < 0 && errno == EINTR);
+#endif /* MINIUPNPC_IGNORE_EINTR */
+ if(n < 0) {
+ PRINT_SOCKET_ERROR("poll");
+ return -1;
+ } else if(n == 0) {
+ /* timeout */
+ return 0;
+ }
+#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+ /* using select under _WIN32 and amigaos */
+ fd_set socketSet;
+ TIMEVAL timeval;
+ FD_ZERO(&socketSet);
+ FD_SET(socket, &socketSet);
+ timeval.tv_sec = timeout / 1000;
+ timeval.tv_usec = (timeout % 1000) * 1000;
+ n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
+ if(n < 0) {
+ PRINT_SOCKET_ERROR("select");
+ return -1;
+ } else if(n == 0) {
+ return 0;
+ }
+#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+#if MINIUPNPC_GET_SRC_ADDR
+ memset(&src_addr, 0, sizeof(src_addr));
+ n = recvfrom(socket, data, length, 0,
+ (struct sockaddr *)&src_addr, &src_addr_len);
+#else /* MINIUPNPC_GET_SRC_ADDR */
+ n = recv(socket, data, length, 0);
+#endif /* MINIUPNPC_GET_SRC_ADDR */
+ if(n<0) {
+ PRINT_SOCKET_ERROR("recv");
+ }
+#if MINIUPNPC_GET_SRC_ADDR
+ if (src_addr.ss_family == AF_INET6) {
+ const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr;
+#ifdef DEBUG
+ printf("scope_id=%u\n", src_addr6->sin6_scope_id);
+#endif /* DEBUG */
+ if(scope_id)
+ *scope_id = src_addr6->sin6_scope_id;
+ }
+#endif /* MINIUPNPC_GET_SRC_ADDR */
+ return n;
+}
+
diff --git a/ext/miniupnpc/receivedata.h b/ext/miniupnpc/receivedata.h
new file mode 100644
index 00000000..0520a11d
--- /dev/null
+++ b/ext/miniupnpc/receivedata.h
@@ -0,0 +1,19 @@
+/* $Id: receivedata.h,v 1.4 2012/09/27 15:42:10 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2011-2012 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef RECEIVEDATA_H_INCLUDED
+#define RECEIVEDATA_H_INCLUDED
+
+/* Reads data from the specified socket.
+ * Returns the number of bytes read if successful, zero if no bytes were
+ * read or if we timed out. Returns negative if there was an error. */
+int receivedata(int socket,
+ char * data, int length,
+ int timeout, unsigned int * scope_id);
+
+#endif
+
diff --git a/ext/miniupnpc/setup.py b/ext/miniupnpc/setup.py
new file mode 100644
index 00000000..97e42bf1
--- /dev/null
+++ b/ext/miniupnpc/setup.py
@@ -0,0 +1,28 @@
+#! /usr/bin/python
+# vim: tabstop=8 shiftwidth=8 expandtab
+# $Id: setup.py,v 1.12 2015/10/26 17:03:17 nanard Exp $
+# the MiniUPnP Project (c) 2007-2014 Thomas Bernard
+# http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
+#
+# python script to build the miniupnpc module under unix
+#
+# replace libminiupnpc.a by libminiupnpc.so for shared library usage
+try:
+ from setuptools import setup, Extension
+except ImportError:
+ from distutils.core import setup, Extension
+from distutils import sysconfig
+sysconfig.get_config_vars()["OPT"] = ''
+sysconfig.get_config_vars()["CFLAGS"] = ''
+setup(name="miniupnpc",
+ version=open('VERSION').read().strip(),
+ author='Thomas BERNARD',
+ author_email='miniupnp@free.fr',
+ license=open('LICENSE').read(),
+ url='http://miniupnp.free.fr/',
+ description='miniUPnP client',
+ ext_modules=[
+ Extension(name="miniupnpc", sources=["miniupnpcmodule.c"],
+ extra_objects=["libminiupnpc.a"])
+ ])
+
diff --git a/ext/miniupnpc/setupmingw32.py b/ext/miniupnpc/setupmingw32.py
new file mode 100644
index 00000000..43dfb465
--- /dev/null
+++ b/ext/miniupnpc/setupmingw32.py
@@ -0,0 +1,28 @@
+#! /usr/bin/python
+# vim: tabstop=8 shiftwidth=8 expandtab
+# $Id: setupmingw32.py,v 1.10 2015/10/26 17:03:17 nanard Exp $
+# the MiniUPnP Project (c) 2007-2014 Thomas Bernard
+# http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
+#
+# python script to build the miniupnpc module under windows (using mingw32)
+#
+try:
+ from setuptools import setup, Extension
+except ImportError:
+ from distutils.core import setup, Extension
+from distutils import sysconfig
+sysconfig.get_config_vars()["OPT"] = ''
+sysconfig.get_config_vars()["CFLAGS"] = ''
+setup(name="miniupnpc",
+ version=open('VERSION').read().strip(),
+ author='Thomas BERNARD',
+ author_email='miniupnp@free.fr',
+ license=open('LICENSE').read(),
+ url='http://miniupnp.free.fr/',
+ description='miniUPnP client',
+ ext_modules=[
+ Extension(name="miniupnpc", sources=["miniupnpcmodule.c"],
+ libraries=["ws2_32", "iphlpapi"],
+ extra_objects=["libminiupnpc.a"])
+ ])
+
diff --git a/ext/miniupnpc/testdesc/linksys_WAG200G_desc.values b/ext/miniupnpc/testdesc/linksys_WAG200G_desc.values
new file mode 100644
index 00000000..cf422218
--- /dev/null
+++ b/ext/miniupnpc/testdesc/linksys_WAG200G_desc.values
@@ -0,0 +1,14 @@
+# values for linksys_WAG200G_desc.xml
+
+CIF:
+ servicetype = urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1
+ controlurl = /upnp/control/WANCommonIFC1
+ eventsuburl = /upnp/event/WANCommonIFC1
+ scpdurl = /cmnicfg.xml
+
+first:
+ servicetype = urn:schemas-upnp-org:service:WANPPPConnection:1
+ controlurl = /upnp/control/WANPPPConn1
+ eventsuburl = /upnp/event/WANPPPConn1
+ scpdurl = /pppcfg.xml
+
diff --git a/ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml b/ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml
new file mode 100644
index 00000000..d428d73b
--- /dev/null
+++ b/ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+<specVersion>
+<major>1</major>
+<minor>0</minor>
+</specVersion>
+<URLBase>http://192.168.1.1:49152</URLBase>
+<device>
+<deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType>
+<friendlyName>LINKSYS WAG200G Gateway</friendlyName>
+<manufacturer>LINKSYS</manufacturer>
+<manufacturerURL>http://www.linksys.com</manufacturerURL>
+<modelDescription>LINKSYS WAG200G Gateway</modelDescription>
+<modelName>Wireless-G ADSL Home Gateway</modelName>
+<modelNumber>WAG200G</modelNumber>
+<modelURL>http://www.linksys.com</modelURL>
+<serialNumber>123456789</serialNumber>
+<UDN>uuid:8ca2eb37-1dd2-11b2-86f1-001a709b5aa8</UDN>
+<UPC>WAG200G</UPC>
+<serviceList>
+<service>
+<serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>
+<serviceId>urn:upnp-org:serviceId:L3Forwarding1</serviceId>
+<controlURL>/upnp/control/L3Forwarding1</controlURL>
+<eventSubURL>/upnp/event/L3Forwarding1</eventSubURL>
+<SCPDURL>/l3frwd.xml</SCPDURL>
+</service>
+</serviceList>
+<deviceList>
+<device>
+<deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType>
+<friendlyName>WANDevice</friendlyName>
+<manufacturer>LINKSYS</manufacturer>
+<manufacturerURL>http://www.linksys.com/</manufacturerURL>
+<modelDescription>Residential Gateway</modelDescription>
+<modelName>Internet Connection Sharing</modelName>
+<modelNumber>1</modelNumber>
+<modelURL>http://www.linksys.com/</modelURL>
+<serialNumber>0000001</serialNumber>
+<UDN>uuid:8ca2eb36-1dd2-11b2-86f1-001a709b5aa8</UDN>
+<UPC>WAG200G</UPC>
+<serviceList>
+<service>
+<serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
+<serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId>
+<controlURL>/upnp/control/WANCommonIFC1</controlURL>
+<eventSubURL>/upnp/event/WANCommonIFC1</eventSubURL>
+<SCPDURL>/cmnicfg.xml</SCPDURL>
+</service>
+</serviceList>
+<deviceList>
+<device>
+<deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType>
+<friendlyName>WANConnectionDevice</friendlyName>
+<manufacturer>LINKSYS</manufacturer>
+<manufacturerURL>http://www.linksys.com/</manufacturerURL>
+<modelDescription>Residential Gateway</modelDescription>
+<modelName>Internet Connection Sharing</modelName>
+<modelNumber>1</modelNumber>
+<modelURL>http://www.linksys.com/</modelURL>
+<serialNumber>0000001</serialNumber>
+<UDN>uuid:8ca2eb37-1dd2-11b2-86f0-001a709b5aa8</UDN>
+<UPC>WAG200G</UPC>
+<serviceList>
+<service>
+<serviceType>urn:schemas-upnp-org:service:WANEthernetLinkConfig:1</serviceType>
+<serviceId>urn:upnp-org:serviceId:WANEthLinkC1</serviceId>
+<controlURL>/upnp/control/WANEthLinkC1</controlURL>
+<eventSubURL>/upnp/event/WANEthLinkC1</eventSubURL>
+<SCPDURL>/wanelcfg.xml</SCPDURL>
+</service>
+<service>
+<serviceType>urn:schemas-upnp-org:service:WANPPPConnection:1</serviceType>
+<serviceId>urn:upnp-org:serviceId:WANPPPConn1</serviceId>
+<controlURL>/upnp/control/WANPPPConn1</controlURL>
+<eventSubURL>/upnp/event/WANPPPConn1</eventSubURL>
+<SCPDURL>/pppcfg.xml</SCPDURL>
+</service>
+</serviceList>
+</device>
+</deviceList>
+</device>
+<device>
+<deviceType>urn:schemas-upnp-org:device:LANDevice:1</deviceType>
+<friendlyName>LANDevice</friendlyName>
+<manufacturer>LINKSYS</manufacturer>
+<manufacturerURL>http://www.linksys.com/</manufacturerURL>
+<modelDescription>Residential Gateway</modelDescription>
+<modelName>Residential Gateway</modelName>
+<modelNumber>1</modelNumber>
+<modelURL>http://www.linksys.com/</modelURL>
+<serialNumber>0000001</serialNumber>
+<UDN>uuid:8ca2eb36-1dd2-11b2-86f0-001a709b5aa
+8</UDN>
+<UPC>WAG200G</UPC>
+<serviceList>
+<service>
+<serviceType>urn:schemas-upnp-org:service:LANHostConfigManagement:1</serviceType>
+<serviceId>urn:upnp-org:serviceId:LANHostCfg1</serviceId>
+<controlURL>/upnp/control/LANHostCfg1</controlURL>
+<eventSubURL>/upnp/event/LANHostCfg1</eventSubURL>
+<SCPDURL>/lanhostc.xml</SCPDURL>
+</service>
+</serviceList>
+</device>
+</deviceList>
+<presentationURL>http://192.168.1.1/index.htm</presentationURL>
+</device>
+</root>
+
diff --git a/ext/miniupnpc/testdesc/new_LiveBox_desc.values b/ext/miniupnpc/testdesc/new_LiveBox_desc.values
new file mode 100644
index 00000000..c55552e5
--- /dev/null
+++ b/ext/miniupnpc/testdesc/new_LiveBox_desc.values
@@ -0,0 +1,20 @@
+# values for new_LiveBox_desc.xml
+
+CIF:
+ servicetype = urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1
+ controlurl = /87895a19/upnp/control/WANCommonIFC1
+ eventsuburl = /87895a19/upnp/control/WANCommonIFC1
+ scpdurl = /87895a19/gateicfgSCPD.xml
+
+first:
+ servicetype = urn:schemas-upnp-org:service:WANPPPConnection:2
+ controlurl = /87895a19/upnp/control/WANIPConn1
+ eventsuburl = /87895a19/upnp/control/WANIPConn1
+ scpdurl = /87895a19/gateconnSCPD_PPP.xml
+
+IPv6FC:
+ servicetype = urn:schemas-upnp-org:service:WANIPv6FirewallControl:1
+ controlurl = /87895a19/upnp/control/WANIPv6FwCtrl1
+ eventsuburl = /87895a19/upnp/control/WANIPv6FwCtrl1
+ scpdurl = /87895a19/wanipv6fwctrlSCPD.xml
+
diff --git a/ext/miniupnpc/testdesc/new_LiveBox_desc.xml b/ext/miniupnpc/testdesc/new_LiveBox_desc.xml
new file mode 100644
index 00000000..620eb55a
--- /dev/null
+++ b/ext/miniupnpc/testdesc/new_LiveBox_desc.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+ <specVersion>
+ <major>1</major>
+ <minor>0</minor>
+ </specVersion>
+ <device>
+ <pnpx:X_hardwareId xmlns:pnpx="http://schemas.microsoft.com/windows/pnpx/2005/11">VEN_0129&amp;DEV_0000&amp;SUBSYS_03&amp;REV_250417</pnpx:X_hardwareId>
+ <pnpx:X_compatibleId xmlns:pnpx="http://schemas.microsoft.com/windows/pnpx/2005/11">GenericUmPass</pnpx:X_compatibleId>
+ <pnpx:X_deviceCategory xmlns:pnpx="http://schemas.microsoft.com/windows/pnpx/2005/11">NetworkInfrastructure.Gateway</pnpx:X_deviceCategory>
+ <df:X_deviceCategory xmlns:df="http://schemas.microsoft.com/windows/2008/09/devicefoundation">Network.Gateway</df:X_deviceCategory>
+ <deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:2</deviceType>
+ <friendlyName>Orange Livebox</friendlyName>
+ <manufacturer>Sagemcom</manufacturer>
+ <manufacturerURL>http://www.sagemcom.com/</manufacturerURL>
+ <modelName>Residential Livebox,(DSL,WAN Ethernet)</modelName>
+ <UDN>uuid:87895a19-50f9-3736-a87f-115c230155f8</UDN>
+ <modelDescription>Sagemcom,fr,SG30_sip-fr-4.28.35.1</modelDescription>
+ <modelNumber>3</modelNumber>
+ <serialNumber>LK14129DP441489</serialNumber>
+ <presentationURL>http://192.168.1.1</presentationURL>
+ <UPC></UPC>
+ <iconList>
+ <icon>
+ <mimetype>image/png</mimetype>
+ <width>16</width>
+ <height>16</height>
+ <depth>8</depth>
+ <url>/87895a19/ligd.png</url>
+ </icon>
+ </iconList>
+ <deviceList>
+ <device>
+ <deviceType>urn:schemas-upnp-org:device:WANDevice:2</deviceType>
+ <friendlyName>WANDevice</friendlyName>
+ <manufacturer>Sagemcom</manufacturer>
+ <manufacturerURL>http://www.sagemcom.com/</manufacturerURL>
+ <modelDescription>WAN Device on Sagemcom,fr,SG30_sip-fr-4.28.35.1</modelDescription>
+ <modelName>Residential Livebox,(DSL,WAN Ethernet)</modelName>
+ <modelNumber>3</modelNumber>
+ <modelURL>http://www.sagemcom.com/</modelURL>
+ <serialNumber>LK14129DP441489</serialNumber>
+ <presentationURL>http://192.168.1.1</presentationURL>
+ <UDN>uuid:e2397374-53d8-3fc6-8306-593ba1a34625</UDN>
+ <UPC></UPC>
+ <serviceList>
+ <service>
+ <serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
+ <serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId>
+ <controlURL>/87895a19/upnp/control/WANCommonIFC1</controlURL>
+ <eventSubURL>/87895a19/upnp/control/WANCommonIFC1</eventSubURL>
+ <SCPDURL>/87895a19/gateicfgSCPD.xml</SCPDURL>
+ </service>
+ </serviceList>
+ <deviceList>
+ <device>
+ <deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:2</deviceType>
+ <friendlyName>WANConnectionDevice</friendlyName>
+ <manufacturer>Sagemcom</manufacturer>
+ <manufacturerURL>http://www.sagemcom.com/</manufacturerURL>
+ <modelDescription>WanConnectionDevice on Sagemcom,fr,SG30_sip-fr-4.28.35.1</modelDescription>
+ <modelName>Residential Livebox,(DSL,WAN Ethernet)</modelName>
+ <modelNumber>3</modelNumber>
+ <modelURL>http://www.sagemcom.com/</modelURL>
+ <serialNumber>LK14129DP441489</serialNumber>
+ <presentationURL>http://192.168.1.1</presentationURL>
+ <UDN>uuid:44598a08-288e-32c9-8a4d-d3c008ede331</UDN>
+ <UPC></UPC>
+ <serviceList>
+ <service>
+ <serviceType>urn:schemas-upnp-org:service:WANPPPConnection:2</serviceType>
+ <serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId>
+ <controlURL>/87895a19/upnp/control/WANIPConn1</controlURL>
+ <eventSubURL>/87895a19/upnp/control/WANIPConn1</eventSubURL>
+ <SCPDURL>/87895a19/gateconnSCPD_PPP.xml</SCPDURL>
+ </service>
+ <service>
+ <serviceType>urn:schemas-upnp-org:service:WANIPv6FirewallControl:1</serviceType>
+ <serviceId>urn:upnp-org:serviceId:WANIPv6FwCtrl1</serviceId>
+ <controlURL>/87895a19/upnp/control/WANIPv6FwCtrl1</controlURL>
+ <eventSubURL>/87895a19/upnp/control/WANIPv6FwCtrl1</eventSubURL>
+ <SCPDURL>/87895a19/wanipv6fwctrlSCPD.xml</SCPDURL>
+ </service>
+ </serviceList>
+ </device>
+ </deviceList>
+ </device>
+ </deviceList>
+ </device>
+</root> \ No newline at end of file
diff --git a/ext/miniupnpc/testigddescparse.c b/ext/miniupnpc/testigddescparse.c
new file mode 100644
index 00000000..c1907fd0
--- /dev/null
+++ b/ext/miniupnpc/testigddescparse.c
@@ -0,0 +1,187 @@
+/* $Id: testigddescparse.c,v 1.10 2015/08/06 09:55:24 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2008-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "igd_desc_parse.h"
+#include "minixml.h"
+#include "miniupnpc.h"
+
+/* count number of differences */
+int compare_service(struct IGDdatas_service * s, FILE * f)
+{
+ int n = 0;
+ char line[1024];
+
+ while(fgets(line, sizeof(line), f)) {
+ char * value;
+ char * equal;
+ char * name;
+ char * parsedvalue;
+ int l;
+ l = strlen(line);
+ while((l > 0) && ((line[l-1] == '\r') || (line[l-1] == '\n') || (line[l-1] == ' ')))
+ line[--l] = '\0';
+ if(l == 0)
+ break; /* end on blank line */
+ if(line[0] == '#')
+ continue; /* skip comments */
+ equal = strchr(line, '=');
+ if(equal == NULL) {
+ fprintf(stderr, "Warning, line does not contain '=' : %s\n", line);
+ continue;
+ }
+ *equal = '\0';
+ name = line;
+ while(*name == ' ' || *name == '\t')
+ name++;
+ l = strlen(name);
+ while((l > 0) && (name[l-1] == ' ' || name[l-1] == '\t'))
+ name[--l] = '\0';
+ value = equal + 1;
+ while(*value == ' ' || *value == '\t')
+ value++;
+ if(strcmp(name, "controlurl") == 0)
+ parsedvalue = s->controlurl;
+ else if(strcmp(name, "eventsuburl") == 0)
+ parsedvalue = s->eventsuburl;
+ else if(strcmp(name, "scpdurl") == 0)
+ parsedvalue = s->scpdurl;
+ else if(strcmp(name, "servicetype") == 0)
+ parsedvalue = s->servicetype;
+ else {
+ fprintf(stderr, "unknown field '%s'\n", name);
+ continue;
+ }
+ if(0 != strcmp(parsedvalue, value)) {
+ fprintf(stderr, "difference : '%s' != '%s'\n", parsedvalue, value);
+ n++;
+ }
+ }
+ return n;
+}
+
+int compare_igd(struct IGDdatas * p, FILE * f)
+{
+ int n = 0;
+ char line[1024];
+ struct IGDdatas_service * s;
+
+ while(fgets(line, sizeof(line), f)) {
+ char * colon;
+ int l = (int)strlen(line);
+ while((l > 0) && (line[l-1] == '\r' || (line[l-1] == '\n')))
+ line[--l] = '\0';
+ if(l == 0 || line[0] == '#')
+ continue; /* skip blank lines and comments */
+ colon = strchr(line, ':');
+ if(colon == NULL) {
+ fprintf(stderr, "Warning, no ':' : %s\n", line);
+ continue;
+ }
+ s = NULL;
+ *colon = '\0';
+ if(strcmp(line, "CIF") == 0)
+ s = &p->CIF;
+ else if(strcmp(line, "first") == 0)
+ s = &p->first;
+ else if(strcmp(line, "second") == 0)
+ s = &p->second;
+ else if(strcmp(line, "IPv6FC") == 0)
+ s = &p->IPv6FC;
+ else {
+ s = NULL;
+ fprintf(stderr, "*** unknown service '%s' ***\n", line);
+ n++;
+ continue;
+ }
+ n += compare_service(s, f);
+ }
+ if(n > 0)
+ fprintf(stderr, "*** %d difference%s ***\n", n, (n > 1) ? "s" : "");
+ return n;
+}
+
+int test_igd_desc_parse(char * buffer, int len, FILE * f)
+{
+ int n;
+ struct IGDdatas igd;
+ struct xmlparser parser;
+ struct UPNPUrls urls;
+
+ memset(&igd, 0, sizeof(struct IGDdatas));
+ memset(&parser, 0, sizeof(struct xmlparser));
+ parser.xmlstart = buffer;
+ parser.xmlsize = len;
+ parser.data = &igd;
+ parser.starteltfunc = IGDstartelt;
+ parser.endeltfunc = IGDendelt;
+ parser.datafunc = IGDdata;
+ parsexml(&parser);
+#ifdef DEBUG
+ printIGD(&igd);
+#endif /* DEBUG */
+ GetUPNPUrls(&urls, &igd, "http://fake/desc/url/file.xml", 0);
+ printf("ipcondescURL='%s'\n", urls.ipcondescURL);
+ printf("controlURL='%s'\n", urls.controlURL);
+ printf("controlURL_CIF='%s'\n", urls.controlURL_CIF);
+ n = f ? compare_igd(&igd, f) : 0;
+ FreeUPNPUrls(&urls);
+ return n;
+}
+
+int main(int argc, char * * argv)
+{
+ FILE * f;
+ char * buffer;
+ int len;
+ int r;
+ if(argc<2) {
+ fprintf(stderr, "Usage: %s file.xml [file.values]\n", argv[0]);
+ return 1;
+ }
+ f = fopen(argv[1], "r");
+ if(!f) {
+ fprintf(stderr, "Cannot open %s for reading.\n", argv[1]);
+ return 1;
+ }
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ buffer = malloc(len);
+ if(!buffer) {
+ fprintf(stderr, "Memory allocation error.\n");
+ fclose(f);
+ return 1;
+ }
+ r = (int)fread(buffer, 1, len, f);
+ if(r != len) {
+ fprintf(stderr, "Failed to read file %s. %d out of %d bytes.\n",
+ argv[1], r, len);
+ fclose(f);
+ free(buffer);
+ return 1;
+ }
+ fclose(f);
+ f = NULL;
+ if(argc > 2) {
+ f = fopen(argv[2], "r");
+ if(!f) {
+ fprintf(stderr, "Cannot open %s for reading.\n", argv[2]);
+ free(buffer);
+ return 1;
+ }
+ }
+ r = test_igd_desc_parse(buffer, len, f);
+ free(buffer);
+ if(f)
+ fclose(f);
+ return r;
+}
+
diff --git a/ext/miniupnpc/testminiwget.c b/ext/miniupnpc/testminiwget.c
new file mode 100644
index 00000000..8ae90320
--- /dev/null
+++ b/ext/miniupnpc/testminiwget.c
@@ -0,0 +1,53 @@
+/* $Id: testminiwget.c,v 1.4 2012/06/23 22:35:59 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2012 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#include <stdio.h>
+#include <stdlib.h>
+#include "miniwget.h"
+
+/**
+ * This program uses the miniwget / miniwget_getaddr function
+ * from miniwget.c in order to retreive a web ressource using
+ * a GET HTTP method, and store it in a file.
+ */
+int main(int argc, char * * argv)
+{
+ void * data;
+ int size, writtensize;
+ FILE *f;
+ char addr[64];
+
+ if(argc < 3) {
+ fprintf(stderr, "Usage:\t%s url file\n", argv[0]);
+ fprintf(stderr, "Example:\t%s http://www.google.com/ out.html\n", argv[0]);
+ return 1;
+ }
+ data = miniwget_getaddr(argv[1], &size, addr, sizeof(addr), 0);
+ if(!data) {
+ fprintf(stderr, "Error fetching %s\n", argv[1]);
+ return 1;
+ }
+ printf("local address : %s\n", addr);
+ printf("got %d bytes\n", size);
+ f = fopen(argv[2], "wb");
+ if(!f) {
+ fprintf(stderr, "Cannot open file %s for writing\n", argv[2]);
+ free(data);
+ return 1;
+ }
+ writtensize = fwrite(data, 1, size, f);
+ if(writtensize != size) {
+ fprintf(stderr, "Could only write %d bytes out of %d to %s\n",
+ writtensize, size, argv[2]);
+ } else {
+ printf("%d bytes written to %s\n", writtensize, argv[2]);
+ }
+ fclose(f);
+ free(data);
+ return 0;
+}
+
diff --git a/ext/miniupnpc/testminiwget.sh b/ext/miniupnpc/testminiwget.sh
new file mode 100755
index 00000000..690b4056
--- /dev/null
+++ b/ext/miniupnpc/testminiwget.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+# $Id: testminiwget.sh,v 1.13 2015/09/03 17:57:44 nanard Exp $
+# project miniupnp : http://miniupnp.free.fr/
+# (c) 2011-2015 Thomas Bernard
+#
+# test program for miniwget.c
+# is usually invoked by "make check"
+#
+# This test program :
+# 1 - launches a local HTTP server (minihttptestserver)
+# 2 - uses testminiwget to retreive data from this server
+# 3 - compares served and received data
+# 4 - kills the local HTTP server and exits
+#
+# The script was tested and works with ksh, bash
+# it should now also run with dash
+
+TMPD=`mktemp -d -t miniwgetXXXXXXXXXX`
+HTTPSERVEROUT="${TMPD}/httpserverout"
+EXPECTEDFILE="${TMPD}/expectedfile"
+DOWNLOADEDFILE="${TMPD}/downloadedfile"
+PORT=
+RET=0
+
+case "$HAVE_IPV6" in
+ n|no|0)
+ ADDR=localhost
+ SERVERARGS=""
+ ;;
+ *)
+ ADDR="[::1]"
+ SERVERARGS="-6"
+ ;;
+
+esac
+
+#make minihttptestserver
+#make testminiwget
+
+# launching the test HTTP server
+./minihttptestserver $SERVERARGS -e $EXPECTEDFILE > $HTTPSERVEROUT &
+SERVERPID=$!
+while [ -z "$PORT" ]; do
+ sleep 1
+ PORT=`cat $HTTPSERVEROUT | sed 's/Listening on port \([0-9]*\)/\1/' `
+done
+echo "Test HTTP server is listening on $PORT"
+
+URL1="http://$ADDR:$PORT/index.html"
+URL2="http://$ADDR:$PORT/chunked"
+URL3="http://$ADDR:$PORT/addcrap"
+
+echo "standard test ..."
+./testminiwget $URL1 "${DOWNLOADEDFILE}.1"
+if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.1" ; then
+ echo "ok"
+else
+ echo "standard test FAILED"
+ RET=1
+fi
+
+echo "chunked transfert encoding test ..."
+./testminiwget $URL2 "${DOWNLOADEDFILE}.2"
+if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.2" ; then
+ echo "ok"
+else
+ echo "chunked transfert encoding test FAILED"
+ RET=1
+fi
+
+echo "response too long test ..."
+./testminiwget $URL3 "${DOWNLOADEDFILE}.3"
+if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.3" ; then
+ echo "ok"
+else
+ echo "response too long test FAILED"
+ RET=1
+fi
+
+# kill the test HTTP server
+kill $SERVERPID
+wait $SERVERPID
+
+# remove temporary files (for success cases)
+if [ $RET -eq 0 ]; then
+ rm -f "${DOWNLOADEDFILE}.1"
+ rm -f "${DOWNLOADEDFILE}.2"
+ rm -f "${DOWNLOADEDFILE}.3"
+ rm -f $EXPECTEDFILE $HTTPSERVEROUT
+ rmdir ${TMPD}
+else
+ echo "at least one of the test FAILED"
+ echo "directory ${TMPD} is left intact"
+fi
+exit $RET
+
diff --git a/ext/miniupnpc/testminixml.c b/ext/miniupnpc/testminixml.c
new file mode 100644
index 00000000..57c4a85e
--- /dev/null
+++ b/ext/miniupnpc/testminixml.c
@@ -0,0 +1,89 @@
+/* $Id: testminixml.c,v 1.10 2014/11/17 17:19:13 nanard Exp $
+ * MiniUPnP project
+ * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author : Thomas Bernard.
+ * Copyright (c) 2005-2014 Thomas Bernard
+ *
+ * testminixml.c
+ * test program for the "minixml" functions.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "minixml.h"
+#include "igd_desc_parse.h"
+
+/* ---------------------------------------------------------------------- */
+void printeltname1(void * d, const char * name, int l)
+{
+ int i;
+ (void)d;
+ printf("element ");
+ for(i=0;i<l;i++)
+ putchar(name[i]);
+}
+void printeltname2(void * d, const char * name, int l)
+{
+ int i;
+ (void)d;
+ putchar('/');
+ for(i=0;i<l;i++)
+ putchar(name[i]);
+ putchar('\n');
+}
+void printdata(void *d, const char * data, int l)
+{
+ int i;
+ (void)d;
+ printf("data : ");
+ for(i=0;i<l;i++)
+ putchar(data[i]);
+ putchar('\n');
+}
+
+void burptest(const char * buffer, int bufsize)
+{
+ struct IGDdatas data;
+ struct xmlparser parser;
+ /*objet IGDdatas */
+ memset(&data, 0, sizeof(struct IGDdatas));
+ /* objet xmlparser */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = &data;
+ /*parser.starteltfunc = printeltname1;
+ parser.endeltfunc = printeltname2;
+ parser.datafunc = printdata; */
+ parser.starteltfunc = IGDstartelt;
+ parser.endeltfunc = IGDendelt;
+ parser.datafunc = IGDdata;
+ parsexml(&parser);
+#ifdef DEBUG
+ printIGD(&data);
+#endif /* DEBUG */
+}
+
+/* ----- main ---- */
+#define XML_MAX_SIZE (8192)
+int main(int argc, char * * argv)
+{
+ FILE * f;
+ char buffer[XML_MAX_SIZE];
+ int bufsize;
+ if(argc<2)
+ {
+ printf("usage:\t%s file.xml\n", argv[0]);
+ return 1;
+ }
+ f = fopen(argv[1], "r");
+ if(!f)
+ {
+ printf("cannot open file %s\n", argv[1]);
+ return 1;
+ }
+ bufsize = (int)fread(buffer, 1, XML_MAX_SIZE, f);
+ fclose(f);
+ burptest(buffer, bufsize);
+ return 0;
+}
+
diff --git a/ext/miniupnpc/testportlistingparse.c b/ext/miniupnpc/testportlistingparse.c
new file mode 100644
index 00000000..bd9247dc
--- /dev/null
+++ b/ext/miniupnpc/testportlistingparse.c
@@ -0,0 +1,151 @@
+/* $Id: testportlistingparse.c,v 1.2 2014/11/01 10:37:32 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author : Thomas Bernard
+ * Copyright (c) 2014 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+
+#include <string.h>
+#include <stdio.h>
+#include "portlistingparse.h"
+
+struct port_mapping {
+ unsigned int leasetime;
+ unsigned short externalport;
+ unsigned short internalport;
+ const char * remotehost;
+ const char * client;
+ const char * proto;
+ const char * desc;
+ unsigned char enabled;
+};
+
+/* return the number of differences */
+int test(const char * portListingXml, int portListingXmlLen,
+ const struct port_mapping * ref, int count)
+{
+ int i;
+ int r = 0;
+ struct PortMappingParserData data;
+ struct PortMapping * pm;
+
+ memset(&data, 0, sizeof(data));
+ ParsePortListing(portListingXml, portListingXmlLen, &data);
+ for(i = 0, pm = data.l_head;
+ (pm != NULL) && (i < count);
+ i++, pm = pm->l_next) {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost,
+ (unsigned)pm->leaseTime);
+ if(0 != strcmp(pm->protocol, ref[i].proto)) {
+ printf("protocol : '%s' != '%s'\n", pm->protocol, ref[i].proto);
+ r++;
+ }
+ if(pm->externalPort != ref[i].externalport) {
+ printf("externalPort : %hu != %hu\n",
+ pm->externalPort, ref[i].externalport);
+ r++;
+ }
+ if(0 != strcmp(pm->internalClient, ref[i].client)) {
+ printf("client : '%s' != '%s'\n",
+ pm->internalClient, ref[i].client);
+ r++;
+ }
+ if(pm->internalPort != ref[i].internalport) {
+ printf("internalPort : %hu != %hu\n",
+ pm->internalPort, ref[i].internalport);
+ r++;
+ }
+ if(0 != strcmp(pm->description, ref[i].desc)) {
+ printf("description : '%s' != '%s'\n",
+ pm->description, ref[i].desc);
+ r++;
+ }
+ if(0 != strcmp(pm->remoteHost, ref[i].remotehost)) {
+ printf("remoteHost : '%s' != '%s'\n",
+ pm->remoteHost, ref[i].remotehost);
+ r++;
+ }
+ if((unsigned)pm->leaseTime != ref[i].leasetime) {
+ printf("leaseTime : %u != %u\n",
+ (unsigned)pm->leaseTime, ref[i].leasetime);
+ r++;
+ }
+ if(pm->enabled != ref[i].enabled) {
+ printf("enabled : %d != %d\n",
+ (int)pm->enabled, (int)ref[i].enabled);
+ r++;
+ }
+ }
+ if((i != count) || (pm != NULL)) {
+ printf("count mismatch : i=%d count=%d pm=%p\n", i, count, pm);
+ r++;
+ }
+ FreePortListing(&data);
+ return r;
+}
+
+const char test_document[] =
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+"<p:PortMappingList xmlns:p=\"urn:schemas-upnp-org:gw:WANIPConnection\"\n"
+"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n"
+"xsi:schemaLocation=\"urn:schemas-upnp-org:gw:WANIPConnection "
+"http://www.upnp.org/schemas/gw/WANIPConnection-v2.xsd\">\n"
+" <p:PortMappingEntry>\n"
+" <p:NewRemoteHost></p:NewRemoteHost>\n"
+" <p:NewExternalPort>5002</p:NewExternalPort>\n"
+" <p:NewProtocol>UDP</p:NewProtocol>\n"
+" <p:NewInternalPort>4001</p:NewInternalPort>\n"
+" <p:NewInternalClient>192.168.1.123</p:NewInternalClient>\n"
+" <p:NewEnabled>1</p:NewEnabled>\n"
+" <p:NewDescription>xxx</p:NewDescription>\n"
+" <p:NewLeaseTime>0</p:NewLeaseTime>\n"
+" </p:PortMappingEntry>\n"
+" <p:PortMappingEntry>\n"
+" <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>\n"
+" <p:NewExternalPort>2345</p:NewExternalPort>\n"
+" <p:NewProtocol>TCP</p:NewProtocol>\n"
+" <p:NewInternalPort>2349</p:NewInternalPort>\n"
+" <p:NewInternalClient>192.168.1.137</p:NewInternalClient>\n"
+" <p:NewEnabled>1</p:NewEnabled>\n"
+" <p:NewDescription>dooom</p:NewDescription>\n"
+" <p:NewLeaseTime>346</p:NewLeaseTime>\n"
+" </p:PortMappingEntry>\n"
+" <p:PortMappingEntry>\n"
+" <p:NewRemoteHost>134.231.2.11</p:NewRemoteHost>\n"
+" <p:NewExternalPort>12345</p:NewExternalPort>\n"
+" <p:NewProtocol>TCP</p:NewProtocol>\n"
+" <p:NewInternalPort>12345</p:NewInternalPort>\n"
+" <p:NewInternalClient>192.168.1.137</p:NewInternalClient>\n"
+" <p:NewEnabled>1</p:NewEnabled>\n"
+" <p:NewDescription>dooom A</p:NewDescription>\n"
+" <p:NewLeaseTime>347</p:NewLeaseTime>\n"
+" </p:PortMappingEntry>\n"
+"</p:PortMappingList>";
+
+#define PORT_MAPPINGS_COUNT 3
+const struct port_mapping port_mappings[PORT_MAPPINGS_COUNT] = {
+{347, 12345, 12345, "134.231.2.11", "192.168.1.137", "TCP", "dooom A", 1},
+{346, 2345, 2349, "202.233.2.1", "192.168.1.137", "TCP", "dooom", 1},
+{0, 5002, 4001, "", "192.168.1.123", "UDP", "xxx", 1}
+};
+
+/* --- main --- */
+int main(void)
+{
+ int r;
+ r = test(test_document, sizeof(test_document) - 1,
+ port_mappings, PORT_MAPPINGS_COUNT);
+ if(r == 0) {
+ printf("test of portlistingparse OK\n");
+ return 0;
+ } else {
+ printf("test FAILED (%d differences counted)\n", r);
+ return 1;
+ }
+}
+
diff --git a/ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue b/ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue
new file mode 100644
index 00000000..48ca0ccc
--- /dev/null
+++ b/ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue
@@ -0,0 +1,3 @@
+NewRemoteHost=
+NewExternalPort=123
+NewProtocol=TCP
diff --git a/ext/miniupnpc/testreplyparse/DeletePortMapping.xml b/ext/miniupnpc/testreplyparse/DeletePortMapping.xml
new file mode 100644
index 00000000..a955c53f
--- /dev/null
+++ b/ext/miniupnpc/testreplyparse/DeletePortMapping.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:DeletePortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewRemoteHost></NewRemoteHost><NewExternalPort>123</NewExternalPort>
+<NewProtocol>TCP</NewProtocol></u:DeletePortMapping></s:Body>
+
+</s:Envelope>
+
diff --git a/ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue b/ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue
new file mode 100644
index 00000000..5aa75f88
--- /dev/null
+++ b/ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue
@@ -0,0 +1,2 @@
+NewExternalIPAddress=1.2.3.4
+
diff --git a/ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml b/ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml
new file mode 100644
index 00000000..db7ec1f9
--- /dev/null
+++ b/ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetExternalIPAddressResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewExternalIPAddress>1.2.3.4</NewExternalIPAddress></u:GetExternalIPAddressResponse></s:Body></s:Envelope>
+
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue
new file mode 100644
index 00000000..26b169c3
--- /dev/null
+++ b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue
@@ -0,0 +1,3 @@
+NewProtocol=UDP
+NewExternalPort=12345
+NewRemoteHost=
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml
new file mode 100644
index 00000000..bbb540ea
--- /dev/null
+++ b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetSpecificPortMappingEntry xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewRemoteHost></NewRemoteHost><NewExternalPort>12345</NewExternalPort><NewProtocol>UDP</NewProtocol></u:GetSpecificPortMappingEntry></s:Body></s:Envelope>
+
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue
new file mode 100644
index 00000000..2189789b
--- /dev/null
+++ b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue
@@ -0,0 +1,5 @@
+NewInternalPort=12345
+NewInternalClient=192.168.10.110
+NewEnabled=1
+NewPortMappingDescription=libminiupnpc
+NewLeaseDuration=0
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml
new file mode 100644
index 00000000..77e8d9c7
--- /dev/null
+++ b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetSpecificPortMappingEntryResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewInternalPort>12345</NewInternalPort><NewInternalClient>192.168.10.110</NewInternalClient><NewEnabled>1</NewEnabled><NewPortMappingDescription>libminiupnpc</NewPortMappingDescription><NewLeaseDuration>0</NewLeaseDuration></u:GetSpecificPortMappingEntryResponse></s:Body></s:Envelope>
+
diff --git a/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue b/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue
new file mode 100644
index 00000000..f78c7e2a
--- /dev/null
+++ b/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue
@@ -0,0 +1 @@
+NewDefaultConnectionService=uuid:c6c05a33-f704-48df-9910-e099b3471d81:WANConnectionDevice:1,INVALID_SERVICE_ID
diff --git a/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml b/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml
new file mode 100644
index 00000000..ac04c07a
--- /dev/null
+++ b/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><u:SetDefaultConnectionService xmlns:u="urn:schemas-upnp-org:service:Layer3Forwarding:1"><NewDefaultConnectionService>uuid:c6c05a33-f704-48df-9910-e099b3471d81:WANConnectionDevice:1,INVALID_SERVICE_ID</NewDefaultConnectionService></u:SetDefaultConnectionService></s:Body></s:Envelope>
diff --git a/ext/miniupnpc/testreplyparse/readme.txt b/ext/miniupnpc/testreplyparse/readme.txt
new file mode 100644
index 00000000..3eb1f015
--- /dev/null
+++ b/ext/miniupnpc/testreplyparse/readme.txt
@@ -0,0 +1,7 @@
+This directory contains files used for validation of upnpreplyparse.c code.
+
+Each .xml file to parse should give the results which are in the .namevalue
+file.
+
+A .namevalue file contain name=value lines.
+
diff --git a/ext/miniupnpc/testupnpigd.py b/ext/miniupnpc/testupnpigd.py
new file mode 100755
index 00000000..6d167a4c
--- /dev/null
+++ b/ext/miniupnpc/testupnpigd.py
@@ -0,0 +1,84 @@
+#! /usr/bin/python
+# $Id: testupnpigd.py,v 1.4 2008/10/11 10:27:20 nanard Exp $
+# MiniUPnP project
+# Author : Thomas Bernard
+# This Sample code is public domain.
+# website : http://miniupnp.tuxfamily.org/
+
+# import the python miniupnpc module
+import miniupnpc
+import socket
+import BaseHTTPServer
+
+# function definition
+def list_redirections():
+ i = 0
+ while True:
+ p = u.getgenericportmapping(i)
+ if p==None:
+ break
+ print i, p
+ i = i + 1
+
+#define the handler class for HTTP connections
+class handler_class(BaseHTTPServer.BaseHTTPRequestHandler):
+ def do_GET(self):
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write("OK MON GARS")
+
+# create the object
+u = miniupnpc.UPnP()
+#print 'inital(default) values :'
+#print ' discoverdelay', u.discoverdelay
+#print ' lanaddr', u.lanaddr
+#print ' multicastif', u.multicastif
+#print ' minissdpdsocket', u.minissdpdsocket
+u.discoverdelay = 200;
+
+try:
+ print 'Discovering... delay=%ums' % u.discoverdelay
+ ndevices = u.discover()
+ print ndevices, 'device(s) detected'
+
+ # select an igd
+ u.selectigd()
+ # display information about the IGD and the internet connection
+ print 'local ip address :', u.lanaddr
+ externalipaddress = u.externalipaddress()
+ print 'external ip address :', externalipaddress
+ print u.statusinfo(), u.connectiontype()
+
+ #instanciate a HTTPd object. The port is assigned by the system.
+ httpd = BaseHTTPServer.HTTPServer((u.lanaddr, 0), handler_class)
+ eport = httpd.server_port
+
+ # find a free port for the redirection
+ r = u.getspecificportmapping(eport, 'TCP')
+ while r != None and eport < 65536:
+ eport = eport + 1
+ r = u.getspecificportmapping(eport, 'TCP')
+
+ print 'trying to redirect %s port %u TCP => %s port %u TCP' % (externalipaddress, eport, u.lanaddr, httpd.server_port)
+
+ b = u.addportmapping(eport, 'TCP', u.lanaddr, httpd.server_port,
+ 'UPnP IGD Tester port %u' % eport, '')
+ if b:
+ print 'Success. Now waiting for some HTTP request on http://%s:%u' % (externalipaddress ,eport)
+ try:
+ httpd.handle_request()
+ httpd.server_close()
+ except KeyboardInterrupt, details:
+ print "CTRL-C exception!", details
+ b = u.deleteportmapping(eport, 'TCP')
+ if b:
+ print 'Successfully deleted port mapping'
+ else:
+ print 'Failed to remove port mapping'
+ else:
+ print 'Failed'
+
+ httpd.server_close()
+
+except Exception, e:
+ print 'Exception :', e
diff --git a/ext/miniupnpc/testupnpreplyparse.c b/ext/miniupnpc/testupnpreplyparse.c
new file mode 100644
index 00000000..7ba7131e
--- /dev/null
+++ b/ext/miniupnpc/testupnpreplyparse.c
@@ -0,0 +1,96 @@
+/* $Id: testupnpreplyparse.c,v 1.4 2014/01/27 11:45:19 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2014 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "upnpreplyparse.h"
+
+int
+test_parsing(const char * buf, int len, FILE * f)
+{
+ char line[1024];
+ struct NameValueParserData pdata;
+ int ok = 1;
+ ParseNameValue(buf, len, &pdata);
+ /* check result */
+ if(f != NULL)
+ {
+ while(fgets(line, sizeof(line), f))
+ {
+ char * value;
+ char * equal;
+ char * parsedvalue;
+ int l;
+ l = strlen(line);
+ while((l > 0) && ((line[l-1] == '\r') || (line[l-1] == '\n')))
+ line[--l] = '\0';
+ /* skip empty lines */
+ if(l == 0)
+ continue;
+ equal = strchr(line, '=');
+ if(equal == NULL)
+ {
+ fprintf(stderr, "Warning, line does not contain '=' : %s\n", line);
+ continue;
+ }
+ *equal = '\0';
+ value = equal + 1;
+ parsedvalue = GetValueFromNameValueList(&pdata, line);
+ if((parsedvalue == NULL) || (strcmp(parsedvalue, value) != 0))
+ {
+ fprintf(stderr, "Element <%s> : expecting value '%s', got '%s'\n",
+ line, value, parsedvalue ? parsedvalue : "<null string>");
+ ok = 0;
+ }
+ }
+ }
+ ClearNameValueList(&pdata);
+ return ok;
+}
+
+int main(int argc, char * * argv)
+{
+ FILE * f;
+ char buffer[4096];
+ int l;
+ int ok;
+
+ if(argc<2)
+ {
+ fprintf(stderr, "Usage: %s file.xml [file.namevalues]\n", argv[0]);
+ return 1;
+ }
+ f = fopen(argv[1], "r");
+ if(!f)
+ {
+ fprintf(stderr, "Error : can not open file %s\n", argv[1]);
+ return 2;
+ }
+ l = fread(buffer, 1, sizeof(buffer)-1, f);
+ fclose(f);
+ f = NULL;
+ buffer[l] = '\0';
+ if(argc > 2)
+ {
+ f = fopen(argv[2], "r");
+ if(!f)
+ {
+ fprintf(stderr, "Error : can not open file %s\n", argv[2]);
+ return 2;
+ }
+ }
+#ifdef DEBUG
+ DisplayNameValueList(buffer, l);
+#endif
+ ok = test_parsing(buffer, l, f);
+ if(f)
+ {
+ fclose(f);
+ }
+ return ok ? 0 : 3;
+}
+
diff --git a/ext/miniupnpc/testupnpreplyparse.sh b/ext/miniupnpc/testupnpreplyparse.sh
new file mode 100755
index 00000000..992930b7
--- /dev/null
+++ b/ext/miniupnpc/testupnpreplyparse.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+for f in testreplyparse/*.xml ; do
+ bf="`dirname $f`/`basename $f .xml`"
+ if ./testupnpreplyparse $f $bf.namevalue ; then
+ echo "$f : passed"
+ else
+ echo "$f : FAILED"
+ exit 1
+ fi
+done
+
+exit 0
+
diff --git a/ext/miniupnpc/updateminiupnpcstrings.sh b/ext/miniupnpc/updateminiupnpcstrings.sh
new file mode 100755
index 00000000..dde4354a
--- /dev/null
+++ b/ext/miniupnpc/updateminiupnpcstrings.sh
@@ -0,0 +1,53 @@
+#! /bin/sh
+# $Id: updateminiupnpcstrings.sh,v 1.7 2011/01/04 11:41:53 nanard Exp $
+# project miniupnp : http://miniupnp.free.fr/
+# (c) 2009 Thomas Bernard
+
+FILE=miniupnpcstrings.h
+TMPFILE=miniupnpcstrings.h.tmp
+TEMPLATE_FILE=${FILE}.in
+
+# detecting the OS name and version
+OS_NAME=`uname -s`
+OS_VERSION=`uname -r`
+if [ -f /etc/debian_version ]; then
+ OS_NAME=Debian
+ OS_VERSION=`cat /etc/debian_version`
+fi
+# use lsb_release (Linux Standard Base) when available
+LSB_RELEASE=`which lsb_release`
+if [ 0 -eq $? -a -x "${LSB_RELEASE}" ]; then
+ OS_NAME=`${LSB_RELEASE} -i -s`
+ OS_VERSION=`${LSB_RELEASE} -r -s`
+ case $OS_NAME in
+ Debian)
+ #OS_VERSION=`${LSB_RELEASE} -c -s`
+ ;;
+ Ubuntu)
+ #OS_VERSION=`${LSB_RELEASE} -c -s`
+ ;;
+ esac
+fi
+
+# on AmigaOS 3, uname -r returns "unknown", so we use uname -v
+if [ "$OS_NAME" = "AmigaOS" ]; then
+ if [ "$OS_VERSION" = "unknown" ]; then
+ OS_VERSION=`uname -v`
+ fi
+fi
+
+echo "Detected OS [$OS_NAME] version [$OS_VERSION]"
+MINIUPNPC_VERSION=`cat VERSION`
+echo "MiniUPnPc version [${MINIUPNPC_VERSION}]"
+
+EXPR="s|OS_STRING \".*\"|OS_STRING \"${OS_NAME}/${OS_VERSION}\"|"
+#echo $EXPR
+test -f ${FILE}.in
+echo "setting OS_STRING macro value to ${OS_NAME}/${OS_VERSION} in $FILE."
+sed -e "$EXPR" < $TEMPLATE_FILE > $TMPFILE
+
+EXPR="s|MINIUPNPC_VERSION_STRING \".*\"|MINIUPNPC_VERSION_STRING \"${MINIUPNPC_VERSION}\"|"
+echo "setting MINIUPNPC_VERSION_STRING macro value to ${MINIUPNPC_VERSION} in $FILE."
+sed -e "$EXPR" < $TMPFILE > $FILE
+rm $TMPFILE
+
diff --git a/ext/miniupnpc/upnpc.c b/ext/miniupnpc/upnpc.c
new file mode 100644
index 00000000..8bc552ef
--- /dev/null
+++ b/ext/miniupnpc/upnpc.c
@@ -0,0 +1,833 @@
+/* $Id: upnpc.c,v 1.112 2015/10/08 16:15:48 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#define snprintf _snprintf
+#else
+/* for IPPROTO_TCP / IPPROTO_UDP */
+#include <netinet/in.h>
+#endif
+#include <ctype.h>
+#include "miniwget.h"
+#include "miniupnpc.h"
+#include "upnpcommands.h"
+#include "upnperrors.h"
+#include "miniupnpcstrings.h"
+
+/* protofix() checks if protocol is "UDP" or "TCP"
+ * returns NULL if not */
+const char * protofix(const char * proto)
+{
+ static const char proto_tcp[4] = { 'T', 'C', 'P', 0};
+ static const char proto_udp[4] = { 'U', 'D', 'P', 0};
+ int i, b;
+ for(i=0, b=1; i<4; i++)
+ b = b && ( (proto[i] == proto_tcp[i])
+ || (proto[i] == (proto_tcp[i] | 32)) );
+ if(b)
+ return proto_tcp;
+ for(i=0, b=1; i<4; i++)
+ b = b && ( (proto[i] == proto_udp[i])
+ || (proto[i] == (proto_udp[i] | 32)) );
+ if(b)
+ return proto_udp;
+ return 0;
+}
+
+/* is_int() checks if parameter is an integer or not
+ * 1 for integer
+ * 0 for not an integer */
+int is_int(char const* s)
+{
+ if(s == NULL)
+ return 0;
+ while(*s) {
+ /* #define isdigit(c) ((c) >= '0' && (c) <= '9') */
+ if(!isdigit(*s))
+ return 0;
+ s++;
+ }
+ return 1;
+}
+
+static void DisplayInfos(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ char externalIPAddress[40];
+ char connectionType[64];
+ char status[64];
+ char lastconnerr[64];
+ unsigned int uptime;
+ unsigned int brUp, brDown;
+ time_t timenow, timestarted;
+ int r;
+ if(UPNP_GetConnectionTypeInfo(urls->controlURL,
+ data->first.servicetype,
+ connectionType) != UPNPCOMMAND_SUCCESS)
+ printf("GetConnectionTypeInfo failed.\n");
+ else
+ printf("Connection Type : %s\n", connectionType);
+ if(UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
+ status, &uptime, lastconnerr) != UPNPCOMMAND_SUCCESS)
+ printf("GetStatusInfo failed.\n");
+ else
+ printf("Status : %s, uptime=%us, LastConnectionError : %s\n",
+ status, uptime, lastconnerr);
+ timenow = time(NULL);
+ timestarted = timenow - uptime;
+ printf(" Time started : %s", ctime(&timestarted));
+ if(UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype,
+ &brDown, &brUp) != UPNPCOMMAND_SUCCESS) {
+ printf("GetLinkLayerMaxBitRates failed.\n");
+ } else {
+ printf("MaxBitRateDown : %u bps", brDown);
+ if(brDown >= 1000000) {
+ printf(" (%u.%u Mbps)", brDown / 1000000, (brDown / 100000) % 10);
+ } else if(brDown >= 1000) {
+ printf(" (%u Kbps)", brDown / 1000);
+ }
+ printf(" MaxBitRateUp %u bps", brUp);
+ if(brUp >= 1000000) {
+ printf(" (%u.%u Mbps)", brUp / 1000000, (brUp / 100000) % 10);
+ } else if(brUp >= 1000) {
+ printf(" (%u Kbps)", brUp / 1000);
+ }
+ printf("\n");
+ }
+ r = UPNP_GetExternalIPAddress(urls->controlURL,
+ data->first.servicetype,
+ externalIPAddress);
+ if(r != UPNPCOMMAND_SUCCESS) {
+ printf("GetExternalIPAddress failed. (errorcode=%d)\n", r);
+ } else {
+ printf("ExternalIPAddress = %s\n", externalIPAddress);
+ }
+}
+
+static void GetConnectionStatus(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
+ DisplayInfos(urls, data);
+ bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
+ bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
+ packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
+ printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
+}
+
+static void ListRedirections(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ int r;
+ int i = 0;
+ char index[6];
+ char intClient[40];
+ char intPort[6];
+ char extPort[6];
+ char protocol[4];
+ char desc[80];
+ char enabled[6];
+ char rHost[64];
+ char duration[16];
+ /*unsigned int num=0;
+ UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num);
+ printf("PortMappingNumberOfEntries : %u\n", num);*/
+ printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n");
+ do {
+ snprintf(index, 6, "%d", i);
+ rHost[0] = '\0'; enabled[0] = '\0';
+ duration[0] = '\0'; desc[0] = '\0';
+ extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
+ r = UPNP_GetGenericPortMappingEntry(urls->controlURL,
+ data->first.servicetype,
+ index,
+ extPort, intClient, intPort,
+ protocol, desc, enabled,
+ rHost, duration);
+ if(r==0)
+ /*
+ printf("%02d - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n"
+ " desc='%s' rHost='%s'\n",
+ i, protocol, extPort, intClient, intPort,
+ enabled, duration,
+ desc, rHost);
+ */
+ printf("%2d %s %5s->%s:%-5s '%s' '%s' %s\n",
+ i, protocol, extPort, intClient, intPort,
+ desc, rHost, duration);
+ else
+ printf("GetGenericPortMappingEntry() returned %d (%s)\n",
+ r, strupnperror(r));
+ i++;
+ } while(r==0);
+}
+
+static void NewListRedirections(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ int r;
+ int i = 0;
+ struct PortMappingParserData pdata;
+ struct PortMapping * pm;
+
+ memset(&pdata, 0, sizeof(struct PortMappingParserData));
+ r = UPNP_GetListOfPortMappings(urls->controlURL,
+ data->first.servicetype,
+ "0",
+ "65535",
+ "TCP",
+ "1000",
+ &pdata);
+ if(r == UPNPCOMMAND_SUCCESS)
+ {
+ printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n");
+ for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost,
+ (unsigned)pm->leaseTime);
+ i++;
+ }
+ FreePortListing(&pdata);
+ }
+ else
+ {
+ printf("GetListOfPortMappings() returned %d (%s)\n",
+ r, strupnperror(r));
+ }
+ r = UPNP_GetListOfPortMappings(urls->controlURL,
+ data->first.servicetype,
+ "0",
+ "65535",
+ "UDP",
+ "1000",
+ &pdata);
+ if(r == UPNPCOMMAND_SUCCESS)
+ {
+ for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost,
+ (unsigned)pm->leaseTime);
+ i++;
+ }
+ FreePortListing(&pdata);
+ }
+ else
+ {
+ printf("GetListOfPortMappings() returned %d (%s)\n",
+ r, strupnperror(r));
+ }
+}
+
+/* Test function
+ * 1 - get connection type
+ * 2 - get extenal ip address
+ * 3 - Add port mapping
+ * 4 - get this port mapping from the IGD */
+static void SetRedirectAndTest(struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ const char * iaddr,
+ const char * iport,
+ const char * eport,
+ const char * proto,
+ const char * leaseDuration,
+ const char * description,
+ int addAny)
+{
+ char externalIPAddress[40];
+ char intClient[40];
+ char intPort[6];
+ char reservedPort[6];
+ char duration[16];
+ int r;
+
+ if(!iaddr || !iport || !eport || !proto)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+ proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "invalid protocol\n");
+ return;
+ }
+
+ r = UPNP_GetExternalIPAddress(urls->controlURL,
+ data->first.servicetype,
+ externalIPAddress);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetExternalIPAddress failed.\n");
+ else
+ printf("ExternalIPAddress = %s\n", externalIPAddress);
+
+ if (addAny) {
+ r = UPNP_AddAnyPortMapping(urls->controlURL, data->first.servicetype,
+ eport, iport, iaddr, description,
+ proto, 0, leaseDuration, reservedPort);
+ if(r==UPNPCOMMAND_SUCCESS)
+ eport = reservedPort;
+ else
+ printf("AddAnyPortMapping(%s, %s, %s) failed with code %d (%s)\n",
+ eport, iport, iaddr, r, strupnperror(r));
+ } else {
+ r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
+ eport, iport, iaddr, description,
+ proto, 0, leaseDuration);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
+ eport, iport, iaddr, r, strupnperror(r));
+ }
+
+ r = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
+ data->first.servicetype,
+ eport, proto, NULL/*remoteHost*/,
+ intClient, intPort, NULL/*desc*/,
+ NULL/*enabled*/, duration);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
+ r, strupnperror(r));
+ else {
+ printf("InternalIP:Port = %s:%s\n", intClient, intPort);
+ printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
+ externalIPAddress, eport, proto, intClient, intPort, duration);
+ }
+}
+
+static void
+RemoveRedirect(struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ const char * eport,
+ const char * proto,
+ const char * remoteHost)
+{
+ int r;
+ if(!proto || !eport)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "protocol invalid\n");
+ return;
+ }
+ r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost);
+ printf("UPNP_DeletePortMapping() returned : %d\n", r);
+}
+
+static void
+RemoveRedirectRange(struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ const char * ePortStart, char const * ePortEnd,
+ const char * proto, const char * manage)
+{
+ int r;
+
+ if (!manage)
+ manage = "0";
+
+ if(!proto || !ePortStart || !ePortEnd)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "protocol invalid\n");
+ return;
+ }
+ r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage);
+ printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
+}
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data)
+{
+ unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
+ int firewallEnabled = 0, inboundPinholeAllowed = 0;
+
+ UPNP_GetFirewallStatus(urls->controlURL_6FC, data->IPv6FC.servicetype, &firewallEnabled, &inboundPinholeAllowed);
+ printf("FirewallEnabled: %d & Inbound Pinhole Allowed: %d\n", firewallEnabled, inboundPinholeAllowed);
+ printf("GetFirewallStatus:\n Firewall Enabled: %s\n Inbound Pinhole Allowed: %s\n", (firewallEnabled)? "Yes":"No", (inboundPinholeAllowed)? "Yes":"No");
+
+ bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
+ bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
+ packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
+ printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
+}
+
+/* Test function
+ * 1 - Add pinhole
+ * 2 - Check if pinhole is working from the IGD side */
+static void SetPinholeAndTest(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * remoteaddr, const char * eport,
+ const char * intaddr, const char * iport,
+ const char * proto, const char * lease_time)
+{
+ char uniqueID[8];
+ /*int isWorking = 0;*/
+ int r;
+ char proto_tmp[8];
+
+ if(!intaddr || !remoteaddr || !iport || !eport || !proto || !lease_time)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+ if(atoi(proto) == 0)
+ {
+ const char * protocol;
+ protocol = protofix(proto);
+ if(protocol && (strcmp("TCP", protocol) == 0))
+ {
+ snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_TCP);
+ proto = proto_tmp;
+ }
+ else if(protocol && (strcmp("UDP", protocol) == 0))
+ {
+ snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_UDP);
+ proto = proto_tmp;
+ }
+ else
+ {
+ fprintf(stderr, "invalid protocol\n");
+ return;
+ }
+ }
+ r = UPNP_AddPinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, lease_time, uniqueID);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("AddPinhole([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
+ remoteaddr, eport, intaddr, iport, r, strupnperror(r));
+ else
+ {
+ printf("AddPinhole: ([%s]:%s -> [%s]:%s) / Pinhole ID = %s\n",
+ remoteaddr, eport, intaddr, iport, uniqueID);
+ /*r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->servicetype_6FC, uniqueID, &isWorking);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");*/
+ }
+}
+
+/* Test function
+ * 1 - Check if pinhole is working from the IGD side
+ * 2 - Update pinhole */
+static void GetPinholeAndUpdate(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * uniqueID, const char * lease_time)
+{
+ int isWorking = 0;
+ int r;
+
+ if(!uniqueID || !lease_time)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+ r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ if(isWorking || r==709)
+ {
+ r = UPNP_UpdatePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, lease_time);
+ printf("UpdatePinhole: Pinhole ID = %s with Lease Time: %s\n", uniqueID, lease_time);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("UpdatePinhole: ID (%s) failed with code %d (%s)\n", uniqueID, r, strupnperror(r));
+ }
+}
+
+/* Test function
+ * Get pinhole timeout
+ */
+static void GetPinholeOutboundTimeout(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * remoteaddr, const char * eport,
+ const char * intaddr, const char * iport,
+ const char * proto)
+{
+ int timeout = 0;
+ int r;
+
+ if(!intaddr || !remoteaddr || !iport || !eport || !proto)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+
+ r = UPNP_GetOutboundPinholeTimeout(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, &timeout);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetOutboundPinholeTimeout([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
+ intaddr, iport, remoteaddr, eport, r, strupnperror(r));
+ else
+ printf("GetOutboundPinholeTimeout: ([%s]:%s -> [%s]:%s) / Timeout = %d\n", intaddr, iport, remoteaddr, eport, timeout);
+}
+
+static void
+GetPinholePackets(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r, pinholePackets = 0;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_GetPinholePackets(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &pinholePackets);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetPinholePackets() failed with code %d (%s)\n", r, strupnperror(r));
+ else
+ printf("GetPinholePackets: Pinhole ID = %s / PinholePackets = %d\n", uniqueID, pinholePackets);
+}
+
+static void
+CheckPinhole(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r, isWorking = 0;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ else
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
+}
+
+static void
+RemovePinhole(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_DeletePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID);
+ printf("UPNP_DeletePinhole() returned : %d\n", r);
+}
+
+
+/* sample upnp client program */
+int main(int argc, char ** argv)
+{
+ char command = 0;
+ char ** commandargv = 0;
+ int commandargc = 0;
+ struct UPNPDev * devlist = 0;
+ char lanaddr[64]; /* my ip address on the LAN */
+ int i;
+ const char * rootdescurl = 0;
+ const char * multicastif = 0;
+ const char * minissdpdpath = 0;
+ int localport = UPNP_LOCAL_PORT_ANY;
+ int retcode = 0;
+ int error = 0;
+ int ipv6 = 0;
+ unsigned char ttl = 2; /* defaulting to 2 */
+ const char * description = 0;
+
+#ifdef _WIN32
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+ if(nResult != NO_ERROR)
+ {
+ fprintf(stderr, "WSAStartup() failed.\n");
+ return -1;
+ }
+#endif
+ printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING);
+ printf(" (c) 2005-2015 Thomas Bernard.\n");
+ printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n"
+ "for more information.\n");
+ /* command line processing */
+ for(i=1; i<argc; i++)
+ {
+ if(0 == strcmp(argv[i], "--help") || 0 == strcmp(argv[i], "-h"))
+ {
+ command = 0;
+ break;
+ }
+ if(argv[i][0] == '-')
+ {
+ if(argv[i][1] == 'u')
+ rootdescurl = argv[++i];
+ else if(argv[i][1] == 'm')
+ multicastif = argv[++i];
+ else if(argv[i][1] == 'z')
+ {
+ char junk;
+ if(sscanf(argv[++i], "%d%c", &localport, &junk)!=1 ||
+ localport<0 || localport>65535 ||
+ (localport >1 && localport < 1024))
+ {
+ fprintf(stderr, "Invalid localport '%s'\n", argv[i]);
+ localport = UPNP_LOCAL_PORT_ANY;
+ break;
+ }
+ }
+ else if(argv[i][1] == 'p')
+ minissdpdpath = argv[++i];
+ else if(argv[i][1] == '6')
+ ipv6 = 1;
+ else if(argv[i][1] == 'e')
+ description = argv[++i];
+ else if(argv[i][1] == 't')
+ ttl = (unsigned char)atoi(argv[++i]);
+ else
+ {
+ command = argv[i][1];
+ i++;
+ commandargv = argv + i;
+ commandargc = argc - i;
+ break;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "option '%s' invalid\n", argv[i]);
+ }
+ }
+
+ if(!command
+ || (command == 'a' && commandargc<4)
+ || (command == 'd' && argc<2)
+ || (command == 'r' && argc<2)
+ || (command == 'A' && commandargc<6)
+ || (command == 'U' && commandargc<2)
+ || (command == 'D' && commandargc<1))
+ {
+ fprintf(stderr, "Usage :\t%s [options] -a ip port external_port protocol [duration]\n\t\tAdd port redirection\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -d external_port protocol <remote host>\n\t\tDelete port redirection\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -s\n\t\tGet Connection status\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -l\n\t\tList redirections\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -L\n\t\tList redirections (using GetListOfPortMappings (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -n ip port external_port protocol [duration]\n\t\tAdd (any) port redirection allowing IGD to use alternative external_port (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -N external_port_start external_port_end protocol [manage]\n\t\tDelete range of port redirections (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -r port1 [external_port1] protocol1 [port2 [external_port2] protocol2] [...]\n\t\tAdd all redirections to the current host\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -A remote_ip remote_port internal_ip internal_port protocol lease_time\n\t\tAdd Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -U uniqueID new_lease_time\n\t\tUpdate Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -C uniqueID\n\t\tCheck if Pinhole is Working (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -K uniqueID\n\t\tGet Number of packets going through the rule (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -D uniqueID\n\t\tDelete Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -S\n\t\tGet Firewall status (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -G remote_ip remote_port internal_ip internal_port protocol\n\t\tGet Outbound Pinhole Timeout (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -P\n\t\tGet Presentation url\n", argv[0]);
+ fprintf(stderr, "\nprotocol is UDP or TCP\n");
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -e description : set description for port mapping.\n");
+ fprintf(stderr, " -6 : use ip v6 instead of ip v4.\n");
+ fprintf(stderr, " -u url : bypass discovery process by providing the XML root description url.\n");
+ fprintf(stderr, " -m address/interface : provide ip address (ip v4) or interface name (ip v4 or v6) to use for sending SSDP multicast packets.\n");
+ fprintf(stderr, " -z localport : SSDP packets local (source) port (1024-65535).\n");
+ fprintf(stderr, " -p path : use this path for MiniSSDPd socket.\n");
+ fprintf(stderr, " -t ttl : set multicast TTL. Default value is 2.\n");
+ return 1;
+ }
+
+ if( rootdescurl
+ || (devlist = upnpDiscover(2000, multicastif, minissdpdpath,
+ localport, ipv6, ttl, &error)))
+ {
+ struct UPNPDev * device;
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ if(devlist)
+ {
+ printf("List of UPNP devices found on the network :\n");
+ for(device = devlist; device; device = device->pNext)
+ {
+ printf(" desc: %s\n st: %s\n\n",
+ device->descURL, device->st);
+ }
+ }
+ else if(!rootdescurl)
+ {
+ printf("upnpDiscover() error code=%d\n", error);
+ }
+ i = 1;
+ if( (rootdescurl && UPNP_GetIGDFromUrl(rootdescurl, &urls, &data, lanaddr, sizeof(lanaddr)))
+ || (i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr))))
+ {
+ switch(i) {
+ case 1:
+ printf("Found valid IGD : %s\n", urls.controlURL);
+ break;
+ case 2:
+ printf("Found a (not connected?) IGD : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ break;
+ case 3:
+ printf("UPnP device found. Is it an IGD ? : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ break;
+ default:
+ printf("Found device (igd ?) : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ }
+ printf("Local LAN ip address : %s\n", lanaddr);
+ #if 0
+ printf("getting \"%s\"\n", urls.ipcondescURL);
+ descXML = miniwget(urls.ipcondescURL, &descXMLsize);
+ if(descXML)
+ {
+ /*fwrite(descXML, 1, descXMLsize, stdout);*/
+ free(descXML); descXML = NULL;
+ }
+ #endif
+
+ switch(command)
+ {
+ case 'l':
+ DisplayInfos(&urls, &data);
+ ListRedirections(&urls, &data);
+ break;
+ case 'L':
+ NewListRedirections(&urls, &data);
+ break;
+ case 'a':
+ SetRedirectAndTest(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ (commandargc > 4)?commandargv[4]:"0",
+ description, 0);
+ break;
+ case 'd':
+ RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
+ commandargc > 2 ? commandargv[2] : NULL);
+ break;
+ case 'n': /* aNy */
+ SetRedirectAndTest(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ (commandargc > 4)?commandargv[4]:"0",
+ description, 1);
+ break;
+ case 'N':
+ if (commandargc < 3)
+ fprintf(stderr, "too few arguments\n");
+
+ RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
+ commandargc > 3 ? commandargv[3] : NULL);
+ break;
+ case 's':
+ GetConnectionStatus(&urls, &data);
+ break;
+ case 'r':
+ i = 0;
+ while(i<commandargc)
+ {
+ if(!is_int(commandargv[i])) {
+ /* 1st parameter not an integer : error */
+ fprintf(stderr, "command -r : %s is not an port number\n", commandargv[i]);
+ retcode = 1;
+ break;
+ } else if(is_int(commandargv[i+1])){
+ /* 2nd parameter is an integer : <port> <external_port> <protocol> */
+ SetRedirectAndTest(&urls, &data,
+ lanaddr, commandargv[i],
+ commandargv[i+1], commandargv[i+2], "0",
+ description, 0);
+ i+=3; /* 3 parameters parsed */
+ } else {
+ /* 2nd parameter not an integer : <port> <protocol> */
+ SetRedirectAndTest(&urls, &data,
+ lanaddr, commandargv[i],
+ commandargv[i], commandargv[i+1], "0",
+ description, 0);
+ i+=2; /* 2 parameters parsed */
+ }
+ }
+ break;
+ case 'A':
+ SetPinholeAndTest(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ commandargv[4], commandargv[5]);
+ break;
+ case 'U':
+ GetPinholeAndUpdate(&urls, &data,
+ commandargv[0], commandargv[1]);
+ break;
+ case 'C':
+ for(i=0; i<commandargc; i++)
+ {
+ CheckPinhole(&urls, &data, commandargv[i]);
+ }
+ break;
+ case 'K':
+ for(i=0; i<commandargc; i++)
+ {
+ GetPinholePackets(&urls, &data, commandargv[i]);
+ }
+ break;
+ case 'D':
+ for(i=0; i<commandargc; i++)
+ {
+ RemovePinhole(&urls, &data, commandargv[i]);
+ }
+ break;
+ case 'S':
+ GetFirewallStatus(&urls, &data);
+ break;
+ case 'G':
+ GetPinholeOutboundTimeout(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ commandargv[4]);
+ break;
+ case 'P':
+ printf("Presentation URL found:\n");
+ printf(" %s\n", data.presentationurl);
+ break;
+ default:
+ fprintf(stderr, "Unknown switch -%c\n", command);
+ retcode = 1;
+ }
+
+ FreeUPNPUrls(&urls);
+ }
+ else
+ {
+ fprintf(stderr, "No valid UPNP Internet Gateway Device found.\n");
+ retcode = 1;
+ }
+ freeUPNPDevlist(devlist); devlist = 0;
+ }
+ else
+ {
+ fprintf(stderr, "No IGD UPnP Device found on the network !\n");
+ retcode = 1;
+ }
+#ifdef _WIN32
+ nResult = WSACleanup();
+ if(nResult != NO_ERROR) {
+ fprintf(stderr, "WSACleanup() failed.\n");
+ }
+#endif /* _WIN32 */
+ return retcode;
+}
+
diff --git a/ext/miniupnpc/upnpcommands.c b/ext/miniupnpc/upnpcommands.c
new file mode 100644
index 00000000..cabdb50d
--- /dev/null
+++ b/ext/miniupnpc/upnpcommands.c
@@ -0,0 +1,1237 @@
+/* $Id: upnpcommands.c,v 1.46 2015/07/15 12:19:00 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "upnpcommands.h"
+#include "miniupnpc.h"
+#include "portlistingparse.h"
+
+static UNSIGNED_INTEGER
+my_atoui(const char * s)
+{
+ return s ? ((UNSIGNED_INTEGER)STRTOUI(s, NULL, 0)) : 0;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalBytesSent", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalBytesReceived", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalPacketsSent", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalPacketsReceived", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/* UPNP_GetStatusInfo() call the corresponding UPNP method
+ * returns the current status and uptime */
+MINIUPNP_LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+ const char * servicetype,
+ char * status,
+ unsigned int * uptime,
+ char * lastconnerror)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ char * up;
+ char * err;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!status && !uptime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetStatusInfo", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ up = GetValueFromNameValueList(&pdata, "NewUptime");
+ p = GetValueFromNameValueList(&pdata, "NewConnectionStatus");
+ err = GetValueFromNameValueList(&pdata, "NewLastConnectionError");
+ if(p && up)
+ ret = UPNPCOMMAND_SUCCESS;
+
+ if(status) {
+ if(p){
+ strncpy(status, p, 64 );
+ status[63] = '\0';
+ }else
+ status[0]= '\0';
+ }
+
+ if(uptime) {
+ if(up)
+ sscanf(up,"%u",uptime);
+ else
+ *uptime = 0;
+ }
+
+ if(lastconnerror) {
+ if(err) {
+ strncpy(lastconnerror, err, 64 );
+ lastconnerror[63] = '\0';
+ } else
+ lastconnerror[0] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method
+ * returns the connection type */
+MINIUPNP_LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+ const char * servicetype,
+ char * connectionType)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!connectionType)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetConnectionTypeInfo", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewConnectionType");
+ /*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/
+ /* PossibleConnectionTypes will have several values.... */
+ if(p) {
+ strncpy(connectionType, p, 64 );
+ connectionType[63] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ connectionType[0] = '\0';
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetLinkLayerMaxBitRate() call the corresponding UPNP method.
+ * Returns 2 values: Downloadlink bandwidth and Uplink bandwidth.
+ * One of the values can be null
+ * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only
+ * We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */
+MINIUPNP_LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char * controlURL,
+ const char * servicetype,
+ unsigned int * bitrateDown,
+ unsigned int * bitrateUp)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ char * down;
+ char * up;
+ char * p;
+
+ if(!bitrateDown && !bitrateUp)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ /* shouldn't we use GetCommonLinkProperties ? */
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetCommonLinkProperties", 0, &bufsize))) {
+ /*"GetLinkLayerMaxBitRates", 0, &bufsize);*/
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ /*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/
+ /*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate");*/
+ down = GetValueFromNameValueList(&pdata, "NewLayer1DownstreamMaxBitRate");
+ up = GetValueFromNameValueList(&pdata, "NewLayer1UpstreamMaxBitRate");
+ /*GetValueFromNameValueList(&pdata, "NewWANAccessType");*/
+ /*GetValueFromNameValueList(&pdata, "NewPhysicalLinkStatus");*/
+ if(down && up)
+ ret = UPNPCOMMAND_SUCCESS;
+
+ if(bitrateDown) {
+ if(down)
+ sscanf(down,"%u",bitrateDown);
+ else
+ *bitrateDown = 0;
+ }
+
+ if(bitrateUp) {
+ if(up)
+ sscanf(up,"%u",bitrateUp);
+ else
+ *bitrateUp = 0;
+ }
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+
+/* 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.
+ *
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+ const char * servicetype,
+ char * extIpAdd)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!extIpAdd || !controlURL || !servicetype)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetExternalIPAddress", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ /*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/
+ p = GetValueFromNameValueList(&pdata, "NewExternalIPAddress");
+ if(p) {
+ strncpy(extIpAdd, p, 16 );
+ extIpAdd[15] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ extIpAdd[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration)
+{
+ struct UPNParg * AddPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!inPort || !inClient || !proto || !extPort)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ AddPortMappingArgs = calloc(9, sizeof(struct UPNParg));
+ if(AddPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ AddPortMappingArgs[0].elt = "NewRemoteHost";
+ AddPortMappingArgs[0].val = remoteHost;
+ AddPortMappingArgs[1].elt = "NewExternalPort";
+ AddPortMappingArgs[1].val = extPort;
+ AddPortMappingArgs[2].elt = "NewProtocol";
+ AddPortMappingArgs[2].val = proto;
+ AddPortMappingArgs[3].elt = "NewInternalPort";
+ AddPortMappingArgs[3].val = inPort;
+ AddPortMappingArgs[4].elt = "NewInternalClient";
+ AddPortMappingArgs[4].val = inClient;
+ AddPortMappingArgs[5].elt = "NewEnabled";
+ AddPortMappingArgs[5].val = "1";
+ AddPortMappingArgs[6].elt = "NewPortMappingDescription";
+ AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
+ AddPortMappingArgs[7].elt = "NewLeaseDuration";
+ AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddPortMapping", AddPortMappingArgs,
+ &bufsize))) {
+ free(AddPortMappingArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ /*buffer[bufsize] = '\0';*/
+ /*puts(buffer);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ /*printf("AddPortMapping errorCode = '%s'\n", resVal); */
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ free(AddPortMappingArgs);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration,
+ char * reservedPort)
+{
+ struct UPNParg * AddPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!inPort || !inClient || !proto || !extPort)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ AddPortMappingArgs = calloc(9, sizeof(struct UPNParg));
+ if(AddPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ AddPortMappingArgs[0].elt = "NewRemoteHost";
+ AddPortMappingArgs[0].val = remoteHost;
+ AddPortMappingArgs[1].elt = "NewExternalPort";
+ AddPortMappingArgs[1].val = extPort;
+ AddPortMappingArgs[2].elt = "NewProtocol";
+ AddPortMappingArgs[2].val = proto;
+ AddPortMappingArgs[3].elt = "NewInternalPort";
+ AddPortMappingArgs[3].val = inPort;
+ AddPortMappingArgs[4].elt = "NewInternalClient";
+ AddPortMappingArgs[4].val = inClient;
+ AddPortMappingArgs[5].elt = "NewEnabled";
+ AddPortMappingArgs[5].val = "1";
+ AddPortMappingArgs[6].elt = "NewPortMappingDescription";
+ AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
+ AddPortMappingArgs[7].elt = "NewLeaseDuration";
+ AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddAnyPortMapping", AddPortMappingArgs,
+ &bufsize))) {
+ free(AddPortMappingArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ char *p;
+
+ p = GetValueFromNameValueList(&pdata, "NewReservedPort");
+ if(p) {
+ strncpy(reservedPort, p, 6);
+ reservedPort[5] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else {
+ ret = UPNPCOMMAND_INVALID_RESPONSE;
+ }
+ }
+ ClearNameValueList(&pdata);
+ free(AddPortMappingArgs);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort, const char * proto,
+ const char * remoteHost)
+{
+ /*struct NameValueParserData pdata;*/
+ struct UPNParg * DeletePortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!extPort || !proto)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ DeletePortMappingArgs = calloc(4, sizeof(struct UPNParg));
+ if(DeletePortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ DeletePortMappingArgs[0].elt = "NewRemoteHost";
+ DeletePortMappingArgs[0].val = remoteHost;
+ DeletePortMappingArgs[1].elt = "NewExternalPort";
+ DeletePortMappingArgs[1].val = extPort;
+ DeletePortMappingArgs[2].elt = "NewProtocol";
+ DeletePortMappingArgs[2].val = proto;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMapping",
+ DeletePortMappingArgs, &bufsize))) {
+ free(DeletePortMappingArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ free(DeletePortMappingArgs);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
+ const char * extPortStart, const char * extPortEnd,
+ const char * proto,
+ const char * manage)
+{
+ struct UPNParg * DeletePortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!extPortStart || !extPortEnd || !proto || !manage)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ DeletePortMappingArgs = calloc(5, sizeof(struct UPNParg));
+ if(DeletePortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ DeletePortMappingArgs[0].elt = "NewStartPort";
+ DeletePortMappingArgs[0].val = extPortStart;
+ DeletePortMappingArgs[1].elt = "NewEndPort";
+ DeletePortMappingArgs[1].val = extPortEnd;
+ DeletePortMappingArgs[2].elt = "NewProtocol";
+ DeletePortMappingArgs[2].val = proto;
+ DeletePortMappingArgs[3].elt = "NewManage";
+ DeletePortMappingArgs[3].val = manage;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMappingRange",
+ DeletePortMappingArgs, &bufsize))) {
+ free(DeletePortMappingArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ free(DeletePortMappingArgs);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * index,
+ char * extPort,
+ char * intClient,
+ char * intPort,
+ char * protocol,
+ char * desc,
+ char * enabled,
+ char * rHost,
+ char * duration)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int r = UPNPCOMMAND_UNKNOWN_ERROR;
+ if(!index)
+ return UPNPCOMMAND_INVALID_ARGS;
+ intClient[0] = '\0';
+ intPort[0] = '\0';
+ GetPortMappingArgs = calloc(2, sizeof(struct UPNParg));
+ if(GetPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetPortMappingArgs[0].elt = "NewPortMappingIndex";
+ GetPortMappingArgs[0].val = index;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetGenericPortMappingEntry",
+ GetPortMappingArgs, &bufsize))) {
+ free(GetPortMappingArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewRemoteHost");
+ if(p && rHost)
+ {
+ strncpy(rHost, p, 64);
+ rHost[63] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewExternalPort");
+ if(p && extPort)
+ {
+ strncpy(extPort, p, 6);
+ extPort[5] = '\0';
+ r = UPNPCOMMAND_SUCCESS;
+ }
+ p = GetValueFromNameValueList(&pdata, "NewProtocol");
+ if(p && protocol)
+ {
+ strncpy(protocol, p, 4);
+ protocol[3] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+ if(p && intClient)
+ {
+ strncpy(intClient, p, 16);
+ intClient[15] = '\0';
+ r = 0;
+ }
+ p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+ if(p && intPort)
+ {
+ strncpy(intPort, p, 6);
+ intPort[5] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewEnabled");
+ if(p && enabled)
+ {
+ strncpy(enabled, p, 4);
+ enabled[3] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
+ if(p && desc)
+ {
+ strncpy(desc, p, 80);
+ desc[79] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
+ if(p && duration)
+ {
+ strncpy(duration, p, 16);
+ duration[15] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ r = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &r);
+ }
+ ClearNameValueList(&pdata);
+ free(GetPortMappingArgs);
+ return r;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char * controlURL,
+ const char * servicetype,
+ unsigned int * numEntries)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char* p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetPortMappingNumberOfEntries", 0,
+ &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+#ifdef DEBUG
+ DisplayNameValueList(buffer, bufsize);
+#endif
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingNumberOfEntries");
+ if(numEntries && p) {
+ *numEntries = 0;
+ sscanf(p, "%u", numEntries);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
+ * the result is returned in the intClient and intPort strings
+ * please provide 16 and 6 bytes of data */
+MINIUPNP_LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * extPort,
+ const char * proto,
+ const char * remoteHost,
+ char * intClient,
+ char * intPort,
+ char * desc,
+ char * enabled,
+ char * leaseDuration)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!intPort || !intClient || !extPort || !proto)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetPortMappingArgs = calloc(4, sizeof(struct UPNParg));
+ if(GetPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetPortMappingArgs[0].elt = "NewRemoteHost";
+ GetPortMappingArgs[0].val = remoteHost;
+ GetPortMappingArgs[1].elt = "NewExternalPort";
+ GetPortMappingArgs[1].val = extPort;
+ GetPortMappingArgs[2].elt = "NewProtocol";
+ GetPortMappingArgs[2].val = proto;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetSpecificPortMappingEntry",
+ GetPortMappingArgs, &bufsize))) {
+ free(GetPortMappingArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+ if(p) {
+ strncpy(intClient, p, 16);
+ intClient[15] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ intClient[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+ if(p) {
+ strncpy(intPort, p, 6);
+ intPort[5] = '\0';
+ } else
+ intPort[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "NewEnabled");
+ if(p && enabled) {
+ strncpy(enabled, p, 4);
+ enabled[3] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
+ if(p && desc) {
+ strncpy(desc, p, 80);
+ desc[79] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
+ if(p && leaseDuration)
+ {
+ strncpy(leaseDuration, p, 16);
+ leaseDuration[15] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ free(GetPortMappingArgs);
+ return ret;
+}
+
+/* UPNP_GetListOfPortMappings()
+ *
+ * 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.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+ const char * servicetype,
+ const char * startPort,
+ const char * endPort,
+ const char * protocol,
+ const char * numberOfPorts,
+ struct PortMappingParserData * data)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetListOfPortMappingsArgs;
+ const char * p;
+ char * buffer;
+ int bufsize;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!startPort || !endPort || !protocol)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetListOfPortMappingsArgs = calloc(6, sizeof(struct UPNParg));
+ if(GetListOfPortMappingsArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetListOfPortMappingsArgs[0].elt = "NewStartPort";
+ GetListOfPortMappingsArgs[0].val = startPort;
+ GetListOfPortMappingsArgs[1].elt = "NewEndPort";
+ GetListOfPortMappingsArgs[1].val = endPort;
+ GetListOfPortMappingsArgs[2].elt = "NewProtocol";
+ GetListOfPortMappingsArgs[2].val = protocol;
+ GetListOfPortMappingsArgs[3].elt = "NewManage";
+ GetListOfPortMappingsArgs[3].val = "1";
+ GetListOfPortMappingsArgs[4].elt = "NewNumberOfPorts";
+ GetListOfPortMappingsArgs[4].val = numberOfPorts?numberOfPorts:"1000";
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetListOfPortMappings",
+ GetListOfPortMappingsArgs, &bufsize))) {
+ free(GetListOfPortMappingsArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ free(GetListOfPortMappingsArgs);
+
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ /*p = GetValueFromNameValueList(&pdata, "NewPortListing");*/
+ /*if(p) {
+ printf("NewPortListing : %s\n", p);
+ }*/
+ /*printf("NewPortListing(%d chars) : %s\n",
+ pdata.portListingLength, pdata.portListing);*/
+ if(pdata.portListing)
+ {
+ /*struct PortMapping * pm;
+ int i = 0;*/
+ ParsePortListing(pdata.portListing, pdata.portListingLength,
+ data);
+ ret = UPNPCOMMAND_SUCCESS;
+ /*
+ for(pm = data->head.lh_first; pm != NULL; pm = pm->entries.le_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s'\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost);
+ i++;
+ }
+ */
+ /*FreePortListing(&data);*/
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+
+ /*printf("%.*s", bufsize, buffer);*/
+
+ return ret;
+}
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+MINIUPNP_LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+ const char * servicetype,
+ int * firewallEnabled,
+ int * inboundPinholeAllowed)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * fe, *ipa, *p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!firewallEnabled || !inboundPinholeAllowed)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetFirewallStatus", 0, &bufsize);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ fe = GetValueFromNameValueList(&pdata, "FirewallEnabled");
+ ipa = GetValueFromNameValueList(&pdata, "InboundPinholeAllowed");
+ if(ipa && fe)
+ ret = UPNPCOMMAND_SUCCESS;
+ if(fe)
+ *firewallEnabled = my_atoui(fe);
+ /*else
+ *firewallEnabled = 0;*/
+ if(ipa)
+ *inboundPinholeAllowed = my_atoui(ipa);
+ /*else
+ *inboundPinholeAllowed = 0;*/
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ int * opTimeout)
+{
+ struct UPNParg * GetOutboundPinholeTimeoutArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ char * p;
+ int ret;
+
+ if(!intPort || !intClient || !proto || !remotePort || !remoteHost)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetOutboundPinholeTimeoutArgs = calloc(6, sizeof(struct UPNParg));
+ if(GetOutboundPinholeTimeoutArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetOutboundPinholeTimeoutArgs[0].elt = "RemoteHost";
+ GetOutboundPinholeTimeoutArgs[0].val = remoteHost;
+ GetOutboundPinholeTimeoutArgs[1].elt = "RemotePort";
+ GetOutboundPinholeTimeoutArgs[1].val = remotePort;
+ GetOutboundPinholeTimeoutArgs[2].elt = "Protocol";
+ GetOutboundPinholeTimeoutArgs[2].val = proto;
+ GetOutboundPinholeTimeoutArgs[3].elt = "InternalPort";
+ GetOutboundPinholeTimeoutArgs[3].val = intPort;
+ GetOutboundPinholeTimeoutArgs[4].elt = "InternalClient";
+ GetOutboundPinholeTimeoutArgs[4].val = intClient;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ p = GetValueFromNameValueList(&pdata, "OutboundPinholeTimeout");
+ if(p)
+ *opTimeout = my_atoui(p);
+ }
+ ClearNameValueList(&pdata);
+ free(GetOutboundPinholeTimeoutArgs);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ const char * leaseTime,
+ char * uniqueID)
+{
+ struct UPNParg * AddPinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ char * p;
+ int ret;
+
+ if(!intPort || !intClient || !proto || !remoteHost || !remotePort || !leaseTime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ AddPinholeArgs = calloc(7, sizeof(struct UPNParg));
+ if(AddPinholeArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ /* RemoteHost can be wilcarded */
+ if(strncmp(remoteHost, "empty", 5)==0)
+ {
+ AddPinholeArgs[0].elt = "RemoteHost";
+ AddPinholeArgs[0].val = "";
+ }
+ else
+ {
+ AddPinholeArgs[0].elt = "RemoteHost";
+ AddPinholeArgs[0].val = remoteHost;
+ }
+ AddPinholeArgs[1].elt = "RemotePort";
+ AddPinholeArgs[1].val = remotePort;
+ AddPinholeArgs[2].elt = "Protocol";
+ AddPinholeArgs[2].val = proto;
+ AddPinholeArgs[3].elt = "InternalPort";
+ AddPinholeArgs[3].val = intPort;
+ if(strncmp(intClient, "empty", 5)==0)
+ {
+ AddPinholeArgs[4].elt = "InternalClient";
+ AddPinholeArgs[4].val = "";
+ }
+ else
+ {
+ AddPinholeArgs[4].elt = "InternalClient";
+ AddPinholeArgs[4].val = intClient;
+ }
+ AddPinholeArgs[5].elt = "LeaseTime";
+ AddPinholeArgs[5].val = leaseTime;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddPinhole", AddPinholeArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "UniqueID");
+ if(p)
+ {
+ strncpy(uniqueID, p, 8);
+ uniqueID[7] = '\0';
+ }
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ /*printf("AddPortMapping errorCode = '%s'\n", resVal);*/
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ free(AddPinholeArgs);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+ const char * uniqueID,
+ const char * leaseTime)
+{
+ struct UPNParg * UpdatePinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!uniqueID || !leaseTime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ UpdatePinholeArgs = calloc(3, sizeof(struct UPNParg));
+ if(UpdatePinholeArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ UpdatePinholeArgs[0].elt = "UniqueID";
+ UpdatePinholeArgs[0].val = uniqueID;
+ UpdatePinholeArgs[1].elt = "NewLeaseTime";
+ UpdatePinholeArgs[1].val = leaseTime;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "UpdatePinhole", UpdatePinholeArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ /*printf("AddPortMapping errorCode = '%s'\n", resVal); */
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ free(UpdatePinholeArgs);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID)
+{
+ /*struct NameValueParserData pdata;*/
+ struct UPNParg * DeletePinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ DeletePinholeArgs = calloc(2, sizeof(struct UPNParg));
+ if(DeletePinholeArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ DeletePinholeArgs[0].elt = "UniqueID";
+ DeletePinholeArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePinhole", DeletePinholeArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ free(DeletePinholeArgs);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * isWorking)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * CheckPinholeWorkingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ CheckPinholeWorkingArgs = calloc(4, sizeof(struct UPNParg));
+ if(CheckPinholeWorkingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ CheckPinholeWorkingArgs[0].elt = "UniqueID";
+ CheckPinholeWorkingArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "CheckPinholeWorking", CheckPinholeWorkingArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "IsWorking");
+ if(p)
+ {
+ *isWorking=my_atoui(p);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ else
+ *isWorking = 0;
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ free(CheckPinholeWorkingArgs);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * packets)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPinholePacketsArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetPinholePacketsArgs = calloc(4, sizeof(struct UPNParg));
+ if(GetPinholePacketsArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetPinholePacketsArgs[0].elt = "UniqueID";
+ GetPinholePacketsArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetPinholePackets", GetPinholePacketsArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "PinholePackets");
+ if(p)
+ {
+ *packets=my_atoui(p);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ free(GetPinholePacketsArgs);
+ return ret;
+}
+
+
diff --git a/ext/miniupnpc/upnpcommands.h b/ext/miniupnpc/upnpcommands.h
new file mode 100644
index 00000000..22eda5e3
--- /dev/null
+++ b/ext/miniupnpc/upnpcommands.h
@@ -0,0 +1,348 @@
+/* $Id: upnpcommands.h,v 1.31 2015/07/21 13:16:55 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef UPNPCOMMANDS_H_INCLUDED
+#define UPNPCOMMANDS_H_INCLUDED
+
+#include "upnpreplyparse.h"
+#include "portlistingparse.h"
+#include "miniupnpc_declspec.h"
+#include "miniupnpctypes.h"
+
+/* MiniUPnPc return codes : */
+#define UPNPCOMMAND_SUCCESS (0)
+#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
+#define UPNPCOMMAND_INVALID_ARGS (-2)
+#define UPNPCOMMAND_HTTP_ERROR (-3)
+#define UPNPCOMMAND_INVALID_RESPONSE (-4)
+#define UPNPCOMMAND_MEM_ALLOC_ERROR (-5)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+ const char * servicetype);
+
+/* UPNP_GetStatusInfo()
+ * status and lastconnerror are 64 byte buffers
+ * Return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+MINIUPNP_LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+ const char * servicetype,
+ char * status,
+ unsigned int * uptime,
+ char * lastconnerror);
+
+/* UPNP_GetConnectionTypeInfo()
+ * argument connectionType is a 64 character buffer
+ * Return Values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+MINIUPNP_LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+ const char * servicetype,
+ char * connectionType);
+
+/* 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. */
+MINIUPNP_LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+ const char * servicetype,
+ char * extIpAdd);
+
+/* UPNP_GetLinkLayerMaxBitRates()
+ * call WANCommonInterfaceConfig:1#GetCommonLinkProperties
+ *
+ * return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code. */
+MINIUPNP_LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
+ const char* servicetype,
+ unsigned int * bitrateDown,
+ unsigned int * bitrateUp);
+
+/* 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
+ */
+MINIUPNP_LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration);
+
+/* 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
+ */
+MINIUPNP_LIBSPEC int
+UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration,
+ char * reservedPort);
+
+/* 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 */
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort, const char * proto,
+ const char * remoteHost);
+
+/* 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. */
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
+ const char * extPortStart, const char * extPortEnd,
+ const char * proto,
+ const char * manage);
+
+/* UPNP_GetPortMappingNumberOfEntries()
+ * not supported by all routers */
+MINIUPNP_LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char* controlURL,
+ const char* servicetype,
+ unsigned int * num);
+
+/* 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.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * extPort,
+ const char * proto,
+ const char * remoteHost,
+ char * intClient,
+ char * intPort,
+ char * desc,
+ char * enabled,
+ char * leaseDuration);
+
+/* 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
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * index,
+ char * extPort,
+ char * intClient,
+ char * intPort,
+ char * protocol,
+ char * desc,
+ char * enabled,
+ char * rHost,
+ char * duration);
+
+/* 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.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+ const char * servicetype,
+ const char * startPort,
+ const char * endPort,
+ const char * protocol,
+ const char * numberOfPorts,
+ struct PortMappingParserData * data);
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+MINIUPNP_LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+ const char * servicetype,
+ int * firewallEnabled,
+ int * inboundPinholeAllowed);
+
+MINIUPNP_LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ int * opTimeout);
+
+MINIUPNP_LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ const char * leaseTime,
+ char * uniqueID);
+
+MINIUPNP_LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+ const char * uniqueID,
+ const char * leaseTime);
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
+
+MINIUPNP_LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * isWorking);
+
+MINIUPNP_LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * packets);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/ext/miniupnpc/upnpdev.c b/ext/miniupnpc/upnpdev.c
new file mode 100644
index 00000000..d89a9934
--- /dev/null
+++ b/ext/miniupnpc/upnpdev.c
@@ -0,0 +1,23 @@
+/* $Id: upnpdev.c,v 1.1 2015/08/28 12:14:19 nanard Exp $ */
+/* Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENSE file. */
+#include <stdlib.h>
+#include "upnpdev.h"
+
+/* freeUPNPDevlist() should be used to
+ * free the chained list returned by upnpDiscover() */
+void freeUPNPDevlist(struct UPNPDev * devlist)
+{
+ struct UPNPDev * next;
+ while(devlist)
+ {
+ next = devlist->pNext;
+ free(devlist);
+ devlist = next;
+ }
+}
+
diff --git a/ext/miniupnpc/upnpdev.h b/ext/miniupnpc/upnpdev.h
new file mode 100644
index 00000000..f49fbe17
--- /dev/null
+++ b/ext/miniupnpc/upnpdev.h
@@ -0,0 +1,36 @@
+/* $Id: upnpdev.h,v 1.1 2015/08/28 12:14:19 nanard Exp $ */
+/* Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENSE file. */
+#ifndef UPNPDEV_H_INCLUDED
+#define UPNPDEV_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct UPNPDev {
+ struct UPNPDev * pNext;
+ char * descURL;
+ char * st;
+ unsigned int scope_id;
+ char * usn;
+ char buffer[3];
+};
+
+/* freeUPNPDevlist()
+ * free list returned by upnpDiscover() */
+MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* UPNPDEV_H_INCLUDED */
diff --git a/ext/miniupnpc/upnperrors.c b/ext/miniupnpc/upnperrors.c
new file mode 100644
index 00000000..7ab8ee96
--- /dev/null
+++ b/ext/miniupnpc/upnperrors.c
@@ -0,0 +1,107 @@
+/* $Id: upnperrors.c,v 1.8 2014/06/10 09:41:48 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2007 Thomas Bernard
+ * All Right reserved.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#include <string.h>
+#include "upnperrors.h"
+#include "upnpcommands.h"
+#include "miniupnpc.h"
+
+const char * strupnperror(int err)
+{
+ const char * s = NULL;
+ switch(err) {
+ case UPNPCOMMAND_SUCCESS:
+ s = "Success";
+ break;
+ case UPNPCOMMAND_UNKNOWN_ERROR:
+ s = "Miniupnpc Unknown Error";
+ break;
+ case UPNPCOMMAND_INVALID_ARGS:
+ s = "Miniupnpc Invalid Arguments";
+ break;
+ case UPNPCOMMAND_INVALID_RESPONSE:
+ s = "Miniupnpc Invalid response";
+ break;
+ case UPNPDISCOVER_SOCKET_ERROR:
+ s = "Miniupnpc Socket error";
+ break;
+ case UPNPDISCOVER_MEMORY_ERROR:
+ s = "Miniupnpc Memory allocation error";
+ break;
+ case 401:
+ s = "Invalid Action";
+ break;
+ case 402:
+ s = "Invalid Args";
+ break;
+ case 501:
+ s = "Action Failed";
+ break;
+ case 606:
+ s = "Action not authorized";
+ break;
+ case 701:
+ s = "PinholeSpaceExhausted";
+ break;
+ case 702:
+ s = "FirewallDisabled";
+ break;
+ case 703:
+ s = "InboundPinholeNotAllowed";
+ break;
+ case 704:
+ s = "NoSuchEntry";
+ break;
+ case 705:
+ s = "ProtocolNotSupported";
+ break;
+ case 706:
+ s = "InternalPortWildcardingNotAllowed";
+ break;
+ case 707:
+ s = "ProtocolWildcardingNotAllowed";
+ break;
+ case 708:
+ s = "WildcardNotPermittedInSrcIP";
+ break;
+ case 709:
+ s = "NoPacketSent";
+ break;
+ case 713:
+ s = "SpecifiedArrayIndexInvalid";
+ break;
+ case 714:
+ s = "NoSuchEntryInArray";
+ break;
+ case 715:
+ s = "WildCardNotPermittedInSrcIP";
+ break;
+ case 716:
+ s = "WildCardNotPermittedInExtPort";
+ break;
+ case 718:
+ s = "ConflictInMappingEntry";
+ break;
+ case 724:
+ s = "SamePortValuesRequired";
+ break;
+ case 725:
+ s = "OnlyPermanentLeasesSupported";
+ break;
+ case 726:
+ s = "RemoteHostOnlySupportsWildcard";
+ break;
+ case 727:
+ s = "ExternalPortOnlySupportsWildcard";
+ break;
+ default:
+ s = "UnknownError";
+ break;
+ }
+ return s;
+}
diff --git a/ext/miniupnpc/upnperrors.h b/ext/miniupnpc/upnperrors.h
new file mode 100644
index 00000000..3115aee5
--- /dev/null
+++ b/ext/miniupnpc/upnperrors.h
@@ -0,0 +1,26 @@
+/* $Id: upnperrors.h,v 1.6 2015/07/21 13:16:55 nanard Exp $ */
+/* (c) 2007-2015 Thomas Bernard
+ * All rights reserved.
+ * MiniUPnP Project.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef UPNPERRORS_H_INCLUDED
+#define UPNPERRORS_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* strupnperror()
+ * Return a string description of the UPnP error code
+ * or NULL for undefinded errors */
+MINIUPNP_LIBSPEC const char * strupnperror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/ext/miniupnpc/upnpreplyparse.c b/ext/miniupnpc/upnpreplyparse.c
new file mode 100644
index 00000000..5de5796a
--- /dev/null
+++ b/ext/miniupnpc/upnpreplyparse.c
@@ -0,0 +1,197 @@
+/* $Id: upnpreplyparse.c,v 1.19 2015/07/15 10:29:11 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2015 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "upnpreplyparse.h"
+#include "minixml.h"
+
+static void
+NameValueParserStartElt(void * d, const char * name, int l)
+{
+ struct NameValueParserData * data = (struct NameValueParserData *)d;
+ data->topelt = 1;
+ if(l>63)
+ l = 63;
+ memcpy(data->curelt, name, l);
+ data->curelt[l] = '\0';
+ data->cdata = NULL;
+ data->cdatalen = 0;
+}
+
+static void
+NameValueParserEndElt(void * d, const char * name, int l)
+{
+ struct NameValueParserData * data = (struct NameValueParserData *)d;
+ struct NameValue * nv;
+ (void)name;
+ (void)l;
+ if(!data->topelt)
+ return;
+ if(strcmp(data->curelt, "NewPortListing") != 0)
+ {
+ int l;
+ /* standard case. Limited to n chars strings */
+ l = data->cdatalen;
+ nv = malloc(sizeof(struct NameValue));
+ if(nv == NULL)
+ {
+ /* malloc error */
+#ifdef DEBUG
+ fprintf(stderr, "%s: error allocating memory",
+ "NameValueParserEndElt");
+#endif /* DEBUG */
+ return;
+ }
+ if(l>=(int)sizeof(nv->value))
+ l = sizeof(nv->value) - 1;
+ strncpy(nv->name, data->curelt, 64);
+ nv->name[63] = '\0';
+ if(data->cdata != NULL)
+ {
+ memcpy(nv->value, data->cdata, l);
+ nv->value[l] = '\0';
+ }
+ else
+ {
+ nv->value[0] = '\0';
+ }
+ nv->l_next = data->l_head; /* insert in list */
+ data->l_head = nv;
+ }
+ data->cdata = NULL;
+ data->cdatalen = 0;
+ data->topelt = 0;
+}
+
+static void
+NameValueParserGetData(void * d, const char * datas, int l)
+{
+ struct NameValueParserData * data = (struct NameValueParserData *)d;
+ if(strcmp(data->curelt, "NewPortListing") == 0)
+ {
+ /* specific case for NewPortListing which is a XML Document */
+ data->portListing = malloc(l + 1);
+ if(!data->portListing)
+ {
+ /* malloc error */
+#ifdef DEBUG
+ fprintf(stderr, "%s: error allocating memory",
+ "NameValueParserGetData");
+#endif /* DEBUG */
+ return;
+ }
+ memcpy(data->portListing, datas, l);
+ data->portListing[l] = '\0';
+ data->portListingLength = l;
+ }
+ else
+ {
+ /* standard case. */
+ data->cdata = datas;
+ data->cdatalen = l;
+ }
+}
+
+void
+ParseNameValue(const char * buffer, int bufsize,
+ struct NameValueParserData * data)
+{
+ struct xmlparser parser;
+ data->l_head = NULL;
+ data->portListing = NULL;
+ data->portListingLength = 0;
+ /* init xmlparser object */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = data;
+ parser.starteltfunc = NameValueParserStartElt;
+ parser.endeltfunc = NameValueParserEndElt;
+ parser.datafunc = NameValueParserGetData;
+ parser.attfunc = 0;
+ parsexml(&parser);
+}
+
+void
+ClearNameValueList(struct NameValueParserData * pdata)
+{
+ struct NameValue * nv;
+ if(pdata->portListing)
+ {
+ free(pdata->portListing);
+ pdata->portListing = NULL;
+ pdata->portListingLength = 0;
+ }
+ while((nv = pdata->l_head) != NULL)
+ {
+ pdata->l_head = nv->l_next;
+ free(nv);
+ }
+}
+
+char *
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+ const char * Name)
+{
+ struct NameValue * nv;
+ char * p = NULL;
+ for(nv = pdata->l_head;
+ (nv != NULL) && (p == NULL);
+ nv = nv->l_next)
+ {
+ if(strcmp(nv->name, Name) == 0)
+ p = nv->value;
+ }
+ return p;
+}
+
+#if 0
+/* useless now that minixml ignores namespaces by itself */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+ const char * Name)
+{
+ struct NameValue * nv;
+ char * p = NULL;
+ char * pname;
+ for(nv = pdata->head.lh_first;
+ (nv != NULL) && (p == NULL);
+ nv = nv->entries.le_next)
+ {
+ pname = strrchr(nv->name, ':');
+ if(pname)
+ pname++;
+ else
+ pname = nv->name;
+ if(strcmp(pname, Name)==0)
+ p = nv->value;
+ }
+ return p;
+}
+#endif
+
+/* debug all-in-one function
+ * do parsing then display to stdout */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize)
+{
+ struct NameValueParserData pdata;
+ struct NameValue * nv;
+ ParseNameValue(buffer, bufsize, &pdata);
+ for(nv = pdata.l_head;
+ nv != NULL;
+ nv = nv->l_next)
+ {
+ printf("%s = %s\n", nv->name, nv->value);
+ }
+ ClearNameValueList(&pdata);
+}
+#endif /* DEBUG */
+
diff --git a/ext/miniupnpc/upnpreplyparse.h b/ext/miniupnpc/upnpreplyparse.h
new file mode 100644
index 00000000..6badd15b
--- /dev/null
+++ b/ext/miniupnpc/upnpreplyparse.h
@@ -0,0 +1,63 @@
+/* $Id: upnpreplyparse.h,v 1.19 2014/10/27 16:33:19 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2013 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#ifndef UPNPREPLYPARSE_H_INCLUDED
+#define UPNPREPLYPARSE_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct NameValue {
+ struct NameValue * l_next;
+ char name[64];
+ char value[128];
+};
+
+struct NameValueParserData {
+ struct NameValue * l_head;
+ char curelt[64];
+ char * portListing;
+ int portListingLength;
+ int topelt;
+ const char * cdata;
+ int cdatalen;
+};
+
+/* ParseNameValue() */
+void
+ParseNameValue(const char * buffer, int bufsize,
+ struct NameValueParserData * data);
+
+/* ClearNameValueList() */
+void
+ClearNameValueList(struct NameValueParserData * pdata);
+
+/* GetValueFromNameValueList() */
+char *
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+ const char * Name);
+
+#if 0
+/* GetValueFromNameValueListIgnoreNS() */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+ const char * Name);
+#endif
+
+/* DisplayNameValueList() */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/ext/miniupnpc/wingenminiupnpcstrings.c b/ext/miniupnpc/wingenminiupnpcstrings.c
new file mode 100644
index 00000000..50df06a7
--- /dev/null
+++ b/ext/miniupnpc/wingenminiupnpcstrings.c
@@ -0,0 +1,83 @@
+/* $Id: wingenminiupnpcstrings.c,v 1.4 2015/02/08 08:46:06 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENSE file provided within this distribution */
+#include <stdio.h>
+#include <windows.h>
+
+/* This program display the Windows version and is used to
+ * generate the miniupnpcstrings.h
+ * wingenminiupnpcstrings miniupnpcstrings.h.in miniupnpcstrings.h
+ */
+int main(int argc, char * * argv) {
+ char buffer[256];
+ OSVERSIONINFO osvi;
+ FILE * fin;
+ FILE * fout;
+ int n;
+ char miniupnpcVersion[32];
+ /* dwMajorVersion :
+ The major version number of the operating system. For more information, see Remarks.
+ dwMinorVersion :
+ The minor version number of the operating system. For more information, see Remarks.
+ dwBuildNumber :
+ The build number of the operating system.
+ dwPlatformId
+ The operating system platform. This member can be the following value.
+ szCSDVersion
+ A null-terminated string, such as "Service Pack 3", that indicates the
+ latest Service Pack installed on the system. If no Service Pack has
+ been installed, the string is empty.
+ */
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+ GetVersionEx(&osvi);
+
+ printf("Windows %lu.%lu Build %lu %s\n",
+ osvi.dwMajorVersion, osvi.dwMinorVersion,
+ osvi.dwBuildNumber, (const char *)&(osvi.szCSDVersion));
+
+ fin = fopen("VERSION", "r");
+ fgets(miniupnpcVersion, sizeof(miniupnpcVersion), fin);
+ fclose(fin);
+ for(n = 0; n < sizeof(miniupnpcVersion); n++) {
+ if(miniupnpcVersion[n] < ' ')
+ miniupnpcVersion[n] = '\0';
+ }
+ printf("MiniUPnPc version %s\n", miniupnpcVersion);
+
+ if(argc >= 3) {
+ fin = fopen(argv[1], "r");
+ if(!fin) {
+ fprintf(stderr, "Cannot open %s for reading.\n", argv[1]);
+ return 1;
+ }
+ fout = fopen(argv[2], "w");
+ if(!fout) {
+ fprintf(stderr, "Cannot open %s for writing.\n", argv[2]);
+ fclose(fin);
+ return 1;
+ }
+ n = 0;
+ while(fgets(buffer, sizeof(buffer), fin)) {
+ if(0 == memcmp(buffer, "#define OS_STRING \"OS/version\"", 30)) {
+ sprintf(buffer, "#define OS_STRING \"MSWindows/%ld.%ld.%ld\"\n",
+ osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber);
+ } else if(0 == memcmp(buffer, "#define MINIUPNPC_VERSION_STRING \"version\"", 42)) {
+ sprintf(buffer, "#define MINIUPNPC_VERSION_STRING \"%s\"\n",
+ miniupnpcVersion);
+ }
+ /*fputs(buffer, stdout);*/
+ fputs(buffer, fout);
+ n++;
+ }
+ fclose(fin);
+ fclose(fout);
+ printf("%d lines written to %s.\n", n, argv[2]);
+ }
+ return 0;
+}