Sfoglia il codice sorgente

Ton of uPnP work and adding NAT-PMP support, still testing...

Adam Ierymenko 9 anni fa
parent
commit
6768521330

+ 98 - 0
ext/libnatpmp/Changelog.txt

@@ -0,0 +1,98 @@
+$Id: Changelog.txt,v 1.33 2013/11/26 08:47:36 nanard Exp $
+
+2013/11/26:
+  enforce strict aliasing rules.
+
+2013/09/10:
+  small patch for MSVC >= 16
+  rename win32 implementation of gettimeofday() to natpmp_gettimeofday()
+
+2012/08/21:
+  Little change in Makefile
+  removed warnings in testgetgateway.c
+  Fixed bugs in command line argumentparsing in natpmpc.c
+
+2011/08/07:
+  Patch to build on debian/kFreeBSD.
+
+2011/07/15:
+  Put 3 clauses BSD licence at the top of source files.
+
+2011/06/18:
+  --no-undefined => -Wl,--no-undefined
+  adding a natpmpc.1 man page
+
+2011/05/19:
+  Small fix in libnatpmpmodule.c thanks to Manuel Mausz
+
+2011/01/03:
+  Added an argument to initnatpmp() in order to force the gateway to be used
+
+2011/01/01:
+  fix in make install
+
+2010/05/21:
+  make install now working under MacOSX (and BSD)
+
+2010/04/12:
+  cplusplus stuff in natpmp.h
+
+2010/02/02:
+  Fixed compilation under Mac OS X
+
+2009/12/19:
+  improve and fix building under Windows.
+  Project files for MS Visual Studio 2008
+  More simple (and working) code for Win32.
+  More checks in the /proc/net/route parsing. Add some comments.
+
+2009/08/04:
+  improving getgateway.c for windows
+
+2009/07/13:
+  Adding Haiku code in getgateway.c
+
+2009/06/04:
+  Adding Python module thanks to David Wu
+
+2009/03/10:
+  Trying to have windows get gateway working if not using DHCP
+
+2009/02/27:
+  dont include declspec.h if not under WIN32.
+
+2009/01/23:
+  Prefixed the libraries name with lib
+
+2008/10/06:
+  Fixed a memory leak in getdefaultgateway() (USE_SYSCTL_NET_ROUTE)
+
+2008/07/03:
+  Adding WIN32 code from Robbie Hanson
+
+2008/06/30:
+  added a Solaris implementation for getgateway().
+  added a LICENCE file to the distribution
+
+2008/05/29:
+  Anonymous unions are forbidden in ANSI C. That was causing problems with
+  non-GCC compilers.
+
+2008/04/28:
+  introduced strnatpmperr()
+  improved natpmpc.c sample
+  make install now install the binary
+
+2007/12/13:
+  Fixed getgateway.c for working under OS X ;)
+  Fixed values for NATPMP_PROTOCOL_TCP and NATPMP_PROTOCOL_UDP
+
+2007/12/11:
+  Fixed getgateway.c for compilation under Mac OS X
+
+2007/12/01:
+  added some comments in .h
+
+2007/11/30:
+  implemented almost everything
+

+ 42 - 0
ext/libnatpmp/JavaTest.java

@@ -0,0 +1,42 @@
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+
+import fr.free.miniupnp.libnatpmp.NatPmp;
+import fr.free.miniupnp.libnatpmp.NatPmpResponse;
+
+class JavaTest {
+    public static void main(String[] args) {
+	NatPmp natpmp = new NatPmp();
+
+        natpmp.sendPublicAddressRequest();
+        NatPmpResponse response = new NatPmpResponse();
+
+        int result = -1;
+        do{
+            result = natpmp.readNatPmpResponseOrRetry(response);
+	    try {
+		Thread.sleep(4000);
+	    } catch (InterruptedException e) {
+		//fallthrough
+	    }
+        } while (result != 0);
+
+	byte[] bytes = intToByteArray(response.addr);
+
+	try {
+	    InetAddress inetAddress = InetAddress.getByAddress(bytes);
+	    System.out.println("Public address is " + inetAddress);
+	} catch (UnknownHostException e) {
+	    throw new RuntimeException(e);
+	}
+    }
+
+    public static final byte[] intToByteArray(int value) {
+        return new byte[] {
+            (byte)value,
+            (byte)(value >>> 8),
+            (byte)(value >>> 16),
+            (byte)(value >>> 24)};
+    }
+}

+ 26 - 0
ext/libnatpmp/LICENSE

@@ -0,0 +1,26 @@
+Copyright (c) 2007-2011, 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.
+

+ 177 - 0
ext/libnatpmp/Makefile

@@ -0,0 +1,177 @@
+# $Id: Makefile,v 1.23 2013/11/26 16:38:15 nanard Exp $
+# This Makefile is designed for use with GNU make
+# libnatpmp
+# (c) 2007-2013 Thomas Bernard
+# http://miniupnp.free.fr/libnatpmp.html
+
+OS = $(shell uname -s)
+CC = gcc
+INSTALL = install -p
+ARCH = $(shell uname -m | sed -e s/i.86/i686/)
+VERSION = $(shell cat VERSION)
+
+ifeq ($(OS), Darwin)
+JARSUFFIX=mac
+endif
+ifeq ($(OS), Linux)
+JARSUFFIX=linux
+endif
+ifneq (,$(findstring WIN,$(OS)))
+JARSUFFIX=win32
+endif
+
+# APIVERSION is used in soname
+APIVERSION = 1
+#LDFLAGS = -Wl,--no-undefined
+CFLAGS ?= -Os
+#CFLAGS = -g -O0
+CFLAGS += -fPIC
+CFLAGS += -Wall
+#CFLAGS += -Wextra
+CFLAGS += -DENABLE_STRNATPMPERR
+#CFLAGS += -Wstrict-aliasing
+
+LIBOBJS = natpmp.o getgateway.o
+
+OBJS = $(LIBOBJS) testgetgateway.o natpmpc.o natpmp-jni.o
+
+STATICLIB = libnatpmp.a
+ifeq ($(OS), Darwin)
+  SHAREDLIB = libnatpmp.dylib
+  JNISHAREDLIB = libjninatpmp.dylib
+  SONAME = $(basename $(SHAREDLIB)).$(APIVERSION).dylib
+  CFLAGS := -DMACOSX -D_DARWIN_C_SOURCE $(CFLAGS)
+  SONAMEFLAGS=-Wl,-install_name,$(JNISHAREDLIB) -dynamiclib -framework JavaVM
+else
+ifneq (,$(findstring WIN,$(OS)))
+  SHAREDLIB = natpmp.dll
+  JNISHAREDLIB = jninatpmp.dll
+  CC = i686-w64-mingw32-gcc
+  EXTRA_LD = -lws2_32 -lIphlpapi -Wl,--no-undefined -Wl,--enable-runtime-pseudo-reloc --Wl,kill-at
+else
+  SHAREDLIB = libnatpmp.so
+  JNISHAREDLIB = libjninatpmp.so
+  SONAME = $(SHAREDLIB).$(APIVERSION)
+  SONAMEFLAGS=-Wl,-soname,$(JNISHAREDLIB)
+endif
+endif
+
+HEADERS = natpmp.h
+
+EXECUTABLES = testgetgateway natpmpc-shared natpmpc-static
+
+INSTALLPREFIX ?= $(PREFIX)/usr
+INSTALLDIRINC = $(INSTALLPREFIX)/include
+INSTALLDIRLIB = $(INSTALLPREFIX)/lib
+INSTALLDIRBIN = $(INSTALLPREFIX)/bin
+
+JAVA ?= java
+JAVAC ?= javac
+JAVAH ?= javah
+JAVAPACKAGE = fr/free/miniupnp/libnatpmp
+JAVACLASSES = $(JAVAPACKAGE)/NatPmp.class $(JAVAPACKAGE)/NatPmpResponse.class $(JAVAPACKAGE)/LibraryExtractor.class $(JAVAPACKAGE)/URLUtils.class
+JNIHEADERS = fr_free_miniupnp_libnatpmp_NatPmp.h
+
+.PHONY:	all clean depend install cleaninstall installpythonmodule
+
+all: $(STATICLIB) $(SHAREDLIB) $(EXECUTABLES)
+
+pythonmodule: $(STATICLIB) libnatpmpmodule.c setup.py
+	python setup.py build
+	touch $@
+
+installpythonmodule: pythonmodule
+	python setup.py install
+
+clean:
+	$(RM) $(OBJS) $(EXECUTABLES) $(STATICLIB) $(SHAREDLIB) $(JAVACLASSES) $(JNISHAREDLIB)
+	$(RM) pythonmodule
+	$(RM) -r build/ dist/ libraries/
+
+depend:
+	makedepend -f$(MAKEFILE_LIST) -Y $(OBJS:.o=.c) 2>/dev/null
+
+install:	$(HEADERS) $(STATICLIB) $(SHAREDLIB) natpmpc-shared
+	$(INSTALL) -d $(INSTALLDIRINC)
+	$(INSTALL) -m 644 $(HEADERS) $(INSTALLDIRINC)
+	$(INSTALL) -d $(INSTALLDIRLIB)
+	$(INSTALL) -m 644 $(STATICLIB) $(INSTALLDIRLIB)
+	$(INSTALL) -m 644 $(SHAREDLIB) $(INSTALLDIRLIB)/$(SONAME)
+	$(INSTALL) -d $(INSTALLDIRBIN)
+	$(INSTALL) -m 755 natpmpc-shared $(INSTALLDIRBIN)/natpmpc
+	ln -s -f $(SONAME) $(INSTALLDIRLIB)/$(SHAREDLIB)
+
+$(JNIHEADERS): fr/free/miniupnp/libnatpmp/NatPmp.class
+	$(JAVAH) -jni fr.free.miniupnp.libnatpmp.NatPmp
+
+%.class: %.java
+	$(JAVAC) -cp . $<
+
+$(JNISHAREDLIB): $(SHAREDLIB) $(JNIHEADERS) $(JAVACLASSES)
+ifeq (,$(JAVA_HOME))
+	@echo "Check your JAVA_HOME environement variable" && false
+endif
+ifneq (,$(findstring WIN,$(OS)))
+	$(CC) -m32 -D_JNI_Implementation_ -Wl,--kill-at \
+	-I"$(JAVA_HOME)/include" -I"$(JAVA_HOME)/include/win32" \
+	natpmp-jni.c -shared \
+	-o $(JNISHAREDLIB) -L. -lnatpmp -lws2_32 -lIphlpapi
+else
+	$(CC) $(CFLAGS) -c -I"$(JAVA_HOME)/include" -I"$(JAVA_HOME)/include/win32" natpmp-jni.c
+	$(CC) $(CFLAGS) -o $(JNISHAREDLIB) -shared $(SONAMEFLAGS) natpmp-jni.o -lc -L. -lnatpmp
+endif
+
+jar: $(JNISHAREDLIB)
+	find fr -name '*.class' -print > classes.list
+	$(eval JNISHAREDLIBPATH := $(shell java fr.free.miniupnp.libnatpmp.LibraryExtractor))
+	mkdir -p libraries/$(JNISHAREDLIBPATH)
+	mv $(JNISHAREDLIB) libraries/$(JNISHAREDLIBPATH)/$(JNISHAREDLIB)
+	jar cf natpmp_$(JARSUFFIX).jar @classes.list libraries/$(JNISHAREDLIBPATH)/$(JNISHAREDLIB)
+	$(RM) classes.list
+
+jnitest: $(JNISHAREDLIB) JavaTest.class
+	$(RM) libjninatpmp.so
+	$(JAVA) -Djna.nosys=true -cp . JavaTest
+
+mvn_install:
+	mvn install:install-file -Dfile=java/natpmp_$(JARSUFFIX).jar \
+	 -DgroupId=com.github \
+	 -DartifactId=natpmp \
+	 -Dversion=$(VERSION) \
+	 -Dpackaging=jar \
+	 -Dclassifier=$(JARSUFFIX) \
+	 -DgeneratePom=true \
+	 -DcreateChecksum=true
+
+cleaninstall:
+	$(RM) $(addprefix $(INSTALLDIRINC), $(HEADERS))
+	$(RM) $(INSTALLDIRLIB)/$(SONAME)
+	$(RM) $(INSTALLDIRLIB)/$(SHAREDLIB)
+	$(RM) $(INSTALLDIRLIB)/$(STATICLIB)
+
+testgetgateway:	testgetgateway.o getgateway.o
+	$(CC) $(LDFLAGS) -o $@ $^ $(EXTRA_LD)
+
+natpmpc-static:	natpmpc.o $(STATICLIB)
+	$(CC) $(LDFLAGS) -o $@ $^ $(EXTRA_LD)
+
+natpmpc-shared:	natpmpc.o $(SHAREDLIB)
+	$(CC) $(LDFLAGS) -o $@ $^ $(EXTRA_LD)
+
+$(STATICLIB):	$(LIBOBJS)
+	$(AR) crs $@ $?
+
+$(SHAREDLIB):	$(LIBOBJS)
+ifeq ($(OS), Darwin)
+	$(CC) -dynamiclib -Wl,-install_name,$(SONAME) -o $@ $^
+else
+	$(CC) -shared -Wl,-soname,$(SONAME) -o $@ $^ $(EXTRA_LD)
+endif
+
+
+# DO NOT DELETE
+
+natpmp.o: natpmp.h getgateway.h declspec.h
+getgateway.o: getgateway.h declspec.h
+testgetgateway.o: getgateway.h declspec.h
+natpmpc.o: natpmp.h

+ 7 - 0
ext/libnatpmp/README

@@ -0,0 +1,7 @@
+libnatpmp (c) 2007-2009 Thomas Bernard
+contact : [email protected]
+
+see http://miniupnp.free.fr/libnatpmp.html
+or http://miniupnp.tuxfamily.org/libnatpmp.html
+for some documentation and code samples.
+

+ 30 - 0
ext/libnatpmp/build.bat

@@ -0,0 +1,30 @@
+@echo Compiling with MinGW
+@SET LIBS=-lws2_32 -liphlpapi
+
+@echo Compile getgateway
+gcc -c -Wall -Os -DWIN32 -DSTATICLIB -DENABLE_STRNATPMPERR getgateway.c
+gcc -c -Wall -Os -DWIN32 -DSTATICLIB -DENABLE_STRNATPMPERR testgetgateway.c
+gcc -o testgetgateway getgateway.o testgetgateway.o %LIBS%
+del testgetgateway.o
+
+@echo Compile natpmp-static:
+gcc -c -Wall -Os -DWIN32 -DSTATICLIB -DENABLE_STRNATPMPERR getgateway.c
+gcc -c -Wall -Os -DWIN32 -DSTATICLIB -DENABLE_STRNATPMPERR natpmp.c
+gcc -c -Wall -Os -DWIN32 wingettimeofday.c
+ar cr natpmp.a getgateway.o natpmp.o wingettimeofday.o
+del getgateway.o natpmp.o
+gcc -c -Wall -Os -DWIN32 -DSTATICLIB -DENABLE_STRNATPMPERR natpmpc.c
+gcc -o natpmpc-static natpmpc.o natpmp.a %LIBS%
+upx --best natpmpc-static.exe
+del natpmpc.o
+
+@echo Create natpmp.dll:
+gcc -c -Wall -Os -DWIN32 -DENABLE_STRNATPMPERR -DNATPMP_EXPORTS getgateway.c
+gcc -c -Wall -Os -DWIN32 -DENABLE_STRNATPMPERR -DNATPMP_EXPORTS natpmp.c
+dllwrap -k --driver-name gcc --def natpmp.def --output-def natpmp.dll.def --implib natpmp.lib -o natpmp.dll getgateway.o natpmp.o wingettimeofday.o %LIBS%
+
+@echo Compile natpmp-shared:
+gcc -c -Wall -Os -DWIN32 -DENABLE_STRNATPMPERR -DNATPMP_EXPORTS natpmpc.c
+gcc -o natpmpc-shared natpmpc.o natpmp.lib -lws2_32
+upx --best natpmpc-shared.exe
+del *.o

