From cd519708b1214c42733a83420f8aa76147575997 Mon Sep 17 00:00:00 2001
From: cgeek <cem.moreau@gmail.com>
Date: Thu, 27 Jul 2017 16:06:42 +0200
Subject: [PATCH] [fix] #1045 Detect the best UPnP available port

---
 app/modules/bma/index.ts          | 10 +---------
 app/modules/bma/lib/constants.ts  |  4 ++++
 app/modules/bma/lib/network.ts    | 27 ++++++++++++++++++++-------
 test/integration/tools/toolbox.ts |  5 +++--
 4 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/app/modules/bma/index.ts b/app/modules/bma/index.ts
index ae281165e..7b4a958c7 100644
--- a/app/modules/bma/index.ts
+++ b/app/modules/bma/index.ts
@@ -147,15 +147,7 @@ export const BmaDependency = {
     methods: {
       noLimit: () => BMALimitation.noLimit(),
       bma, sanitize, dtos,
-      upnpConf: Network.upnpConf,
-      getRandomPort: Network.getRandomPort,
-      listInterfaces: Network.listInterfaces,
-      getEndpoint: getEndpoint,
-      getMainEndpoint: (conf:NetworkConfDTO) => Promise.resolve(getEndpoint(conf)),
-      getBestLocalIPv6: Network.getBestLocalIPv6,
-      getBestLocalIPv4: Network.getBestLocalIPv4,
-      createServersAndListen: Network.createServersAndListen,
-      http2raw
+      getMainEndpoint: (conf:NetworkConfDTO) => Promise.resolve(getEndpoint(conf))
     }
   }
 }
diff --git a/app/modules/bma/lib/constants.ts b/app/modules/bma/lib/constants.ts
index 5dd915869..18abf2eb7 100644
--- a/app/modules/bma/lib/constants.ts
+++ b/app/modules/bma/lib/constants.ts
@@ -1,4 +1,8 @@
 export const BMAConstants = {
+
+  BMA_PORTS_START: 10901,
+  BMA_PORTS_END: 10999,
+
   ENTITY_BLOCK: 'block',
   ENTITY_IDENTITY: 'identity',
   ENTITY_CERTIFICATION: 'certification',
diff --git a/app/modules/bma/lib/network.ts b/app/modules/bma/lib/network.ts
index ea20a3eda..cdb146801 100644
--- a/app/modules/bma/lib/network.ts
+++ b/app/modules/bma/lib/network.ts
@@ -29,7 +29,7 @@ export const Network = {
 
   listInterfaces: listInterfaces,
 
-  upnpConf: (noupnp:boolean, logger:any) => upnpConf(noupnp, logger),
+  upnpConf,
 
   getRandomPort: getRandomPort,
 
@@ -335,22 +335,22 @@ function listInterfaces() {
 }
 
 async function upnpConf (noupnp:boolean, logger:any) {
+  const client = require('nnupnp').createClient();
+  // Look for 2 random ports
+  const publicPort = await getAvailablePort(client)
+  const privatePort = publicPort
   const conf:NetworkConfDTO = {
-    port: 10901,
+    port: privatePort,
     ipv4: '127.0.0.1',
     ipv6: '::1',
     dos: null,
     upnp: false,
     httplogs: false,
-    remoteport: 10901,
+    remoteport: publicPort,
     remotehost: null,
     remoteipv4: null,
     remoteipv6: null
   }
-  const client = require('nnupnp').createClient();
-  // Look for 2 random ports
-  const privatePort = getRandomPort(conf);
-  const publicPort = privatePort;
   logger && logger.info('Checking UPnP features...');
   if (noupnp) {
     throw Error('No UPnP');
@@ -374,6 +374,19 @@ async function upnpConf (noupnp:boolean, logger:any) {
   return conf;
 }
 
+async function getAvailablePort(client:any) {
+  const mappings:{ public: { port:number }}[] = await Q.nbind(client.getMappings, client)();
+  const externalPortsUsed = mappings.map(m => m.public.port)
+  let availablePort = BMAConstants.BMA_PORTS_START
+  while (externalPortsUsed.indexOf(availablePort) !== -1 && availablePort <= BMAConstants.BMA_PORTS_END) {
+    availablePort++
+  }
+  if (availablePort > BMAConstants.BMA_PORTS_END) {
+    throw "No port available for UPnP"
+  }
+  return availablePort
+}
+
 function getRandomPort(conf:NetworkConfDTO) {
   if (conf && conf.remoteport) {
     return conf.remoteport;
diff --git a/test/integration/tools/toolbox.ts b/test/integration/tools/toolbox.ts
index 2efb96025..1205a3fbd 100644
--- a/test/integration/tools/toolbox.ts
+++ b/test/integration/tools/toolbox.ts
@@ -6,6 +6,7 @@ import * as stream from "stream"
 import {RevocationDTO} from "../../../app/lib/dto/RevocationDTO"
 import {IdentityDTO} from "../../../app/lib/dto/IdentityDTO"
 import {PeerDTO} from "../../../app/lib/dto/PeerDTO"
+import {Network} from "../../../app/modules/bma/lib/network";
 
 const _           = require('underscore');
 const rp          = require('request-promise');
@@ -135,7 +136,7 @@ export const fakeSyncServer = async (readBlocksMethod:any, readParticularBlockMe
     processRequest: () => { /* Does nothing */ }
   };
 
-  const fakeServer = await require('../../../app/modules/bma').BmaDependency.duniter.methods.createServersAndListen("Fake Duniter Server", { conf: {} }, [{
+  const fakeServer = await Network.createServersAndListen("Fake Duniter Server", new Server("", true, {}), [{
     ip: host,
     port: port
   }], NO_HTTP_LOGS, logger, NO_STATIC_PATH, (app:any, httpMethods:any) => {
@@ -170,7 +171,7 @@ export const fakeSyncServer = async (readBlocksMethod:any, readParticularBlockMe
       return readParticularBlockMethod(number);
 
     }, dtos.Block, noLimit);
-  });
+  }, null)
 
   await fakeServer.openConnections();
   return {
-- 
GitLab