+ 21 - 0
ext/libnatpmp/declspec.h

@@ -0,0 +1,21 @@
+#ifndef DECLSPEC_H_INCLUDED
+#define DECLSPEC_H_INCLUDED
+
+#if defined(WIN32) && !defined(STATICLIB)
+	/* for windows dll */
+	#ifdef NATPMP_EXPORTS
+		#define LIBSPEC __declspec(dllexport)
+	#else
+		#define 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 LIBSPEC __attribute__ ((visibility ("default")))
+	#else
+		#define LIBSPEC
+	#endif
+#endif
+
+#endif
+

+ 238 - 0
ext/libnatpmp/fr/free/miniupnp/libnatpmp/LibraryExtractor.java

@@ -0,0 +1,238 @@
+package fr.free.miniupnp.libnatpmp;
+
+/** I (Leah X Schmidt) copied this code from jnaerator, because
+JNAerator's extractor requires you to buy into the whole JNA
+concept.
+
+JNAErator is
+Copyright (c) 2009 Olivier Chafik, All Rights Reserved
+
+JNAerator is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+JNAerator is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with JNAerator.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class LibraryExtractor {
+
+    private static boolean libPathSet = false;
+
+    public static String getLibraryPath(String libraryName, boolean extractAllLibraries, Class<?> cl) {
+        try {
+            String customPath = System.getProperty("library." + libraryName);
+            if (customPath == null)
+                customPath = System.getenv(libraryName.toUpperCase() + "_LIBRARY");
+            if (customPath != null) {
+                File f = new File(customPath);
+                if (!f.exists())
+                    System.err.println("Library file '" + customPath + "' does not exist !");
+                else
+                    return f.getAbsolutePath();
+            }
+            //ClassLoader cl = LibraryExtractor.class.getClassLoader();
+            String prefix = "(?i)" + (isWindows() ? "" : "lib") + libraryName + "[^A-Za-z_].*";
+            String libsuffix = "(?i).*\\.(so|dll|dylib|jnilib)";
+            //String othersuffix = "(?i).*\\.(pdb)";
+
+            URL sourceURL = null;
+            List<URL> otherURLs = new ArrayList<URL>();
+
+
+            String arch = getCurrentOSAndArchString();
+            //System.out.println("libURL = " + libURL);
+            List<URL> list = URLUtils.listFiles(URLUtils.getResource(cl, "libraries/" + arch)),
+                noArchList = URLUtils.listFiles(URLUtils.getResource(cl, "libraries/noarch"));
+
+            Set<String> names = new HashSet<String>();
+            for (URL url : list) {
+                String name = getFileName(url);
+                names.add(name);
+            }
+            for (URL url : noArchList) {
+                String name = getFileName(url);
+                if (names.add(name))
+                    list.add(url);
+            }
+
+            for (File f : new File(".").listFiles())
+                if (f.isFile())
+                    list.add(f.toURI().toURL());
+
+            for (URL url : list) {
+                String name = getFileName(url);
+                boolean pref = name.matches(prefix), suff = name.matches(libsuffix);
+                if (pref && suff)
+                    sourceURL = url;
+                else //if (suff || fileName.matches(othersuffix))
+                    otherURLs.add(url);
+            }
+            List<File> files = new ArrayList<File>();
+            if (extractAllLibraries) {
+                for (URL url : otherURLs)
+                    files.add(extract(url));
+            }
+
+            if (System.getProperty("javawebstart.version") != null) {
+                if (isWindows()) {
+                    //File f = new File("c:\\Windows\\" + (Platform.is64Bit() ? "SysWOW64\\" : "System32\\") + libraryName + ".dll");
+                    File f = new File("c:\\Windows\\" + "System32\\" + libraryName + ".dll");
+                    if (f.exists())
+                        return f.toString();
+                } else if (isMac()) {
+                    File f = new File("/System/Library/Frameworks/" + libraryName + ".framework/" + libraryName);
+                    if (f.exists())
+                        return f.toString();
+                }
+            }
+
+            if (sourceURL == null)
+                return libraryName;
+            else {
+                File file = extract(sourceURL);
+                files.add(file);
+
+                int lastSize;
+                do {
+                    lastSize = files.size();
+                    for (Iterator<File> it = files.iterator(); it.hasNext();) {
+                        File f = it.next();
+                        if (!f.getName().matches(libsuffix))
+                            continue;
+
+                        try {
+                            System.load(f.toString());
+                            it.remove();
+                        } catch (Throwable ex) {
+                            System.err.println("Loading " + f.getName() + " failed (" + ex + ")");
+                        }
+                    }
+                } while (files.size() < lastSize);
+
+                return file.getCanonicalPath();
+            }
+        } catch (Throwable ex) {
+            System.err.println("ERROR: Failed to extract library " + libraryName);
+            ex.printStackTrace();
+            return libraryName;
+        }
+    }
+
+    public static final boolean isWindows() {
+        String osName = System.getProperty("os.name");
+        return osName.startsWith("Windows");
+    }
+
+    public static final boolean isMac() {
+        String osName = System.getProperty("os.name");
+        return osName.startsWith("Mac") || osName.startsWith("Darwin");
+    }
+
+    //this code is from JNA, but JNA has a fallback to some native
+    //stuff in case this doesn't work.  Since sun.arch.data.model is
+    //defined for Sun and IBM, this should work nearly everywhere.
+    public static final boolean is64Bit() {
+        String model = System.getProperty("sun.arch.data.model",
+                                          System.getProperty("com.ibm.vm.bitmode"));
+        if (model != null) {
+            return "64".equals(model);
+        }
+        String arch = System.getProperty("os.arch").toLowerCase();
+        if ("x86_64".equals(arch)
+            || "ia64".equals(arch)
+            || "ppc64".equals(arch)
+            || "sparcv9".equals(arch)
+            || "amd64".equals(arch)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public static String getCurrentOSAndArchString() {
+        String os = System.getProperty("os.name"), arch = System.getProperty("os.arch");
+        if (os.equals("Mac OS X")) {
+            os = "darwin";
+            arch = "fat";
+        } else if (os.startsWith("Windows")) {
+            return "win" + (is64Bit() ? "64" : "32");
+        } else if (os.matches("SunOS|Solaris"))
+            os = "solaris";
+        return os + "-" + arch;
+    }
+
+    private static File extract(URL url) throws IOException {
+        File localFile;
+        if ("file".equals(url.getProtocol()))
+            localFile = new File(URLDecoder.decode(url.getFile(), "UTF-8"));
+        else {
+            File f = new File(System.getProperty("user.home"));
+            f = new File(f, ".jnaerator");
+            f = new File(f, "extractedLibraries");
+            if (!f.exists())
+                f.mkdirs();
+
+            if (!libPathSet) {
+                String path = System.getProperty("java.library.path");
+                if (path == null) {
+                    System.setProperty("java.library.path", f.toString());
+                } else {
+                    System.setProperty("java.library.path", path + ":" + f);
+                }
+                
+                libPathSet = true;
+            }
+            localFile = new File(f, new File(url.getFile()).getName());
+			URLConnection c = url.openConnection();
+			if (localFile.exists() && localFile.lastModified() > c.getLastModified()) {
+                            c.getInputStream().close();
+			} else {
+                            System.out.println("Extracting " + url);
+                            InputStream in = c.getInputStream();
+                            OutputStream out = new FileOutputStream(localFile);
+                            int len;
+                            byte[] b = new byte[1024];
+                            while ((len = in.read(b)) > 0)
+                                out.write(b, 0, len);
+                            out.close();
+                            in.close();
+			}
+        }
+        return localFile;
+    }
+
+    private static String getFileName(URL url) {
+        return new File(url.getFile()).getName();
+    }
+
+    public static void main(String[] args) {
+        System.out.println(getCurrentOSAndArchString());
+    }
+}

+ 50 - 0
ext/libnatpmp/fr/free/miniupnp/libnatpmp/NatPmp.java

@@ -0,0 +1,50 @@
+package fr.free.miniupnp.libnatpmp;
+
+import java.nio.ByteBuffer;
+
+
+public class NatPmp {
+       private static final String JNA_LIBRARY_NAME = LibraryExtractor.getLibraryPath("jninatpmp", true, NatPmp.class);
+
+    static {
+        String s = JNA_LIBRARY_NAME;
+        startup();
+    }
+
+    public ByteBuffer natpmp;
+
+    public NatPmp() {
+        init(0, 0);
+    }
+
+    public NatPmp(int forcedgw) {
+        init(1, forcedgw);
+    }
+
+    /** Cleans up the native resources used by this object.
+     Attempting to use the object after this has been called
+     will lead to crashes.*/
+    public void dispose() {
+        free();
+    }
+
+
+    protected void finalize() {
+        if (natpmp != null)
+            free();
+    }
+
+    private native void init(int forcegw, int forcedgw);
+    private native void free();
+
+    private static native void startup();
+
+    public native int sendPublicAddressRequest();
+    public native int sendNewPortMappingRequest(int protocol, int privateport, int publicport, int lifetime);
+
+    //returns a number of milliseconds, in accordance with Java convention
+    public native long getNatPmpRequestTimeout();
+
+    public native int readNatPmpResponseOrRetry(NatPmpResponse response);
+
+}

+ 28 - 0
ext/libnatpmp/fr/free/miniupnp/libnatpmp/NatPmpResponse.java

@@ -0,0 +1,28 @@
+package fr.free.miniupnp.libnatpmp;
+
+public class NatPmpResponse {
+
+    public static final int TYPE_PUBLICADDRESS=0;
+    public static final int TYPE_UDPPORTMAPPING=1;
+    public static final int TYPE_TCPPORTMAPPING=2;
+
+    /** see TYPE_* constants */
+    public short type;
+    /** NAT-PMP response code */
+    public short resultcode;
+    /** milliseconds since start of epoch */
+    public long epoch;
+    
+    /** only defined if type == 0*/
+    public int addr;
+
+    /** only defined if type != 0 */
+    public int privateport;
+
+    /** only defined if type != 0 */
+    public int mappedpublicport;
+
+    /** only defined if type != 0 */
+    public long lifetime; //milliseconds
+
+}

+ 81 - 0
ext/libnatpmp/fr/free/miniupnp/libnatpmp/URLUtils.java

@@ -0,0 +1,81 @@
+package fr.free.miniupnp.libnatpmp;
+
+/** I (Leah X Schmidt) copied this code from jnaerator, because
+JNAerator's extractor requires you to buy into the whole JNA
+concept.
+
+JNAErator is
+Copyright (c) 2009 Olivier Chafik, All Rights Reserved
+
+JNAerator is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+JNAerator is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with JNAerator.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+public class URLUtils {
+
+    public static URL getResource(Class<?> cl, String path) throws IOException {
+        String clp = cl.getName().replace('.', '/') + ".class";
+        URL clu = cl.getClassLoader().getResource(clp);
+        String s = clu.toString();
+        if (s.endsWith(clp))
+            return new URL(s.substring(0, s.length() - clp.length()) + path);
+        
+        if (s.startsWith("jar:")) {
+            String[] ss = s.split("!");
+            return new URL(ss[1] + "!/" + path);
+        }
+        return null;
+    }
+    
+    public static List<URL> listFiles(URL directory) throws IOException {
+        List<URL> ret = new ArrayList<URL>();
+        String s = directory.toString();
+        if (s.startsWith("jar:")) {
+            String[] ss = s.substring("jar:".length()).split("!");
+            String path = ss[1];
+            URL target = new URL(ss[0]);
+            InputStream tin = target.openStream();
+            try {
+                JarInputStream jin = new JarInputStream(tin);
+                JarEntry je;
+                while ((je = jin.getNextJarEntry()) != null) {
+                    String p = "/" + je.getName();
+                    if (p.startsWith(path) && p.indexOf('/', path.length() + 1) < 0)
+                      
+                      ret.add(new URL("jar:" + target + "!" + p));
+                }
+            } finally {
+                tin.close();
+            }
+        } else if (s.startsWith("file:")) {
+            File f = new File(directory.getFile());
+            File[] ffs = f.listFiles();
+            if (ffs != null)
+                for (File ff : ffs)
+                    ret.add(ff.toURI().toURL());
+        } else 
+            throw new IOException("Cannot list contents of " + directory);
+        
+        return ret;
+    }
+}

+ 573 - 0
ext/libnatpmp/getgateway.c

@@ -0,0 +1,573 @@
+/* $Id: getgateway.c,v 1.25 2014/04/22 10:28:57 nanard Exp $ */
+/* libnatpmp
+
+Copyright (c) 2007-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 <stdio.h>
+#include <ctype.h>
+#ifndef WIN32
+#include <netinet/in.h>
+#endif
+#if !defined(_MSC_VER)
+#include <sys/param.h>
+#endif
+/* There is no portable method to get the default route gateway.
+ * So below are four (or five ?) differents functions implementing this.
+ * Parsing /proc/net/route is for linux.
+ * sysctl is the way to access such informations on BSD systems.
+ * Many systems should provide route information through raw PF_ROUTE
+ * sockets.
+ * In MS Windows, default gateway is found by looking into the registry
+ * or by using GetBestRoute(). */
+#ifdef __linux__
+#define USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#endif
+
+#if defined(BSD) || defined(__FreeBSD_kernel__)
+#undef USE_PROC_NET_ROUTE
+#define USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#endif
+
+#ifdef __APPLE__
+#undef USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#define USE_SYSCTL_NET_ROUTE
+#endif
+
+#if (defined(sun) && defined(__SVR4))
+#undef USE_PROC_NET_ROUTE
+#define USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#endif
+
+#ifdef WIN32
+#undef USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+//#define USE_WIN32_CODE
+#define USE_WIN32_CODE_2
+#endif
+
+#ifdef __CYGWIN__
+#undef USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#define USE_WIN32_CODE
+#include <stdarg.h>
+#include <w32api/windef.h>
+#include <w32api/winbase.h>
+#include <w32api/winreg.h>
+#endif
+
+#ifdef __HAIKU__
+#include <stdlib.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <sys/sockio.h>
+#define USE_HAIKU_CODE
+#endif
+
+#ifdef USE_SYSCTL_NET_ROUTE
+#include <stdlib.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#endif
+#ifdef USE_SOCKET_ROUTE
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#endif
+
+#ifdef USE_WIN32_CODE
+#include <unknwn.h>
+#include <winreg.h>
+#define MAX_KEY_LENGTH 255
+#define MAX_VALUE_LENGTH 16383
+#endif
+
+#ifdef USE_WIN32_CODE_2
+#include <windows.h>
+#include <iphlpapi.h>
+#endif
+
+#include "getgateway.h"
+
+#ifndef WIN32
+#define SUCCESS (0)
+#define FAILED  (-1)
+#endif
+
+#ifdef USE_PROC_NET_ROUTE
+/*
+ parse /proc/net/route which is as follow :
+
+Iface   Destination     Gateway         Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT
+wlan0   0001A8C0        00000000        0001    0       0       0       00FFFFFF        0       0       0
+eth0    0000FEA9        00000000        0001    0       0       0       0000FFFF        0       0       0
+wlan0   00000000        0101A8C0        0003    0       0       0       00000000        0       0       0
+eth0    00000000        00000000        0001    0       0       1000    00000000        0       0       0
+
+ One header line, and then one line by route by route table entry.
+*/
+int getdefaultgateway(in_addr_t * addr)
+{
+	unsigned long d, g;
+	char buf[256];
+	int line = 0;
+	FILE * f;
+	char * p;
+	f = fopen("/proc/net/route", "r");
+	if(!f)
+		return FAILED;
+	while(fgets(buf, sizeof(buf), f)) {
+		if(line > 0) {	/* skip the first line */
+			p = buf;
+			/* skip the interface name */
+			while(*p && !isspace(*p))
+				p++;
+			while(*p && isspace(*p))
+				p++;
+			if(sscanf(p, "%lx%lx", &d, &g)==2) {
+				if(d == 0 && g != 0) { /* default */
+					*addr = g;
+					fclose(f);
+					return SUCCESS;
+				}
+			}
+		}
+		line++;
+	}
+	/* default route not found ! */
+	if(f)
+		fclose(f);
+	return FAILED;
+}
+#endif /* #ifdef USE_PROC_NET_ROUTE */
+
+
+#ifdef USE_SYSCTL_NET_ROUTE
+
+#define ROUNDUP(a) \
+	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+int getdefaultgateway(in_addr_t * addr)
+{
+#if 0
+	/* net.route.0.inet.dump.0.0 ? */
+	int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
+	             NET_RT_DUMP, 0, 0/*tableid*/};
+#endif
+	/* net.route.0.inet.flags.gateway */
+	int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
+	             NET_RT_FLAGS, RTF_GATEWAY};
+	size_t l;
+	char * buf, * p;
+	struct rt_msghdr * rt;
+	struct sockaddr * sa;
+	struct sockaddr * sa_tab[RTAX_MAX];
+	int i;
+	int r = FAILED;
+	if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) {
+		return FAILED;
+	}
+	if(l>0) {
+		buf = malloc(l);
+		if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) {
+			free(buf);
+			return FAILED;
+		}
+		for(p=buf; p<buf+l; p+=rt->rtm_msglen) {
+			rt = (struct rt_msghdr *)p;
+			sa = (struct sockaddr *)(rt + 1);
+			for(i=0; i<RTAX_MAX; i++) {
+				if(rt->rtm_addrs & (1 << i)) {
+					sa_tab[i] = sa;
+					sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len));
+				} else {
+					sa_tab[i] = NULL;
+				}
+			}
+			if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
+              && sa_tab[RTAX_DST]->sa_family == AF_INET
+              && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) {
+				if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) {
+					*addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr;
+					r = SUCCESS;
+				}
+			}
+		}
+		free(buf);
+	}
+	return r;
+}
+#endif /* #ifdef USE_SYSCTL_NET_ROUTE */
+
+
+#ifdef USE_SOCKET_ROUTE
+/* Thanks to Darren Kenny for this code */
+#define NEXTADDR(w, u) \
+        if (rtm_addrs & (w)) {\
+            l = sizeof(struct sockaddr); memmove(cp, &(u), l); cp += l;\
+        }
+
+#define rtm m_rtmsg.m_rtm
+
+struct {
+  struct rt_msghdr m_rtm;
+  char       m_space[512];
+} m_rtmsg;
+
+int getdefaultgateway(in_addr_t *addr)
+{
+  int s, seq, l, rtm_addrs, i;
+  pid_t pid;
+  struct sockaddr so_dst, so_mask;
+  char *cp = m_rtmsg.m_space;
+  struct sockaddr *gate = NULL, *sa;
+  struct rt_msghdr *msg_hdr;
+
+  pid = getpid();
+  seq = 0;
+  rtm_addrs = RTA_DST | RTA_NETMASK;
+
+  memset(&so_dst, 0, sizeof(so_dst));
+  memset(&so_mask, 0, sizeof(so_mask));
+  memset(&rtm, 0, sizeof(struct rt_msghdr));
+
+  rtm.rtm_type = RTM_GET;
+  rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
+  rtm.rtm_version = RTM_VERSION;
+  rtm.rtm_seq = ++seq;
+  rtm.rtm_addrs = rtm_addrs;
+
+  so_dst.sa_family = AF_INET;
+  so_mask.sa_family = AF_INET;
+
+  NEXTADDR(RTA_DST, so_dst);
+  NEXTADDR(RTA_NETMASK, so_mask);
+
+  rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+
+  s = socket(PF_ROUTE, SOCK_RAW, 0);
+
+  if (write(s, (char *)&m_rtmsg, l) < 0) {
+      close(s);
+      return FAILED;
+  }
+
+  do {
+    l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+  } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+
+  close(s);
+
+  msg_hdr = &rtm;
+
+  cp = ((char *)(msg_hdr + 1));
+  if (msg_hdr->rtm_addrs) {
+    for (i = 1; i; i <<= 1)
+      if (i & msg_hdr->rtm_addrs) {
+        sa = (struct sockaddr *)cp;
+        if (i == RTA_GATEWAY )
+          gate = sa;
+
+        cp += sizeof(struct sockaddr);
+      }
+  } else {
+      return FAILED;
+  }
+
+
+  if (gate != NULL ) {
+      *addr = ((struct sockaddr_in *)gate)->sin_addr.s_addr;
+      return SUCCESS;
+  } else {
+      return FAILED;
+  }
+}
+#endif /* #ifdef USE_SOCKET_ROUTE */
+
+#ifdef USE_WIN32_CODE
+LIBSPEC int getdefaultgateway(in_addr_t * addr)
+{
+	HKEY networkCardsKey;
+	HKEY networkCardKey;
+	HKEY interfacesKey;
+	HKEY interfaceKey;
+	DWORD i = 0;
+	DWORD numSubKeys = 0;
+	TCHAR keyName[MAX_KEY_LENGTH];
+	DWORD keyNameLength = MAX_KEY_LENGTH;
+	TCHAR keyValue[MAX_VALUE_LENGTH];
+	DWORD keyValueLength = MAX_VALUE_LENGTH;
+	DWORD keyValueType = REG_SZ;
+	TCHAR gatewayValue[MAX_VALUE_LENGTH];
+	DWORD gatewayValueLength = MAX_VALUE_LENGTH;
+	DWORD gatewayValueType = REG_MULTI_SZ;
+	int done = 0;
+
+	//const char * networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
+	//const char * interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
+#ifdef UNICODE
+	LPCTSTR networkCardsPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
+	LPCTSTR interfacesPath = L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
+#define STR_SERVICENAME	 L"ServiceName"
+#define STR_DHCPDEFAULTGATEWAY L"DhcpDefaultGateway"
+#define STR_DEFAULTGATEWAY	L"DefaultGateway"
+#else
+	LPCTSTR networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
+	LPCTSTR interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
+#define STR_SERVICENAME	 "ServiceName"
+#define STR_DHCPDEFAULTGATEWAY "DhcpDefaultGateway"
+#define STR_DEFAULTGATEWAY	"DefaultGateway"
+#endif
+	// The windows registry lists its primary network devices in the following location:
+	// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards
+	//
+	// Each network device has its own subfolder, named with an index, with various properties:
+	// -NetworkCards
+	//   -5
+	//     -Description = Broadcom 802.11n Network Adapter
+	//     -ServiceName = {E35A72F8-5065-4097-8DFE-C7790774EE4D}
+	//   -8
+	//     -Description = Marvell Yukon 88E8058 PCI-E Gigabit Ethernet Controller
+	//     -ServiceName = {86226414-5545-4335-A9D1-5BD7120119AD}
+	//
+	// The above service name is the name of a subfolder within:
+	// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces
+	//
+	// There may be more subfolders in this interfaces path than listed in the network cards path above:
+	// -Interfaces
+	//   -{3a539854-6a70-11db-887c-806e6f6e6963}
+	//     -DhcpIPAddress = 0.0.0.0
+	//     -[more]
+	//   -{E35A72F8-5065-4097-8DFE-C7790774EE4D}
+	//     -DhcpIPAddress = 10.0.1.4
+	//     -DhcpDefaultGateway = 10.0.1.1
+	//     -[more]
+	//   -{86226414-5545-4335-A9D1-5BD7120119AD}
+	//     -DhcpIpAddress = 10.0.1.5
+	//     -DhcpDefaultGateay = 10.0.1.1
+	//     -[more]
+	//
+	// In order to extract this information, we enumerate each network card, and extract the ServiceName value.
+	// This is then used to open the interface subfolder, and attempt to extract a DhcpDefaultGateway value.
+	// Once one is found, we're done.
+	//
+	// It may be possible to simply enumerate the interface folders until we find one with a DhcpDefaultGateway value.
+	// However, the technique used is the technique most cited on the web, and we assume it to be more correct.
+
+	if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predifined key
+	                                 networkCardsPath,   // Name of registry subkey to open
+	                                 0,                  // Reserved - must be zero
+	                                 KEY_READ,           // Mask - desired access rights
+	                                 &networkCardsKey))  // Pointer to output key
+	{
+		// Unable to open network cards keys
+		return -1;
+	}
+
+	if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predefined key
+	                                 interfacesPath,     // Name of registry subkey to open
+	                                 0,                  // Reserved - must be zero
+	                                 KEY_READ,           // Mask - desired access rights
+	                                 &interfacesKey))    // Pointer to output key
+	{
+		// Unable to open interfaces key
+		RegCloseKey(networkCardsKey);
+		return -1;
+	}
+
+	// Figure out how many subfolders are within the NetworkCards folder
+	RegQueryInfoKey(networkCardsKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+	//printf( "Number of subkeys: %u\n", (unsigned int)numSubKeys);
+
+	// Enumrate through each subfolder within the NetworkCards folder
+	for(i = 0; i < numSubKeys && !done; i++)
+	{
+		keyNameLength = MAX_KEY_LENGTH;
+		if(ERROR_SUCCESS == RegEnumKeyEx(networkCardsKey, // Open registry key
+		                                 i,               // Index of subkey to retrieve
+		                                 keyName,         // Buffer that receives the name of the subkey
+		                                 &keyNameLength,  // Variable that receives the size of the above buffer
+		                                 NULL,            // Reserved - must be NULL
+		                                 NULL,            // Buffer that receives the class string
+		                                 NULL,            // Variable that receives the size of the above buffer
+		                                 NULL))           // Variable that receives the last write time of subkey
+		{
+			if(RegOpenKeyEx(networkCardsKey,  keyName, 0, KEY_READ, &networkCardKey) == ERROR_SUCCESS)
+			{
+				keyValueLength = MAX_VALUE_LENGTH;
+				if(ERROR_SUCCESS == RegQueryValueEx(networkCardKey,   // Open registry key
+				                                    STR_SERVICENAME,    // Name of key to query
+				                                    NULL,             // Reserved - must be NULL
+				                                    &keyValueType,    // Receives value type
+				                                    (LPBYTE)keyValue, // Receives value
+				                                    &keyValueLength)) // Receives value length in bytes
+				{
+//					printf("keyValue: %s\n", keyValue);
+					if(RegOpenKeyEx(interfacesKey, keyValue, 0, KEY_READ, &interfaceKey) == ERROR_SUCCESS)
+					{
+						gatewayValueLength = MAX_VALUE_LENGTH;
+						if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey,         // Open registry key
+						                                    STR_DHCPDEFAULTGATEWAY, // Name of key to query
+						                                    NULL,                 // Reserved - must be NULL
+						                                    &gatewayValueType,    // Receives value type
+						                                    (LPBYTE)gatewayValue, // Receives value
+						                                    &gatewayValueLength)) // Receives value length in bytes
+						{
+							// Check to make sure it's a string
+							if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1))
+							{
+								//printf("gatewayValue: %s\n", gatewayValue);
+								done = 1;
+							}
+						}
+						else if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey,         // Open registry key
+						                                    STR_DEFAULTGATEWAY, // Name of key to query
+						                                    NULL,                 // Reserved - must be NULL
+						                                    &gatewayValueType,    // Receives value type
+						                                    (LPBYTE)gatewayValue,// Receives value
+						                                    &gatewayValueLength)) // Receives value length in bytes
+						{
+							// Check to make sure it's a string
+							if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1))
+							{
+								//printf("gatewayValue: %s\n", gatewayValue);
+								done = 1;
+							}
+						}
+						RegCloseKey(interfaceKey);
+					}
+				}
+				RegCloseKey(networkCardKey);
+			}
+		}
+	}
+
+	RegCloseKey(interfacesKey);
+	RegCloseKey(networkCardsKey);
+
+	if(done)
+	{
+#if UNICODE
+		char tmp[32];
+		for(i = 0; i < 32; i++) {
+			tmp[i] = (char)gatewayValue[i];
+			if(!tmp[i])
+				break;
+		}
+		tmp[31] = '\0';
+		*addr = inet_addr(tmp);
+#else
+		*addr = inet_addr(gatewayValue);
+#endif
+		return 0;
+	}
+
+	return -1;
+}
+#endif /* #ifdef USE_WIN32_CODE */
+
+#ifdef USE_WIN32_CODE_2
+int getdefaultgateway(in_addr_t *addr)
+{
+	MIB_IPFORWARDROW ip_forward;
+	memset(&ip_forward, 0, sizeof(ip_forward));
+	if(GetBestRoute(inet_addr("0.0.0.0"), 0, &ip_forward) != NO_ERROR)
+		return -1;
+	*addr = ip_forward.dwForwardNextHop;
+	return 0;
+}
+#endif /* #ifdef USE_WIN32_CODE_2 */
+
+#ifdef USE_HAIKU_CODE
+int getdefaultgateway(in_addr_t *addr)
+{
+    int fd, ret = -1;
+    struct ifconf config;
+    void *buffer = NULL;
+    struct ifreq *interface;
+
+    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+        return -1;
+    }
+    if (ioctl(fd, SIOCGRTSIZE, &config, sizeof(config)) != 0) {
+        goto fail;
+    }
+    if (config.ifc_value < 1) {
+        goto fail; /* No routes */
+    }
+    if ((buffer = malloc(config.ifc_value)) == NULL) {
+        goto fail;
+    }
+    config.ifc_len = config.ifc_value;
+    config.ifc_buf = buffer;
+    if (ioctl(fd, SIOCGRTTABLE, &config, sizeof(config)) != 0) {
+        goto fail;
+    }
+    for (interface = buffer;
+      (uint8_t *)interface < (uint8_t *)buffer + config.ifc_len; ) {
+        struct route_entry route = interface->ifr_route;
+        int intfSize;
+        if (route.flags & (RTF_GATEWAY | RTF_DEFAULT)) {
+            *addr = ((struct sockaddr_in *)route.gateway)->sin_addr.s_addr;
+            ret = 0;
+            break;
+        }
+        intfSize = sizeof(route) + IF_NAMESIZE;
+        if (route.destination != NULL) {
+            intfSize += route.destination->sa_len;
+        }
+        if (route.mask != NULL) {
+            intfSize += route.mask->sa_len;
+        }
+        if (route.gateway != NULL) {
+            intfSize += route.gateway->sa_len;
+        }
+        interface = (struct ifreq *)((uint8_t *)interface + intfSize);
+    }
+fail:
+    free(buffer);
+    close(fd);
+    return ret;
+}
+#endif /* #ifdef USE_HAIKU_CODE */
+
+#if !defined(USE_PROC_NET_ROUTE) && !defined(USE_SOCKET_ROUTE) && !defined(USE_SYSCTL_NET_ROUTE) && !defined(USE_WIN32_CODE) && !defined(USE_WIN32_CODE_2) && !defined(USE_HAIKU_CODE)
+int getdefaultgateway(in_addr_t * addr)
+{
+	return -1;
+}
+#endif

+ 49 - 0
ext/libnatpmp/getgateway.h

@@ -0,0 +1,49 @@
+/* $Id: getgateway.h,v 1.8 2014/04/22 09:15:40 nanard Exp $ */
+/* libnatpmp
+Copyright (c) 2007-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.
+*/
+#ifndef __GETGATEWAY_H__
+#define __GETGATEWAY_H__
+
+#ifdef WIN32
+#if !defined(_MSC_VER) || _MSC_VER >= 1600
+#include <stdint.h>
+#else
+typedef unsigned long uint32_t;
+typedef unsigned short uint16_t;
+#endif
+#define in_addr_t uint32_t
+#endif
+/* #include "declspec.h" */
+
+/* getdefaultgateway() :
+ * return value :
+ *    0 : success
+ *   -1 : failure    */
+/* LIBSPEC */int getdefaultgateway(in_addr_t * addr);
+
+#endif

+ 281 - 0
ext/libnatpmp/libnatpmpmodule.c

@@ -0,0 +1,281 @@
+/* $Id: libnatpmpmodule.c,v 1.7 2012/03/05 19:38:37 nanard Exp $ */
+/* libnatpmp
+ * http://miniupnp.free.fr/libnatpmp.html
+Copyright (c) 2007-2011, 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 <Python.h>
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#define STATICLIB
+#include "structmember.h"
+#include "natpmp.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
+
+typedef struct {
+  PyObject_HEAD
+
+  /* Type-specific fields go here. */
+  unsigned int discoverdelay;
+
+  natpmp_t natpmp;
+} NATPMPObject;
+
+static PyMemberDef NATPMP_members[] = {
+  {"discoverdelay", T_UINT, offsetof(NATPMPObject, discoverdelay),
+   0/*READWRITE*/, "value in ms used to wait for NATPMP responses"
+  },
+  {NULL}
+};
+
+static PyObject *
+NATPMPObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+  NATPMPObject *self;
+
+  self = (NATPMPObject *)type->tp_alloc(type, 0);
+  if (self) {
+    initnatpmp(&self->natpmp, 0, 0);
+  }
+
+  return (PyObject *)self;
+}
+
+static void
+NATPMPObject_dealloc(NATPMPObject *self)
+{
+  closenatpmp(&self->natpmp);
+  self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *
+NATPMP_externalipaddress(NATPMPObject *self)
+{
+  int r;
+  struct timeval timeout;
+  fd_set fds;
+  natpmpresp_t response;
+
+  r = sendpublicaddressrequest(&self->natpmp);
+
+  if (r < 0) {
+#ifdef ENABLE_STRNATPMPERR
+    PyErr_SetString(PyExc_Exception, strnatpmperr(r));
+#endif
+    return NULL;
+  }
+
+  do {
+    FD_ZERO(&fds);
+    FD_SET(self->natpmp.s, &fds);
+    getnatpmprequesttimeout(&self->natpmp, &timeout);
+    select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+    r = readnatpmpresponseorretry(&self->natpmp, &response);
+    if (r < 0 && r != NATPMP_TRYAGAIN) {
+#ifdef ENABLE_STRNATPMPERR
+      PyErr_SetString(PyExc_Exception, strnatpmperr(r));
+#endif
+      return NULL;
+    }
+  } while (r == NATPMP_TRYAGAIN);
+
+  return Py_BuildValue("s", inet_ntoa(response.pnu.publicaddress.addr));
+}
+
+static PyObject *
+NATPMP_domapping(natpmp_t *n, unsigned short eport, unsigned short iport,
+		 const char *protocol, unsigned int lifetime)
+{
+  int proto;
+  struct timeval timeout;
+  fd_set fds;
+  natpmpresp_t response;
+  int r;
+
+  if (!strncasecmp("tcp", protocol, 3)) {
+    proto = NATPMP_PROTOCOL_TCP;
+  } else if (!strncasecmp("udp", protocol, 3)) {
+    proto = NATPMP_PROTOCOL_UDP;
+  } else {
+    PyErr_SetString(PyExc_Exception, "Unknown protocol");
+    return NULL;
+  }
+
+  r = sendnewportmappingrequest(n, proto, iport, eport,
+				lifetime);
+
+  if (r < 0) {
+#ifdef ENABLE_STRNATPMPERR
+    PyErr_SetString(PyExc_Exception, strnatpmperr(r));
+#endif
+    return NULL;
+  }
+
+  do {
+    FD_ZERO(&fds);
+    FD_SET(n->s, &fds);
+    getnatpmprequesttimeout(n, &timeout);
+    select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+    r = readnatpmpresponseorretry(n, &response);
+    if (r < 0 && r != NATPMP_TRYAGAIN) {
+#ifdef ENABLE_STRNATPMPERR
+      PyErr_SetString(PyExc_Exception, strnatpmperr(r));
+#endif
+      return NULL;
+    }
+  } while (r == NATPMP_TRYAGAIN);
+
+  return Py_BuildValue("H", response.pnu.newportmapping.mappedpublicport);
+}
+
+
+/* AddPortMapping(externalPort, protocol, internalPort, lifetime)
+ * protocol is 'UDP' or 'TCP' */
+static PyObject *
+NATPMP_addportmapping(NATPMPObject *self, PyObject *args)
+{
+  unsigned short eport;
+  unsigned short iport;
+  unsigned int lifetime;
+  const char *protocol;
+
+  if (!PyArg_ParseTuple(args, "HsHI", &eport, &protocol, &iport, &lifetime))
+    return NULL;
+
+  return NATPMP_domapping(&self->natpmp, eport, iport, protocol, lifetime);
+}
+
+/* DeletePortMapping(externalPort, protocol, internalPort)
+ * protocol is 'UDP' or 'TCP' */
+static PyObject *
+NATPMP_deleteportmapping(NATPMPObject *self, PyObject *args)
+{
+  unsigned short eport;
+  unsigned short iport;
+  const char *protocol;
+
+  if (!PyArg_ParseTuple(args, "HsH", &eport, &protocol, &iport))
+    return NULL;
+
+  return NATPMP_domapping(&self->natpmp, eport, iport, protocol, 0);
+}
+
+/* natpmp.NATPMP object Method Table */
+static PyMethodDef NATPMP_methods[] = {
+  {"externalipaddress", (PyCFunction)NATPMP_externalipaddress, METH_NOARGS,
+   "return external IP address"
+  },
+  {"addportmapping", (PyCFunction)NATPMP_addportmapping, METH_VARARGS,
+   "add a port mapping"
+  },
+  {"deleteportmapping", (PyCFunction)NATPMP_deleteportmapping, METH_VARARGS,
+   "delete a port mapping"
+  },
+  {NULL}  /* Sentinel */
+};
+
+static PyTypeObject NATPMPType = {
+  PyObject_HEAD_INIT(NULL)
+  0,					/*ob_size*/
+  "libnatpmp.NATPMP",			/*tp_name*/
+  sizeof(NATPMPObject),			/*tp_basicsize*/
+  0,					/*tp_itemsize*/
+  (destructor)NATPMPObject_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*/
+  "NATPMP objects",			/* tp_doc */
+  0,					/* tp_traverse */
+  0,					/* tp_clear */
+  0,					/* tp_richcompare */
+  0,					/* tp_weaklistoffset */
+  0,					/* tp_iter */
+  0,					/* tp_iternext */
+  NATPMP_methods,			/* tp_methods */
+  NATPMP_members,			/* tp_members */
+  0,					/* tp_getset */
+  0,					/* tp_base */
+  0,					/* tp_dict */
+  0,					/* tp_descr_get */
+  0,					/* tp_descr_set */
+  0,					/* tp_dictoffset */
+  0,					/* tp_init */
+  0,					/* tp_alloc */
+  NATPMPObject_new,			/* tp_new */
+};
+
+/* module methods */
+static PyMethodDef libnatpmp_methods[] = {
+    {NULL}  /* Sentinel */
+};
+
+#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+initlibnatpmp(void)
+{
+  PyObject* m;
+
+  if (PyType_Ready(&NATPMPType) < 0)
+    return;
+
+  m = Py_InitModule3("libnatpmp", libnatpmp_methods,
+		     "libnatpmp module.");
+
+  Py_INCREF(&NATPMPType);
+  PyModule_AddObject(m, "NATPMP", (PyObject *)&NATPMPType);
+}
+

+ 29 - 0
ext/libnatpmp/msvc/libnatpmp.sln

@@ -0,0 +1,29 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual C++ Express 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnatpmp", "libnatpmp.vcproj", "{D59B6527-F3DE-4D26-A08D-52F1EE989301}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "natpmpc-static", "natpmpc-static.vcproj", "{A0B49FA9-98AB-4A74-8B4C-8AB7FA36089B}"
+	ProjectSection(ProjectDependencies) = postProject
+		{D59B6527-F3DE-4D26-A08D-52F1EE989301} = {D59B6527-F3DE-4D26-A08D-52F1EE989301}
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{D59B6527-F3DE-4D26-A08D-52F1EE989301}.Debug|Win32.ActiveCfg = Debug|Win32
+		{D59B6527-F3DE-4D26-A08D-52F1EE989301}.Debug|Win32.Build.0 = Debug|Win32
+		{D59B6527-F3DE-4D26-A08D-52F1EE989301}.Release|Win32.ActiveCfg = Release|Win32
+		{D59B6527-F3DE-4D26-A08D-52F1EE989301}.Release|Win32.Build.0 = Release|Win32
+		{A0B49FA9-98AB-4A74-8B4C-8AB7FA36089B}.Debug|Win32.ActiveCfg = Debug|Win32
+		{A0B49FA9-98AB-4A74-8B4C-8AB7FA36089B}.Debug|Win32.Build.0 = Debug|Win32
+		{A0B49FA9-98AB-4A74-8B4C-8AB7FA36089B}.Release|Win32.ActiveCfg = Release|Win32
+		{A0B49FA9-98AB-4A74-8B4C-8AB7FA36089B}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 195 - 0
ext/libnatpmp/msvc/libnatpmp.vcproj

@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9,00"
+	Name="libnatpmp"
+	ProjectGUID="{D59B6527-F3DE-4D26-A08D-52F1EE989301}"
+	RootNamespace="libnatpmp"
+	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="WIN32;_DEBUG;_LIB;STATICLIB"
+				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="WIN32;NDEBUG;_LIB;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="..\getgateway.c"
+				>
+			</File>
+			<File
+				RelativePath="..\natpmp.c"
+				>
+			</File>
+			<File
+				RelativePath="..\wingettimeofday.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="..\declspec.h"
+				>
+			</File>
+			<File
+				RelativePath="..\getgateway.h"
+				>
+			</File>
+			<File
+				RelativePath="..\natpmp.h"
+				>
+			</File>
+			<File
+				RelativePath="..\wingettimeofday.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>

+ 195 - 0
ext/libnatpmp/msvc/natpmpc-static.vcproj

@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9,00"
+	Name="natpmpc-static"
+	ProjectGUID="{A0B49FA9-98AB-4A74-8B4C-8AB7FA36089B}"
+	RootNamespace="natpmpcstatic"
+	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="WIN32;_DEBUG;_CONSOLE;STATICLIB;_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"
+				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="WIN32;NDEBUG;_CONSOLE;STATICLIB;_CRT_SECURE_NO_WARNINGS"
+				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"
+				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="..\natpmpc.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>

+ 157 - 0
ext/libnatpmp/natpmp-jni.c

@@ -0,0 +1,157 @@
+#ifdef __CYGWIN__
+#include <stdint.h>
+#define __int64 uint64_t
+#endif
+
+#ifdef WIN32
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+
+#include <stdlib.h>
+#include "natpmp.h"
+
+#include "fr_free_miniupnp_libnatpmp_NatPmp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT void JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_init (JNIEnv *env, jobject obj, jint forcegw, jint forcedgw) {
+  natpmp_t *p = malloc (sizeof(natpmp_t));
+  if (p == NULL) return;
+
+  initnatpmp(p, forcegw, (in_addr_t) forcedgw);
+
+  jobject wrapped =  (*env)->NewDirectByteBuffer(env, p, sizeof(natpmp_t));
+  if (wrapped == NULL) return;
+
+  jclass thisClass = (*env)->GetObjectClass(env,obj);
+  if (thisClass == NULL) return;
+
+  jfieldID fid = (*env)->GetFieldID(env, thisClass, "natpmp", "Ljava/nio/ByteBuffer;");
+  if (fid == NULL) return;
+  (*env)->SetObjectField(env, obj, fid, wrapped);
+}
+
+JNIEXPORT void JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_free (JNIEnv *env, jobject obj) {
+
+  jclass thisClass = (*env)->GetObjectClass(env,obj);
+  if (thisClass == NULL) return;
+
+  jfieldID fid = (*env)->GetFieldID(env, thisClass, "natpmp", "Ljava/nio/ByteBuffer;");
+
+  if (fid == NULL) return;
+  jobject wrapped = (*env)->GetObjectField(env, obj, fid);
+  if (wrapped == NULL) return;
+
+  natpmp_t* natpmp = (natpmp_t*) (*env)->GetDirectBufferAddress(env, wrapped);
+
+  closenatpmp(natpmp);
+
+  if (natpmp == NULL) return;
+  free(natpmp);
+
+  (*env)->SetObjectField(env, obj, fid, NULL);
+}
+
+static natpmp_t* getNatPmp(JNIEnv* env, jobject obj) {
+  jclass thisClass = (*env)->GetObjectClass(env,obj);
+  if (thisClass == NULL) return NULL;
+
+  jfieldID fid = (*env)->GetFieldID(env, thisClass, "natpmp", "Ljava/nio/ByteBuffer;");
+
+  if (fid == NULL) return NULL;
+  jobject wrapped = (*env)->GetObjectField(env, obj, fid);
+  if (wrapped == NULL) return NULL;
+
+  natpmp_t* natpmp = (natpmp_t*) (*env)->GetDirectBufferAddress(env, wrapped);
+
+  return natpmp;
+}
+
+JNIEXPORT jint JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_sendPublicAddressRequest(JNIEnv* env, jobject obj) {
+  natpmp_t* natpmp = getNatPmp(env, obj);
+  if (natpmp == NULL) return -1;
+
+  return sendpublicaddressrequest(natpmp);
+}
+
+
+JNIEXPORT void JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_startup(JNIEnv* env, jclass cls) {
+  (void)env;
+  (void)cls;
+#ifdef WIN32
+  WSADATA wsaData;
+  WORD wVersionRequested = MAKEWORD(2, 2);
+  WSAStartup(wVersionRequested, &wsaData);
+#endif
+}
+
+
+JNIEXPORT jint JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_sendNewPortMappingRequest(JNIEnv* env, jobject obj, jint protocol, jint privateport, jint publicport, jint lifetime) {
+  natpmp_t* natpmp = getNatPmp(env, obj);
+  if (natpmp == NULL) return -1;
+
+  return sendnewportmappingrequest(natpmp, protocol, privateport, publicport, lifetime);
+}
+
+JNIEXPORT jlong JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_getNatPmpRequestTimeout(JNIEnv* env, jobject obj) {
+  natpmp_t* natpmp = getNatPmp(env, obj);
+
+  struct timeval timeout;
+
+  getnatpmprequesttimeout(natpmp, &timeout);
+
+  return ((jlong) timeout.tv_sec) * 1000 + (timeout.tv_usec / 1000);
+
+}
+
+#define SET_FIELD(prefix, name, type, longtype) {                  \
+  jfieldID fid = (*env)->GetFieldID(env, thisClass, #name, type); \
+  if (fid == NULL) return -1; \
+  (*env)->Set ## longtype ## Field(env, response, fid, resp. prefix name);     \
+}
+
+JNIEXPORT jint JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_readNatPmpResponseOrRetry(JNIEnv* env, jobject obj, jobject response) {
+
+  natpmp_t* natpmp = getNatPmp(env, obj);
+  natpmpresp_t resp;
+  int result = readnatpmpresponseorretry(natpmp, &resp);
+
+  if (result != 0) {
+    return result;
+  }
+
+  jclass thisClass = (*env)->GetObjectClass(env, response);
+  if (thisClass == NULL) return -1;
+
+  SET_FIELD(,type, "S", Short);
+  SET_FIELD(,resultcode, "S", Short);
+
+  jfieldID fid = (*env)->GetFieldID(env, thisClass, "epoch", "J");
+  if (fid == NULL) return -1;
+  (*env)->SetLongField(env, response, fid, ((jlong)resp.epoch) * 1000);
+
+  if (resp.type == 0) {
+  jfieldID fid = (*env)->GetFieldID(env, thisClass, "addr", "I");
+  if (fid == NULL) return -1;
+  (*env)->SetIntField(env, response, fid, resp.pnu.publicaddress.addr.s_addr);
+
+
+  } else {
+    SET_FIELD(pnu.newportmapping., privateport, "I", Int);
+    SET_FIELD(pnu.newportmapping., mappedpublicport, "I", Int);
+
+    jfieldID fid = (*env)->GetFieldID(env, thisClass, "lifetime", "J");
+    if (fid == NULL) return -1;
+    (*env)->SetLongField(env, response, fid, ((jlong) resp.pnu.newportmapping.lifetime) * 1000 * 1000);
+  }
+  return result;
+}
+
+
+#ifdef __cplusplus
+}
+#endif

+ 379 - 0
ext/libnatpmp/natpmp.c

@@ -0,0 +1,379 @@
+/* $Id: natpmp.c,v 1.20 2015/05/27 12:43:15 nanard Exp $ */
+/* libnatpmp
+Copyright (c) 2007-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.
+*/
+#ifdef __linux__
+#define _BSD_SOURCE 1
+#endif
+#include <string.h>
+#include <time.h>
+#if !defined(_MSC_VER)
+#include <sys/time.h>
+#endif
+#ifdef WIN32
+#include <errno.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define ECONNREFUSED WSAECONNREFUSED
+#include "wingettimeofday.h"
+#define gettimeofday natpmp_gettimeofday
+#else
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#define closesocket close
+#endif
+#include "natpmp.h"
+#include "getgateway.h"
+#include <stdio.h>
+
+LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw)
+{
+#ifdef WIN32
+	u_long ioctlArg = 1;
+#else
+	int flags;
+#endif
+	struct sockaddr_in addr;
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+	memset(p, 0, sizeof(natpmp_t));
+	p->s = socket(PF_INET, SOCK_DGRAM, 0);
+	if(p->s < 0)
+		return NATPMP_ERR_SOCKETERROR;
+#ifdef WIN32
+	if(ioctlsocket(p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR)
+		return NATPMP_ERR_FCNTLERROR;
+#else
+	if((flags = fcntl(p->s, F_GETFL, 0)) < 0)
+		return NATPMP_ERR_FCNTLERROR;
+	if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0)
+		return NATPMP_ERR_FCNTLERROR;
+#endif
+
+	if(forcegw) {
+		p->gateway = forcedgw;
+	} else {
+		if(getdefaultgateway(&(p->gateway)) < 0)
+			return NATPMP_ERR_CANNOTGETGATEWAY;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(NATPMP_PORT);
+	addr.sin_addr.s_addr = p->gateway;
+	if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+		return NATPMP_ERR_CONNECTERR;
+	return 0;
+}
+
+LIBSPEC int closenatpmp(natpmp_t * p)
+{
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+	if(closesocket(p->s) < 0)
+		return NATPMP_ERR_CLOSEERR;
+	return 0;
+}
+
+int sendpendingrequest(natpmp_t * p)
+{
+	int r;
+/*	struct sockaddr_in addr;*/
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+/*	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(NATPMP_PORT);
+	addr.sin_addr.s_addr = p->gateway;
+	r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
+	                   (struct sockaddr *)&addr, sizeof(addr));*/
+	r = (int)send(p->s, (const char *)p->pending_request, p->pending_request_len, 0);
+	return (r<0) ? NATPMP_ERR_SENDERR : r;
+}
+
+int sendnatpmprequest(natpmp_t * p)
+{
+	int n;
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+	/* TODO : check if no request is already pending */
+	p->has_pending_request = 1;
+	p->try_number = 1;
+	n = sendpendingrequest(p);
+	gettimeofday(&p->retry_time, NULL);	// check errors !
+	p->retry_time.tv_usec += 250000;	/* add 250ms */
+	if(p->retry_time.tv_usec >= 1000000) {
+		p->retry_time.tv_usec -= 1000000;
+		p->retry_time.tv_sec++;
+	}
+	return n;
+}
+
+LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout)
+{
+	struct timeval now;
+	if(!p || !timeout)
+		return NATPMP_ERR_INVALIDARGS;
+	if(!p->has_pending_request)
+		return NATPMP_ERR_NOPENDINGREQ;
+	if(gettimeofday(&now, NULL) < 0)
+		return NATPMP_ERR_GETTIMEOFDAYERR;
+	timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
+	timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
+	if(timeout->tv_usec < 0) {
+		timeout->tv_usec += 1000000;
+		timeout->tv_sec--;
+	}
+	return 0;
+}
+
+LIBSPEC int sendpublicaddressrequest(natpmp_t * p)
+{
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+	//static const unsigned char request[] = { 0, 0 };
+	p->pending_request[0] = 0;
+	p->pending_request[1] = 0;
+	p->pending_request_len = 2;
+	// TODO: return 0 instead of sizeof(request) ??
+	return sendnatpmprequest(p);
+}
+
+LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol,
+                              uint16_t privateport, uint16_t publicport,
+							  uint32_t lifetime)
+{
+	if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP))
+		return NATPMP_ERR_INVALIDARGS;
+	p->pending_request[0] = 0;
+	p->pending_request[1] = protocol;
+	p->pending_request[2] = 0;
+	p->pending_request[3] = 0;
+	/* break strict-aliasing rules :
+	*((uint16_t *)(p->pending_request + 4)) = htons(privateport); */
+	p->pending_request[4] = (privateport >> 8) & 0xff;
+	p->pending_request[5] = privateport & 0xff;
+	/* break stric-aliasing rules :
+	*((uint16_t *)(p->pending_request + 6)) = htons(publicport); */
+	p->pending_request[6] = (publicport >> 8) & 0xff;
+	p->pending_request[7] = publicport & 0xff;
+	/* break stric-aliasing rules :
+	*((uint32_t *)(p->pending_request + 8)) = htonl(lifetime); */
+	p->pending_request[8] = (lifetime >> 24) & 0xff;
+	p->pending_request[9] = (lifetime >> 16) & 0xff;
+	p->pending_request[10] = (lifetime >> 8) & 0xff;
+	p->pending_request[11] = lifetime & 0xff;
+	p->pending_request_len = 12;
+	return sendnatpmprequest(p);
+}
+
+LIBSPEC int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response)
+{
+	unsigned char buf[16];
+	struct sockaddr_in addr;
+	socklen_t addrlen = sizeof(addr);
+	int n;
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+	n = recvfrom(p->s, (char *)buf, sizeof(buf), 0,
+	             (struct sockaddr *)&addr, &addrlen);
+	if(n<0)
+#ifdef WIN32
+		switch(WSAGetLastError()) {
+#else
+		switch(errno) {
+#endif
+		/*case EAGAIN:*/
+		case EWOULDBLOCK:
+			n = NATPMP_TRYAGAIN;
+			break;
+		case ECONNREFUSED:
+			n = NATPMP_ERR_NOGATEWAYSUPPORT;
+			break;
+		default:
+			n = NATPMP_ERR_RECVFROM;
+		}
+	/* check that addr is correct (= gateway) */
+	else if(addr.sin_addr.s_addr != p->gateway)
+		n = NATPMP_ERR_WRONGPACKETSOURCE;
+	else {
+		response->resultcode = ntohs(*((uint16_t *)(buf + 2)));
+		response->epoch = ntohl(*((uint32_t *)(buf + 4)));
+		if(buf[0] != 0)
+			n = NATPMP_ERR_UNSUPPORTEDVERSION;
+		else if(buf[1] < 128 || buf[1] > 130)
+			n = NATPMP_ERR_UNSUPPORTEDOPCODE;
+		else if(response->resultcode != 0) {
+			switch(response->resultcode) {
+			case 1:
+				n = NATPMP_ERR_UNSUPPORTEDVERSION;
+				break;
+			case 2:
+				n = NATPMP_ERR_NOTAUTHORIZED;
+				break;
+			case 3:
+				n = NATPMP_ERR_NETWORKFAILURE;
+				break;
+			case 4:
+				n = NATPMP_ERR_OUTOFRESOURCES;
+				break;
+			case 5:
+				n = NATPMP_ERR_UNSUPPORTEDOPCODE;
+				break;
+			default:
+				n = NATPMP_ERR_UNDEFINEDERROR;
+			}
+		} else {
+			response->type = buf[1] & 0x7f;
+			if(buf[1] == 128)
+				//response->publicaddress.addr = *((uint32_t *)(buf + 8));
+				response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8));
+			else {
+				response->pnu.newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8)));
+				response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10)));
+				response->pnu.newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12)));
+			}
+			n = 0;
+		}
+	}
+	return n;
+}
+
+int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response)
+{
+	int n;
+	if(!p || !response)
+		return NATPMP_ERR_INVALIDARGS;
+	if(!p->has_pending_request)
+		return NATPMP_ERR_NOPENDINGREQ;
+	n = readnatpmpresponse(p, response);
+	if(n<0) {
+		if(n==NATPMP_TRYAGAIN) {
+			struct timeval now;
+			gettimeofday(&now, NULL);	// check errors !
+			if(timercmp(&now, &p->retry_time, >=)) {
+				int delay, r;
+				if(p->try_number >= 9) {
+					return NATPMP_ERR_NOGATEWAYSUPPORT;
+				}
+				/*printf("retry! %d\n", p->try_number);*/
+				delay = 250 * (1<<p->try_number);	// ms
+				/*for(i=0; i<p->try_number; i++)
+					delay += delay;*/
+				p->retry_time.tv_sec += (delay / 1000);
+				p->retry_time.tv_usec += (delay % 1000) * 1000;
+				if(p->retry_time.tv_usec >= 1000000) {
+					p->retry_time.tv_usec -= 1000000;
+					p->retry_time.tv_sec++;
+				}
+				p->try_number++;
+				r = sendpendingrequest(p);
+				if(r<0)
+					return r;
+			}
+		}
+	} else {
+		p->has_pending_request = 0;
+	}
+	return n;
+}
+
+#ifdef ENABLE_STRNATPMPERR
+LIBSPEC const char * strnatpmperr(int r)
+{
+	const char * s;
+	switch(r) {
+	case NATPMP_ERR_INVALIDARGS:
+		s = "invalid arguments";
+		break;
+	case NATPMP_ERR_SOCKETERROR:
+		s = "socket() failed";
+		break;
+	case NATPMP_ERR_CANNOTGETGATEWAY:
+		s = "cannot get default gateway ip address";
+		break;
+	case NATPMP_ERR_CLOSEERR:
+#ifdef WIN32
+		s = "closesocket() failed";
+#else
+		s = "close() failed";
+#endif
+		break;
+	case NATPMP_ERR_RECVFROM:
+		s = "recvfrom() failed";
+		break;
+	case NATPMP_ERR_NOPENDINGREQ:
+		s = "no pending request";
+		break;
+	case NATPMP_ERR_NOGATEWAYSUPPORT:
+		s = "the gateway does not support nat-pmp";
+		break;
+	case NATPMP_ERR_CONNECTERR:
+		s = "connect() failed";
+		break;
+	case NATPMP_ERR_WRONGPACKETSOURCE:
+		s = "packet not received from the default gateway";
+		break;
+	case NATPMP_ERR_SENDERR:
+		s = "send() failed";
+		break;
+	case NATPMP_ERR_FCNTLERROR:
+		s = "fcntl() failed";
+		break;
+	case NATPMP_ERR_GETTIMEOFDAYERR:
+		s = "gettimeofday() failed";
+		break;
+	case NATPMP_ERR_UNSUPPORTEDVERSION:
+		s = "unsupported nat-pmp version error from server";
+		break;
+	case NATPMP_ERR_UNSUPPORTEDOPCODE:
+		s = "unsupported nat-pmp opcode error from server";
+		break;
+	case NATPMP_ERR_UNDEFINEDERROR:
+		s = "undefined nat-pmp server error";
+		break;
+	case NATPMP_ERR_NOTAUTHORIZED:
+		s = "not authorized";
+		break;
+	case NATPMP_ERR_NETWORKFAILURE:
+		s = "network failure";
+		break;
+	case NATPMP_ERR_OUTOFRESOURCES:
+		s = "nat-pmp server out of resources";
+		break;
+	default:
+		s = "Unknown libnatpmp error";
+	}
+	return s;
+}
+#endif
+

+ 11 - 0
ext/libnatpmp/natpmp.def

@@ -0,0 +1,11 @@
+LIBRARY
+; libnatpmp library
+
+EXPORTS
+  initnatpmp
+  closenatpmp
+  sendpublicaddressrequest
+  sendnewportmappingrequest
+  getnatpmprequesttimeout
+  readnatpmpresponseorretry
+  strnatpmperr

+ 219 - 0
ext/libnatpmp/natpmp.h

@@ -0,0 +1,219 @@
+/* $Id: natpmp.h,v 1.20 2014/04/22 09:15:40 nanard Exp $ */
+/* libnatpmp
+Copyright (c) 2007-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.
+*/
+#ifndef __NATPMP_H__
+#define __NATPMP_H__
+
+/* NAT-PMP Port as defined by the NAT-PMP draft */
+#define NATPMP_PORT (5351)
+
+#include <time.h>
+#if !defined(_MSC_VER)
+#include <sys/time.h>
+#endif	/* !defined(_MSC_VER) */
+
+#ifdef WIN32
+#include <winsock2.h>
+#if !defined(_MSC_VER) || _MSC_VER >= 1600
+#include <stdint.h>
+#else	/* !defined(_MSC_VER) || _MSC_VER >= 1600 */
+typedef unsigned long uint32_t;
+typedef unsigned short uint16_t;
+#endif	/* !defined(_MSC_VER) || _MSC_VER >= 1600 */
+#define in_addr_t uint32_t
+#include "declspec.h"
+#else	/* WIN32 */
+#define LIBSPEC
+#include <netinet/in.h>
+#endif	/* WIN32 */
+
+/* causes problem when installing. Maybe should it be inlined ? */
+/* #include "declspec.h" */
+
+typedef struct {
+	int s;	/* socket */
+	in_addr_t gateway;	/* default gateway (IPv4) */
+	int has_pending_request;
+	unsigned char pending_request[12];
+	int pending_request_len;
+	int try_number;
+	struct timeval retry_time;
+} natpmp_t;
+
+typedef struct {
+	uint16_t type;	/* NATPMP_RESPTYPE_* */
+	uint16_t resultcode;	/* NAT-PMP response code */
+	uint32_t epoch;	/* Seconds since start of epoch */
+	union {
+		struct {
+			//in_addr_t addr;
+			struct in_addr addr;
+		} publicaddress;
+		struct {
+			uint16_t privateport;
+			uint16_t mappedpublicport;
+			uint32_t lifetime;
+		} newportmapping;
+	} pnu;
+} natpmpresp_t;
+
+/* possible values for type field of natpmpresp_t */
+#define NATPMP_RESPTYPE_PUBLICADDRESS (0)
+#define NATPMP_RESPTYPE_UDPPORTMAPPING (1)
+#define NATPMP_RESPTYPE_TCPPORTMAPPING (2)
+
+/* Values to pass to sendnewportmappingrequest() */
+#define NATPMP_PROTOCOL_UDP (1)
+#define NATPMP_PROTOCOL_TCP (2)
+
+/* return values */
+/* NATPMP_ERR_INVALIDARGS : invalid arguments passed to the function */
+#define NATPMP_ERR_INVALIDARGS (-1)
+/* NATPMP_ERR_SOCKETERROR : socket() failed. check errno for details */
+#define NATPMP_ERR_SOCKETERROR (-2)
+/* NATPMP_ERR_CANNOTGETGATEWAY : can't get default gateway IP */
+#define NATPMP_ERR_CANNOTGETGATEWAY (-3)
+/* NATPMP_ERR_CLOSEERR : close() failed. check errno for details */
+#define NATPMP_ERR_CLOSEERR (-4)
+/* NATPMP_ERR_RECVFROM : recvfrom() failed. check errno for details */
+#define NATPMP_ERR_RECVFROM (-5)
+/* NATPMP_ERR_NOPENDINGREQ : readnatpmpresponseorretry() called while
+ * no NAT-PMP request was pending */
+#define NATPMP_ERR_NOPENDINGREQ (-6)
+/* NATPMP_ERR_NOGATEWAYSUPPORT : the gateway does not support NAT-PMP */
+#define NATPMP_ERR_NOGATEWAYSUPPORT (-7)
+/* NATPMP_ERR_CONNECTERR : connect() failed. check errno for details */
+#define NATPMP_ERR_CONNECTERR (-8)
+/* NATPMP_ERR_WRONGPACKETSOURCE : packet not received from the network gateway */
+#define NATPMP_ERR_WRONGPACKETSOURCE (-9)
+/* NATPMP_ERR_SENDERR : send() failed. check errno for details */
+#define NATPMP_ERR_SENDERR (-10)
+/* NATPMP_ERR_FCNTLERROR : fcntl() failed. check errno for details */
+#define NATPMP_ERR_FCNTLERROR (-11)
+/* NATPMP_ERR_GETTIMEOFDAYERR : gettimeofday() failed. check errno for details */
+#define NATPMP_ERR_GETTIMEOFDAYERR (-12)
+
+/* */
+#define NATPMP_ERR_UNSUPPORTEDVERSION (-14)
+#define NATPMP_ERR_UNSUPPORTEDOPCODE (-15)
+
+/* Errors from the server : */
+#define NATPMP_ERR_UNDEFINEDERROR (-49)
+#define NATPMP_ERR_NOTAUTHORIZED (-51)
+#define NATPMP_ERR_NETWORKFAILURE (-52)
+#define NATPMP_ERR_OUTOFRESOURCES (-53)
+
+/* NATPMP_TRYAGAIN : no data available for the moment. try again later */
+#define NATPMP_TRYAGAIN (-100)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* initnatpmp()
+ * initialize a natpmp_t object
+ * With forcegw=1 the gateway is not detected automaticaly.
+ * Return values :
+ * 0 = OK
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_SOCKETERROR
+ * NATPMP_ERR_FCNTLERROR
+ * NATPMP_ERR_CANNOTGETGATEWAY
+ * NATPMP_ERR_CONNECTERR */
+LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw);
+
+/* closenatpmp()
+ * close resources associated with a natpmp_t object
+ * Return values :
+ * 0 = OK
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_CLOSEERR */
+LIBSPEC int closenatpmp(natpmp_t * p);
+
+/* sendpublicaddressrequest()
+ * send a public address NAT-PMP request to the network gateway
+ * Return values :
+ * 2 = OK (size of the request)
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_SENDERR */
+LIBSPEC int sendpublicaddressrequest(natpmp_t * p);
+
+/* sendnewportmappingrequest()
+ * send a new port mapping NAT-PMP request to the network gateway
+ * Arguments :
+ * protocol is either NATPMP_PROTOCOL_TCP or NATPMP_PROTOCOL_UDP,
+ * lifetime is in seconds.
+ * To remove a port mapping, set lifetime to zero.
+ * To remove all port mappings to the host, set lifetime and both ports
+ * to zero.
+ * Return values :
+ * 12 = OK (size of the request)
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_SENDERR */
+LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol,
+                              uint16_t privateport, uint16_t publicport,
+							  uint32_t lifetime);
+
+/* getnatpmprequesttimeout()
+ * fills the timeval structure with the timeout duration of the
+ * currently pending NAT-PMP request.
+ * Return values :
+ * 0 = OK
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_GETTIMEOFDAYERR
+ * NATPMP_ERR_NOPENDINGREQ */
+LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout);
+
+/* readnatpmpresponseorretry()
+ * fills the natpmpresp_t structure if possible
+ * Return values :
+ * 0 = OK
+ * NATPMP_TRYAGAIN
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_NOPENDINGREQ
+ * NATPMP_ERR_NOGATEWAYSUPPORT
+ * NATPMP_ERR_RECVFROM
+ * NATPMP_ERR_WRONGPACKETSOURCE
+ * NATPMP_ERR_UNSUPPORTEDVERSION
+ * NATPMP_ERR_UNSUPPORTEDOPCODE
+ * NATPMP_ERR_NOTAUTHORIZED
+ * NATPMP_ERR_NETWORKFAILURE
+ * NATPMP_ERR_OUTOFRESOURCES
+ * NATPMP_ERR_UNSUPPORTEDOPCODE
+ * NATPMP_ERR_UNDEFINEDERROR */
+LIBSPEC int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response);
+
+#ifdef ENABLE_STRNATPMPERR
+LIBSPEC const char * strnatpmperr(int t);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 19 - 0
ext/libnatpmp/natpmpc.1

@@ -0,0 +1,19 @@
+.TH natpmpc 1
+
+.SH NAME
+natpmpc \- NAT\-PMP library test client and mapping setter.
+
+.SH "SYNOPSIS"
+Display the public IP address:
+.br
+\fBnatpmpc\fP
+
+Add a port mapping:
+.br
+\fBnatpmpc\fP \-a <public port> <private port> <protocol> [lifetime]
+
+.SH DESCRIPTION
+
+In order to remove a mapping, set it with a lifetime of 0 seconds.
+To remove all mappings for your machine, use 0 as private port and        
+lifetime.

+ 244 - 0
ext/libnatpmp/natpmpc.c

@@ -0,0 +1,244 @@
+/* $Id: natpmpc.c,v 1.13 2012/08/21 17:23:38 nanard Exp $ */
+/* libnatpmp
+Copyright (c) 2007-2011, 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 <stdio.h>
+#include <errno.h>
+#include <string.h>
+#if defined(_MSC_VER)
+#if _MSC_VER >= 1400
+#define strcasecmp _stricmp
+#else
+#define strcasecmp stricmp
+#endif
+#else
+#include <unistd.h>
+#endif
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include "natpmp.h"
+
+void usage(FILE * out, const char * argv0)
+{
+	fprintf(out, "Usage :\n");
+	fprintf(out, "  %s [options]\n", argv0);
+	fprintf(out, "\tdisplay the public IP address.\n");
+	fprintf(out, "  %s -h\n", argv0);
+	fprintf(out, "\tdisplay this help screen.\n");
+	fprintf(out, "  %s [options] -a <public port> <private port> <protocol> [lifetime]\n", argv0);
+	fprintf(out, "\tadd a port mapping.\n");
+	fprintf(out, "\nOption available :\n");
+	fprintf(out, "  -g ipv4address\n");
+	fprintf(out, "\tforce the gateway to be used as destination for NAT-PMP commands.\n");
+	fprintf(out, "\n  In order to remove a mapping, set it with a lifetime of 0 seconds.\n");
+	fprintf(out, "  To remove all mappings for your machine, use 0 as private port and lifetime.\n");
+}
+
+/* sample code for using libnatpmp */
+int main(int argc, char * * argv)
+{
+	natpmp_t natpmp;
+	natpmpresp_t response;
+	int r;
+	int sav_errno;
+	struct timeval timeout;
+	fd_set fds;
+	int i;
+	int protocol = 0;
+	uint16_t privateport = 0;
+	uint16_t publicport = 0;
+	uint32_t lifetime = 3600;
+	int command = 0;
+	int forcegw = 0;
+	in_addr_t gateway = 0;
+	struct in_addr gateway_in_use;
+
+#ifdef WIN32
+	WSADATA wsaData;
+	int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+	if(nResult != NO_ERROR)
+	{
+		fprintf(stderr, "WSAStartup() failed.\n");
+		return -1;
+	}
+#endif
+
+	/* argument parsing */
+	for(i=1; i<argc; i++) {
+		if(argv[i][0] == '-') {
+			switch(argv[i][1]) {
+			case 'h':
+				usage(stdout, argv[0]);
+				return 0;
+			case 'g':
+				forcegw = 1;
+				if(argc < i + 1) {
+					fprintf(stderr, "Not enough arguments for option -%c\n", argv[i][1]);
+					return 1;
+				}
+				gateway = inet_addr(argv[++i]);
+				break;
+			case 'a':
+				command = 'a';
+				if(argc < i + 4) {
+					fprintf(stderr, "Not enough arguments for option -%c\n", argv[i][1]);
+					return 1;
+				}
+				i++;
+				if(1 != sscanf(argv[i], "%hu", &publicport)) {
+					fprintf(stderr, "%s is not a correct 16bits unsigned integer\n", argv[i]);
+					return 1;
+				}
+				i++;
+				if(1 != sscanf(argv[i], "%hu", &privateport)) {
+					fprintf(stderr, "%s is not a correct 16bits unsigned integer\n", argv[i]);
+					return 1;
+				}
+				i++;
+				if(0 == strcasecmp(argv[i], "tcp"))
+					protocol = NATPMP_PROTOCOL_TCP;
+				else if(0 == strcasecmp(argv[i], "udp"))
+					protocol = NATPMP_PROTOCOL_UDP;
+				else {
+					fprintf(stderr, "%s is not a valid protocol\n", argv[i]);
+					return 1;
+				}
+				if(argc > i + 1) {
+					if(1 != sscanf(argv[i+1], "%u", &lifetime)) {
+						fprintf(stderr, "%s is not a correct 32bits unsigned integer\n", argv[i]);
+					} else {
+						i++;
+					}
+				}
+				break;
+			default:
+				fprintf(stderr, "Unknown option %s\n", argv[i]);
+				usage(stderr, argv[0]);
+				return 1;
+			}
+		} else {
+			fprintf(stderr, "Unknown option %s\n", argv[i]);
+			usage(stderr, argv[0]);
+			return 1;
+		}
+	}
+
+	/* initnatpmp() */
+	r = initnatpmp(&natpmp, forcegw, gateway);
+	printf("initnatpmp() returned %d (%s)\n", r, r?"FAILED":"SUCCESS");
+	if(r<0)
+		return 1;
+
+	gateway_in_use.s_addr = natpmp.gateway;
+	printf("using gateway : %s\n", inet_ntoa(gateway_in_use));
+
+	/* sendpublicaddressrequest() */
+	r = sendpublicaddressrequest(&natpmp);
+	printf("sendpublicaddressrequest returned %d (%s)\n",
+	       r, r==2?"SUCCESS":"FAILED");
+	if(r<0)
+		return 1;
+
+	do {
+		FD_ZERO(&fds);
+		FD_SET(natpmp.s, &fds);
+		getnatpmprequesttimeout(&natpmp, &timeout);
+		r = select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+		if(r<0) {
+			fprintf(stderr, "select()");
+			return 1;
+		}
+		r = readnatpmpresponseorretry(&natpmp, &response);
+		sav_errno = errno;
+		printf("readnatpmpresponseorretry returned %d (%s)\n",
+		       r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED"));
+		if(r<0 && r!=NATPMP_TRYAGAIN) {
+#ifdef ENABLE_STRNATPMPERR
+			fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n",
+			        strnatpmperr(r));
+#endif
+			fprintf(stderr, "  errno=%d '%s'\n",
+			        sav_errno, strerror(sav_errno));
+		}
+	} while(r==NATPMP_TRYAGAIN);
+	if(r<0)
+		return 1;
+
+	/* TODO : check that response.type == 0 */
+	printf("Public IP address : %s\n", inet_ntoa(response.pnu.publicaddress.addr));
+	printf("epoch = %u\n", response.epoch);
+
+	if(command == 'a') {
+		/* sendnewportmappingrequest() */
+		r = sendnewportmappingrequest(&natpmp, protocol,
+        	                      privateport, publicport,
+								  lifetime);
+		printf("sendnewportmappingrequest returned %d (%s)\n",
+		       r, r==12?"SUCCESS":"FAILED");
+		if(r < 0)
+			return 1;
+
+		do {
+			FD_ZERO(&fds);
+			FD_SET(natpmp.s, &fds);
+			getnatpmprequesttimeout(&natpmp, &timeout);
+			select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+			r = readnatpmpresponseorretry(&natpmp, &response);
+			printf("readnatpmpresponseorretry returned %d (%s)\n",
+			       r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED"));
+		} while(r==NATPMP_TRYAGAIN);
+		if(r<0) {
+#ifdef ENABLE_STRNATPMPERR
+			fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n",
+			        strnatpmperr(r));
+#endif
+			return 1;
+		}
+
+		printf("Mapped public port %hu protocol %s to local port %hu "
+		       "liftime %u\n",
+	    	   response.pnu.newportmapping.mappedpublicport,
+			   response.type == NATPMP_RESPTYPE_UDPPORTMAPPING ? "UDP" :
+			    (response.type == NATPMP_RESPTYPE_TCPPORTMAPPING ? "TCP" :
+			     "UNKNOWN"),
+			   response.pnu.newportmapping.privateport,
+			   response.pnu.newportmapping.lifetime);
+		printf("epoch = %u\n", response.epoch);
+	}
+
+	r = closenatpmp(&natpmp);
+	printf("closenatpmp() returned %d (%s)\n", r, r==0?"SUCCESS":"FAILED");
+	if(r<0)
+		return 1;
+
+	return 0;
+}
+

+ 18 - 0
ext/libnatpmp/setup.py

@@ -0,0 +1,18 @@
+#! /usr/bin/python
+# $Id: setup.py,v 1.3 2012/03/05 04:54:01 nanard Exp $
+#
+# python script to build the libnatpmp module under unix
+#
+# replace libnatpmp.a by libnatpmp.so for shared library usage
+from distutils.core import setup, Extension
+from distutils import sysconfig
+sysconfig.get_config_vars()["OPT"] = ''
+sysconfig.get_config_vars()["CFLAGS"] = ''
+setup(name="libnatpmp", version="1.0",
+      ext_modules=[
+        Extension(name="libnatpmp", sources=["libnatpmpmodule.c"],
+                  extra_objects=["libnatpmp.a"],
+                  define_macros=[('ENABLE_STRNATPMPERR', None)]
+        )]
+     )
+

+ 17 - 0
ext/libnatpmp/setupmingw32.py

@@ -0,0 +1,17 @@
+#! /usr/bin/python
+# $Id: setupmingw32.py,v 1.3 2012/03/05 04:54:01 nanard Exp $
+# python script to build the miniupnpc module under windows
+#
+from distutils.core import setup, Extension
+from distutils import sysconfig
+sysconfig.get_config_vars()["OPT"] = ''
+sysconfig.get_config_vars()["CFLAGS"] = ''
+setup(name="libnatpmp", version="1.0",
+      ext_modules=[
+        Extension(name="libnatpmp", sources=["libnatpmpmodule.c"],
+                  libraries=["ws2_32"],
+                  extra_objects=["libnatpmp.a"],
+                  define_macros=[('ENABLE_STRNATPMPERR', None)]
+        )]
+     )
+

+ 57 - 0
ext/libnatpmp/testgetgateway.c

@@ -0,0 +1,57 @@
+/* $Id: testgetgateway.c,v 1.7 2012/08/21 17:13:31 nanard Exp $ */
+/* libnatpmp
+Copyright (c) 2007-2011, 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 <stdio.h>
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include "getgateway.h"
+
+int main(int argc, char * * argv)
+{
+	(void)argc;
+	(void)argv;
+	struct in_addr gatewayaddr;
+	int r;
+#ifdef WIN32
+	uint32_t temp = 0;
+	r = getdefaultgateway(&temp);
+	gatewayaddr.S_un.S_addr = temp;
+#else
+	r = getdefaultgateway(&(gatewayaddr.s_addr));
+#endif
+	if(r>=0)
+		printf("default gateway : %s\n", inet_ntoa(gatewayaddr));
+	else
+		fprintf(stderr, "getdefaultgateway() failed\n");
+	return 0;
+}
+

+ 60 - 0
ext/libnatpmp/wingettimeofday.c

@@ -0,0 +1,60 @@
+/* $Id: wingettimeofday.c,v 1.6 2013/09/10 20:13:26 nanard Exp $ */
+/* libnatpmp
+Copyright (c) 2007-2013, 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.
+*/
+#ifdef WIN32
+#if defined(_MSC_VER)
+struct timeval {
+	long    tv_sec;
+	long    tv_usec;
+};
+#else
+#include <sys/time.h>
+#endif
+
+typedef struct _FILETIME {
+    unsigned long dwLowDateTime;
+    unsigned long dwHighDateTime;
+} FILETIME;
+
+void __stdcall GetSystemTimeAsFileTime(FILETIME*);
+
+int natpmp_gettimeofday(struct timeval* p, void* tz /* IGNORED */) {
+  union {
+   long long ns100; /*time since 1 Jan 1601 in 100ns units */
+   FILETIME ft;
+  } _now;
+
+	if(!p)
+		return -1;
+  GetSystemTimeAsFileTime( &(_now.ft) );
+  p->tv_usec =(long)((_now.ns100 / 10LL) % 1000000LL );
+  p->tv_sec = (long)((_now.ns100-(116444736000000000LL))/10000000LL);
+  return 0;
+}
+#endif
+

+ 39 - 0
ext/libnatpmp/wingettimeofday.h

@@ -0,0 +1,39 @@
+/* $Id: wingettimeofday.h,v 1.5 2013/09/11 07:22:25 nanard Exp $ */
+/* libnatpmp
+Copyright (c) 2007-2013, 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.
+*/
+#ifndef __WINGETTIMEOFDAY_H__
+#define __WINGETTIMEOFDAY_H__
+#ifdef WIN32
+#if defined(_MSC_VER)
+#include <time.h>
+#else
+#include <sys/time.h>
+#endif
+int natpmp_gettimeofday(struct timeval* p, void* tz /* IGNORED */);
+#endif
+#endif

+ 0 - 4
ext/miniupnpc/minisoap.c

@@ -24,8 +24,6 @@
 #define OS_STRING "Win32"
 #define MINIUPNPC_VERSION_STRING "1.9"
 #define UPNP_VERSION_STRING "UPnP/1.1"
-#else
-#include "miniupnpcstrings.h"
 #endif
 
 /* only for malloc */
@@ -126,5 +124,3 @@ int soapPostSubmit(int fd,
 #endif
 	return httpWrite(fd, body, bodysize, headerbuf, headerssize);
 }
-
-

+ 0 - 3
ext/miniupnpc/miniwget.c

@@ -54,8 +54,6 @@
 #define OS_STRING "Win32"
 #define MINIUPNPC_VERSION_STRING "1.9"
 #define UPNP_VERSION_STRING "UPnP/1.1"
-#else
-#include "miniupnpcstrings.h"
 #endif
 #include "miniwget.h"
 #include "connecthostport.h"
@@ -630,4 +628,3 @@ miniwget_getaddr(const char * url, int * size,
 #endif
 	return miniwget2(hostname, port, path, size, addr, addrlen, scope_id);
 }
-

+ 3 - 8
make-mac.mk

@@ -42,9 +42,8 @@ ifeq ($(ZT_AUTO_UPDATE),1)
 endif
 
 ifeq ($(ZT_USE_MINIUPNPC),1)
-	DEFS+=-DZT_USE_MINIUPNPC
-	LIBS+=ext/miniupnpc/libminiupnpc.a
-	OBJS+=osdep/UPNPClient.o
+	DEFS+=-DMACOSX -DZT_USE_MINIUPNPC -D_DARWIN_C_SOURCE -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -DOS_STRING=\"Darwin/15.0.0\" -DMINIUPNPC_VERSION_STRING=\"1.9\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
+	OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o osdep/PortMapper.o
 endif
 
 # Build with ZT_ENABLE_NETWORK_CONTROLLER=1 to build with the Sqlite network controller
@@ -73,9 +72,6 @@ CXXFLAGS=$(CFLAGS) -fno-rtti
 all: one
 
 one:	$(OBJS) one.o
-ifeq ($(ZT_USE_MINIUPNPC),1)
-	cd ext/miniupnpc ; make clean ; make 'CFLAGS=-D_DARWIN_C_SOURCE -O2 -fstack-protector -fPIE -flto -pthread -mmacosx-version-min=10.7 -fno-common -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE' -j 2 libminiupnpc.a
-endif
 	$(CXX) $(CXXFLAGS) -o zerotier-one $(OBJS) one.o $(LIBS)
 	$(STRIP) zerotier-one
 	ln -sf zerotier-one zerotier-idtool
@@ -101,8 +97,7 @@ official: FORCE
 	make ZT_OFFICIAL_RELEASE=1 mac-dist-pkg
 
 clean:
-	rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-* mkworld
-	cd ext/miniupnpc ; make clean
+	rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o $(OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-* mkworld
 
 # For those building from source -- installs signed binary tap driver in system ZT home
 install-mac-tap: FORCE

+ 314 - 0
osdep/PortMapper.cpp

@@ -0,0 +1,314 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015  ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifdef ZT_USE_MINIUPNPC
+
+// Uncomment to dump debug messages
+#define ZT_PORTMAPPER_TRACE 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include "../node/Utils.hpp"
+#include "OSUtils.hpp"
+#include "PortMapper.hpp"
+
+#ifdef __WINDOWS__
+#ifndef MINIUPNP_STATICLIB
+#define MINIUPNP_STATICLIB
+#endif
+#endif
+
+#include "../ext/miniupnpc/miniupnpc.h"
+#include "../ext/miniupnpc/upnpcommands.h"
+#include "../ext/libnatpmp/natpmp.h"
+
+namespace ZeroTier {
+
+class PortMapperImpl
+{
+public:
+	PortMapperImpl(int localUdpPortToMap,const char *un) :
+		run(true),
+		localPort(localUdpPortToMap),
+		uniqueName(un)
+	{
+	}
+
+	~PortMapperImpl() {}
+
+	void threadMain()
+		throw()
+	{
+		int mode = 0; // 0 == NAT-PMP, 1 == UPnP
+
+#ifdef ZT_PORTMAPPER_TRACE
+		fprintf(stderr,"PortMapper: started for UDP port %d"ZT_EOL_S,localPort);
+#endif
+
+		while (run) {
+
+			// ---------------------------------------------------------------------
+			// NAT-PMP mode (preferred)
+			// ---------------------------------------------------------------------
+			if (mode == 0) {
+			  natpmp_t natpmp;
+			  natpmpresp_t response;
+				int r = 0;
+
+				bool natPmpSuccess = false;
+				for(int tries=0;tries<60;++tries) {
+					int tryPort = (int)localPort + tries;
+					if (tryPort >= 65535)
+						tryPort = (tryPort - 65535) + 1025;
+
+					memset(&natpmp,0,sizeof(natpmp));
+					memset(&response,0,sizeof(response));
+
+					if (initnatpmp(&natpmp,0,0) != 0) {
+						mode = 1;
+#ifdef ZT_PORTMAPPER_TRACE
+						fprintf(stderr,"PortMapper: NAT-PMP: init failed, switching to UPnP mode"ZT_EOL_S);
+#endif
+						break;
+					}
+
+					InetAddress publicAddress;
+					sendpublicaddressrequest(&natpmp);
+					uint64_t myTimeout = OSUtils::now() + 30000;
+					do {
+						fd_set fds;
+						struct timeval timeout;
+						FD_ZERO(&fds);
+						FD_SET(natpmp.s, &fds);
+						getnatpmprequesttimeout(&natpmp, &timeout);
+						select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+						r = readnatpmpresponseorretry(&natpmp, &response);
+						if (OSUtils::now() >= myTimeout)
+							break;
+					} while (r == NATPMP_TRYAGAIN);
+					if (r == 0) {
+						publicAddress = InetAddress((uint32_t)response.pnu.publicaddress.addr.s_addr,0);
+					} else {
+#ifdef ZT_PORTMAPPER_TRACE
+						fprintf(stderr,"PortMapper: NAT-PMP: request for external address failed, aborting..."ZT_EOL_S);
+#endif
+						break;
+					}
+
+				  sendnewportmappingrequest(&natpmp,NATPMP_PROTOCOL_UDP,localPort,tryPort,(ZT_PORTMAPPER_REFRESH_DELAY * 2) / 1000);
+					do {
+				    fd_set fds;
+				    struct timeval timeout;
+				    FD_ZERO(&fds);
+				    FD_SET(natpmp.s, &fds);
+				    getnatpmprequesttimeout(&natpmp, &timeout);
+				    select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+				    r = readnatpmpresponseorretry(&natpmp, &response);
+				  } while (r == NATPMP_TRYAGAIN);
+					if (r == 0) {
+						publicAddress.setPort(response.pnu.newportmapping.mappedpublicport);
+#ifdef ZT_PORTMAPPER_TRACE
+						fprintf(stderr,"PortMapper: NAT-PMP: mapped %u to %s"ZT_EOL_S,(unsigned int)localPort,publicAddress.toString().c_str());
+#endif
+						Mutex::Lock sl(surface_l);
+						surface.clear();
+						surface.push_back(publicAddress);
+						natPmpSuccess = true;
+						break;
+					}
+
+					closenatpmp(&natpmp);
+				}
+
+				if (!natPmpSuccess) {
+					mode = 1;
+#ifdef ZT_PORTMAPPER_TRACE
+					fprintf(stderr,"PortMapper: NAT-PMP: request failed, switching to UPnP mode"ZT_EOL_S);
+#endif
+				}
+			}
+			// ---------------------------------------------------------------------
+
+			// ---------------------------------------------------------------------
+			// UPnP mode
+			// ---------------------------------------------------------------------
+			if (mode == 1) {
+				char lanaddr[4096];
+				char externalip[4096]; // no range checking? so make these buffers larger than any UDP packet a uPnP server could send us as a precaution :P
+				char inport[16];
+				char outport[16];
+				struct UPNPUrls urls;
+				struct IGDdatas data;
+
+				int upnpError = 0;
+				UPNPDev *devlist = upnpDiscover(5000,(const char *)0,(const char *)0,0,0,0,&upnpError);
+				if (devlist) {
+
+#ifdef ZT_PORTMAPPER_TRACE
+					{
+						UPNPDev *dev = devlist;
+						while (dev) {
+							fprintf(stderr,"PortMapper: found UPnP device at URL '%s': %s"ZT_EOL_S,dev->descURL,dev->st);
+							dev = dev->pNext;
+						}
+					}
+#endif
+
+					memset(lanaddr,0,sizeof(lanaddr));
+					memset(externalip,0,sizeof(externalip));
+					memset(&urls,0,sizeof(urls));
+					memset(&data,0,sizeof(data));
+					Utils::snprintf(inport,sizeof(inport),"%d",localPort);
+
+					if ((UPNP_GetValidIGD(devlist,&urls,&data,lanaddr,sizeof(lanaddr)))&&(lanaddr[0])) {
+#ifdef ZT_PORTMAPPER_TRACE
+						fprintf(stderr,"PortMapper: UPnP: my LAN IP address: %s"ZT_EOL_S,lanaddr);
+#endif
+						if ((UPNP_GetExternalIPAddress(urls.controlURL,data.first.servicetype,externalip) == UPNPCOMMAND_SUCCESS)&&(externalip[0])) {
+#ifdef ZT_PORTMAPPER_TRACE
+							fprintf(stderr,"PortMapper: UPnP: my external IP address: %s"ZT_EOL_S,externalip);
+#endif
+
+							for(int tries=0;tries<60;++tries) {
+								int tryPort = (int)localPort + tries;
+								if (tryPort >= 65535)
+									tryPort = (tryPort - 65535) + 1025;
+								Utils::snprintf(outport,sizeof(outport),"%u",tryPort);
+
+								// First check and see if this port is already mapped to the
+								// same unique name. If so, keep this mapping and don't try
+								// to map again since this can break buggy routers. But don't
+								// fail if this command fails since not all routers support it.
+								{
+									char haveIntClient[128]; // 128 == big enough for all these as per miniupnpc "documentation"
+									char haveIntPort[128];
+									char haveDesc[128];
+									char haveEnabled[128];
+									char haveLeaseDuration[128];
+									memset(haveIntClient,0,sizeof(haveIntClient));
+									memset(haveIntPort,0,sizeof(haveIntPort));
+									memset(haveDesc,0,sizeof(haveDesc));
+									memset(haveEnabled,0,sizeof(haveEnabled));
+									memset(haveLeaseDuration,0,sizeof(haveLeaseDuration));
+									if ((UPNP_GetSpecificPortMappingEntry(urls.controlURL,data.first.servicetype,outport,"UDP",(const char *)0,haveIntClient,haveIntPort,haveDesc,haveEnabled,haveLeaseDuration) == UPNPCOMMAND_SUCCESS)&&(uniqueName == haveDesc)) {
+#ifdef ZT_PORTMAPPER_TRACE
+										fprintf(stderr,"PortMapper: UPnP: reusing previously reserved external port: %s"ZT_EOL_S,outport);
+#endif
+										Mutex::Lock sl(surface_l);
+										surface.clear();
+										InetAddress tmp(externalip);
+										tmp.setPort(tryPort);
+										surface.push_back(tmp);
+										break;
+									}
+								}
+
+								// Try to map this port
+								int mapResult = 0;
+								if ((mapResult = UPNP_AddPortMapping(urls.controlURL,data.first.servicetype,outport,inport,lanaddr,uniqueName.c_str(),"UDP",(const char *)0,"0")) == UPNPCOMMAND_SUCCESS) {
+#ifdef ZT_PORTMAPPER_TRACE
+									fprintf(stderr,"PortMapper: UPnP: reserved external port: %s"ZT_EOL_S,outport);
+#endif
+									Mutex::Lock sl(surface_l);
+									surface.clear();
+									InetAddress tmp(externalip);
+									tmp.setPort(tryPort);
+									surface.push_back(tmp);
+									break;
+								} else {
+#ifdef ZT_PORTMAPPER_TRACE
+									fprintf(stderr,"PortMapper: UPnP: UPNP_AddPortMapping(%s) failed: %d"ZT_EOL_S,outport,mapResult);
+#endif
+									Thread::sleep(1000);
+								}
+							}
+
+						} else {
+							mode = 0;
+#ifdef ZT_PORTMAPPER_TRACE
+							fprintf(stderr,"PortMapper: UPnP: UPNP_GetExternalIPAddress failed, returning to NAT-PMP mode"ZT_EOL_S);
+#endif
+						}
+					} else {
+						mode = 0;
+#ifdef ZT_PORTMAPPER_TRACE
+						fprintf(stderr,"PortMapper: UPnP: UPNP_GetValidIGD failed, returning to NAT-PMP mode"ZT_EOL_S);
+#endif
+					}
+
+					freeUPNPDevlist(devlist);
+
+				} else {
+					mode = 0;
+#ifdef ZT_PORTMAPPER_TRACE
+					fprintf(stderr,"PortMapper: upnpDiscover failed, returning to NAT-PMP mode: %d"ZT_EOL_S,upnpError);
+#endif
+				}
+			}
+			// ---------------------------------------------------------------------
+
+#ifdef ZT_PORTMAPPER_TRACE
+			fprintf(stderr,"UPNPClient: rescanning in %d ms"ZT_EOL_S,ZT_PORTMAPPER_REFRESH_DELAY);
+#endif
+			Thread::sleep(ZT_PORTMAPPER_REFRESH_DELAY);
+		}
+
+		delete this;
+	}
+
+	volatile bool run;
+	int localPort;
+	std::string uniqueName;
+
+	Mutex surface_l;
+	std::vector<InetAddress> surface;
+};
+
+PortMapper::PortMapper(int localUdpPortToMap,const char *uniqueName)
+{
+	_impl = new PortMapperImpl(localUdpPortToMap,uniqueName);
+	Thread::start(_impl);
+}
+
+PortMapper::~PortMapper()
+{
+	_impl->run = false;
+}
+
+std::vector<InetAddress> PortMapper::get() const
+{
+	Mutex::Lock _l(_impl->surface_l);
+	return _impl->surface;
+}
+
+} // namespace ZeroTier
+
+#endif // ZT_USE_MINIUPNPC

+ 10 - 9
osdep/UPNPClient.hpp → osdep/PortMapper.hpp

@@ -40,28 +40,29 @@
 /**
  * How frequently should we refresh our UPNP/NAT-PnP/whatever state?
  */
-#define ZT_UPNP_CLIENT_REFRESH_DELAY 600000
+#define ZT_PORTMAPPER_REFRESH_DELAY 300000
 
 namespace ZeroTier {
 
-class UPNPClientImpl;
+class PortMapperImpl;
 
 /**
- * UPnP/NAT-PnP daemon thread
+ * UPnP/NAT-PnP port mapping "daemon"
  */
-class UPNPClient
+class PortMapper
 {
-	friend class UPNPClientImpl;
+	friend class PortMapperImpl;
 
 public:
 	/**
-	 * Create and start UPNP client service
+	 * Create and start port mapper service
 	 *
 	 * @param localUdpPortToMap Port we want visible to the outside world
+	 * @param name Unique name of this endpoint (based on ZeroTier address)
 	 */
-	UPNPClient(int localUdpPortToMap);
+	PortMapper(int localUdpPortToMap,const char *uniqueName);
 
-	~UPNPClient();
+	~PortMapper();
 
 	/**
 	 * @return All current external mappings for our port
@@ -69,7 +70,7 @@ public:
 	std::vector<InetAddress> get() const;
 
 private:
-	UPNPClientImpl *_impl;
+	PortMapperImpl *_impl;
 };
 
 } // namespace ZeroTier

+ 0 - 198
osdep/UPNPClient.cpp

@@ -1,198 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015  ZeroTier, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#ifdef ZT_USE_MINIUPNPC
-
-// Uncomment to dump debug messages
-//#define ZT_UPNP_TRACE 1
-
-// Uncomment to build a main() for ad-hoc testing
-//#define ZT_UPNP_TEST 1
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "../node/Utils.hpp"
-#include "UPNPClient.hpp"
-
-#ifdef __WINDOWS__
-#ifndef MINIUPNP_STATICLIB
-#define MINIUPNP_STATICLIB
-#endif
-#endif
-
-#include "../ext/miniupnpc/miniupnpc.h"
-#include "../ext/miniupnpc/upnpcommands.h"
-
-namespace ZeroTier {
-
-class UPNPClientImpl
-{
-public:
-	UPNPClientImpl(int localUdpPortToMap) :
-		run(true),
-		localPort(localUdpPortToMap)
-	{
-	}
-
-	void threadMain()
-		throw()
-	{
-		char lanaddr[4096];
-		char externalip[4096]; // no range checking? so make these buffers larger than any UDP packet a uPnP server could send us as a precaution :P
-		char inport[16];
-		char outport[16];
-		struct UPNPUrls urls;
-		struct IGDdatas data;
-
-#ifdef ZT_UPNP_TRACE
-		fprintf(stderr,"UPNPClient: started for UDP port %d"ZT_EOL_S,localPort);
-#endif
-
-		unsigned int tryPortStart = 0;
-		Utils::getSecureRandom(&tryPortStart,sizeof(tryPortStart));
-		tryPortStart = (tryPortStart % (65535 - 1025)) + 1025;
-
-		while (run) {
-			{
-				int upnpError = 0;
-				UPNPDev *devlist = upnpDiscover(2000,(const char *)0,(const char *)0,0,0,0,&upnpError);
-				if (devlist) {
-#ifdef ZT_UPNP_TRACE
-					{
-						UPNPDev *dev = devlist;
-						while (dev) {
-							fprintf(stderr,"UPNPClient: found device at URL '%s': %s"ZT_EOL_S,dev->descURL,dev->st);
-							dev = dev->pNext;
-						}
-					}
-#endif
-
-					memset(lanaddr,0,sizeof(lanaddr));
-					memset(externalip,0,sizeof(externalip));
-					memset(&urls,0,sizeof(urls));
-					memset(&data,0,sizeof(data));
-					Utils::snprintf(inport,sizeof(inport),"%d",localPort);
-
-					if ((UPNP_GetValidIGD(devlist,&urls,&data,lanaddr,sizeof(lanaddr)))&&(lanaddr[0])) {
-#ifdef ZT_UPNP_TRACE
-						fprintf(stderr,"UPNPClient: my LAN IP address: %s"ZT_EOL_S,lanaddr);
-#endif
-						if ((UPNP_GetExternalIPAddress(urls.controlURL,data.first.servicetype,externalip) == UPNPCOMMAND_SUCCESS)&&(externalip[0])) {
-#ifdef ZT_UPNP_TRACE
-							fprintf(stderr,"UPNPClient: my external IP address: %s"ZT_EOL_S,externalip);
-#endif
-
-							for(int tries=0;tries<64;++tries) {
-								int tryPort = (int)tryPortStart + tries;
-								if (tryPort >= 65535)
-									tryPort = (tryPort - 65535) + 1025;
-								Utils::snprintf(outport,sizeof(outport),"%u",tryPort);
-
-								int mapResult = 0;
-								if ((mapResult = UPNP_AddPortMapping(urls.controlURL,data.first.servicetype,outport,inport,lanaddr,"ZeroTier","UDP",(const char *)0,"0")) == UPNPCOMMAND_SUCCESS) {
-	#ifdef ZT_UPNP_TRACE
-									fprintf(stderr,"UPNPClient: reserved external port: %s"ZT_EOL_S,outport);
-	#endif
-									{
-										Mutex::Lock sl(surface_l);
-										surface.clear();
-										InetAddress tmp(externalip);
-										tmp.setPort(tryPort);
-										surface.push_back(tmp);
-									}
-									break;
-								} else {
-	#ifdef ZT_UPNP_TRACE
-									fprintf(stderr,"UPNPClient: UPNP_AddAnyPortMapping(%s) failed: %d"ZT_EOL_S,outport,mapResult);
-	#endif
-									Thread::sleep(1000);
-								}
-							}
-						} else {
-#ifdef ZT_UPNP_TRACE
-							fprintf(stderr,"UPNPClient: UPNP_GetExternalIPAddress failed"ZT_EOL_S);
-#endif
-						}
-					} else {
-#ifdef ZT_UPNP_TRACE
-						fprintf(stderr,"UPNPClient: UPNP_GetValidIGD failed"ZT_EOL_S);
-#endif
-					}
-
-					freeUPNPDevlist(devlist);
-				} else {
-#ifdef ZT_UPNP_TRACE
-					fprintf(stderr,"UPNPClient: upnpDiscover error code: %d"ZT_EOL_S,upnpError);
-#endif
-				}
-			}
-
-#ifdef ZT_UPNP_TRACE
-			fprintf(stderr,"UPNPClient: rescanning in %d ms"ZT_EOL_S,ZT_UPNP_CLIENT_REFRESH_DELAY);
-#endif
-			Thread::sleep(ZT_UPNP_CLIENT_REFRESH_DELAY);
-		}
-		delete this;
-	}
-
-	volatile bool run;
-	int localPort;
-	Mutex surface_l;
-	std::vector<InetAddress> surface;
-};
-
-UPNPClient::UPNPClient(int localUdpPortToMap)
-{
-	_impl = new UPNPClientImpl(localUdpPortToMap);
-	Thread::start(_impl);
-}
-
-UPNPClient::~UPNPClient()
-{
-	_impl->run = false;
-}
-
-std::vector<InetAddress> UPNPClient::get() const
-{
-	Mutex::Lock _l(_impl->surface_l);
-	return _impl->surface;
-}
-
-} // namespace ZeroTier
-
-#ifdef ZT_UPNP_TEST
-int main(int argc,char **argv)
-{
-	ZeroTier::UPNPClient *client = new ZeroTier::UPNPClient(12345);
-	ZeroTier::Thread::sleep(0xffffffff); // wait forever
-	return 0;
-}
-#endif
-
-#endif // ZT_USE_MINIUPNPC

+ 11 - 0
selftest.cpp

@@ -58,6 +58,8 @@
 #include "osdep/Phy.hpp"
 #include "osdep/Http.hpp"
 #include "osdep/BackgroundResolver.hpp"
+#include "osdep/PortMapper.hpp"
+#include "osdep/Thread.hpp"
 
 #ifdef ZT_ENABLE_NETWORK_CONTROLLER
 #include "controller/SqliteNetworkController.hpp"
@@ -1094,6 +1096,7 @@ int main(int argc,char **argv)
 
 	srand((unsigned int)time(0));
 
+	/*
 	r |= testSqliteNetworkController();
 	r |= testOther();
 	r |= testCrypto();
@@ -1103,9 +1106,17 @@ int main(int argc,char **argv)
 	r |= testPhy();
 	r |= testResolver();
 	//r |= testHttp();
+	*/
 
 	if (r)
 		std::cout << std::endl << "SOMETHING FAILED!" << std::endl;
 
+#ifdef ZT_USE_MINIUPNPC
+	std::cout << std::endl;
+	std::cout << "[portmapper] Starting port mapper and waiting forever... use CTRL+C to exit. (enable ZT_PORTMAPPER_TRACE in PortMapper.cpp for output)" << std::endl;
+	PortMapper mapper(12345,"ZeroTier/__selftest");
+	Thread::sleep(0xffffffff);
+#endif
+
 	return r;
 }

+ 25 - 23
service/OneService.cpp

@@ -54,7 +54,7 @@
 #include "../osdep/OSUtils.hpp"
 #include "../osdep/Http.hpp"
 #include "../osdep/BackgroundResolver.hpp"
-#include "../osdep/UPNPClient.hpp"
+#include "../osdep/PortMapper.hpp"
 
 #include "OneService.hpp"
 #include "ControlPlane.hpp"
@@ -452,7 +452,7 @@ public:
 		,_port(0)
 #ifdef ZT_USE_MINIUPNPC
 		,_v4UpnpUdpSocket((PhySocket *)0)
-		,_upnpClient((UPNPClient *)0)
+		,_portMapper((PortMapper *)0)
 #endif
 #ifdef ZT_ENABLE_CLUSTER
 		,_clusterMessageSocket((PhySocket *)0)
@@ -508,22 +508,6 @@ public:
 		char portstr[64];
 		Utils::snprintf(portstr,sizeof(portstr),"%u",_port);
 		OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),std::string(portstr));
-
-#ifdef ZT_USE_MINIUPNPC
-		// Bind a secondary port for use with uPnP, since some NAT routers
-		// (cough Ubiquity Edge cough) barf up a lung if you do both conventional
-		// NAT-t and uPnP from behind the same port. I think this is a bug, but
-		// everyone else's router bugs are our problem. :P
-		for(int k=0;k<512;++k) {
-			const unsigned int upnport = 40000 + (((port + 1) * (k + 1)) % 25500);
-			_v4UpnpLocalAddress = InetAddress(0,upnport);
-			_v4UpnpUdpSocket = _phy.udpBind((const struct sockaddr *)&_v4UpnpLocalAddress,reinterpret_cast<void *>(&_v4UpnpLocalAddress),ZT_UDP_DESIRED_BUF_SIZE);
-			if (_v4UpnpUdpSocket) {
-				_upnpClient = new UPNPClient(upnport);
-				break;
-			}
-		}
-#endif
 	}
 
 	virtual ~OneServiceImpl()
@@ -537,7 +521,7 @@ public:
 #endif
 #ifdef ZT_USE_MINIUPNPC
 		_phy.close(_v4UpnpUdpSocket);
-		delete _upnpClient;
+		delete _portMapper;
 #endif
 #ifdef ZT_ENABLE_NETWORK_CONTROLLER
 		delete _controller;
@@ -580,6 +564,24 @@ public:
 				SnodeVirtualNetworkConfigFunction,
 				SnodeEventCallback);
 
+#ifdef ZT_USE_MINIUPNPC
+			// Bind a secondary port for use with uPnP, since some NAT routers
+			// (cough Ubiquity Edge cough) barf up a lung if you do both conventional
+			// NAT-t and uPnP from behind the same port. I think this is a bug, but
+			// everyone else's router bugs are our problem. :P
+			for(int k=0;k<512;++k) {
+				unsigned int mapperPort = 40000 + (((_port + 1) * (k + 1)) % 25500);
+				char uniqueName[64];
+				_v4UpnpLocalAddress = InetAddress(0,mapperPort);
+				_v4UpnpUdpSocket = _phy.udpBind((const struct sockaddr *)&_v4UpnpLocalAddress,reinterpret_cast<void *>(&_v4UpnpLocalAddress),ZT_UDP_DESIRED_BUF_SIZE);
+				if (_v4UpnpUdpSocket) {
+					Utils::snprintf(uniqueName,sizeof(uniqueName),"ZeroTier/%.16llx",_node->address());
+					_portMapper = new PortMapper(mapperPort,uniqueName);
+					break;
+				}
+			}
+#endif
+
 #ifdef ZT_ENABLE_NETWORK_CONTROLLER
 			_controller = new SqliteNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str(),(_homePath + ZT_PATH_SEPARATOR_S + "circuitTestResults.d").c_str());
 			_node->setNetconfMaster((void *)_controller);
@@ -674,7 +676,7 @@ public:
 			_lastRestart = clockShouldBe;
 			uint64_t lastTapMulticastGroupCheck = 0;
 			uint64_t lastTcpFallbackResolve = 0;
-			uint64_t lastLocalInterfaceAddressCheck = (OSUtils::now() - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give UPnP time to configure and other things time to settle
+			uint64_t lastLocalInterfaceAddressCheck = (OSUtils::now() - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle
 #ifdef ZT_AUTO_UPDATE
 			uint64_t lastSoftwareUpdateCheck = 0;
 #endif // ZT_AUTO_UPDATE
@@ -742,8 +744,8 @@ public:
 					_node->clearLocalInterfaceAddresses();
 
 #ifdef ZT_USE_MINIUPNPC
-					std::vector<InetAddress> upnpAddresses(_upnpClient->get());
-					for(std::vector<InetAddress>::const_iterator ext(upnpAddresses.begin());ext!=upnpAddresses.end();++ext)
+					std::vector<InetAddress> mappedAddresses(_portMapper->get());
+					for(std::vector<InetAddress>::const_iterator ext(mappedAddresses.begin());ext!=mappedAddresses.end();++ext)
 						_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*ext)));
 #endif
 
@@ -1472,7 +1474,7 @@ public:
 #ifdef ZT_USE_MINIUPNPC
 	InetAddress _v4UpnpLocalAddress;
 	PhySocket *_v4UpnpUdpSocket;
-	UPNPClient *_upnpClient;
+	PortMapper *_portMapper;
 #endif
 
 #ifdef ZT_ENABLE_CLUSTER