annotate src/plugins/plugin_misc_nat-port.py @ 1607:4741e2f5eed2

plugin file: progressFinished and progressError are now sent on SatFile.close. eof Deferred attribute is removed, as it is not used and bring unnecessary complication
author Goffi <goffi@goffi.org>
date Mon, 16 Nov 2015 00:24:49 +0100
parents 7d91dff71067
children d17772b0fe22
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1 #!/usr/bin/python
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
2 # -*- coding: utf-8 -*-
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
3
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
4 # SAT plugin for NAT port mapping
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
5 # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Jérôme Poisson (goffi@goffi.org)
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
6
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
7 # This program is free software: you can redistribute it and/or modify
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
8 # it under the terms of the GNU Affero General Public License as published by
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
9 # the Free Software Foundation, either version 3 of the License, or
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
10 # (at your option) any later version.
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
11
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
12 # This program is distributed in the hope that it will be useful,
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
15 # GNU Affero General Public License for more details.
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
16
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
17 # You should have received a copy of the GNU Affero General Public License
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
19
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
20 from sat.core.i18n import _
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
21 from sat.core.constants import Const as C
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
22 from sat.core.log import getLogger
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
23 log = getLogger(__name__)
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
24 from sat.core import exceptions
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
25 from twisted.internet import threads
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
26 from twisted.internet import defer
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
27 from twisted.python import failure
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
28 import threading
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
29
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
30 try:
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
31 import miniupnpc
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
32 except ImportError:
1562
7d91dff71067 plugin NAT Port: added instruction with pip when miniupnpc is missing
Goffi <goffi@goffi.org>
parents: 1554
diff changeset
33 raise exceptions.MissingModule(u"Missing module MiniUPnPc, please download/install it (and its Python binding) at http://miniupnp.free.fr/ (or use pip install miniupnpc)")
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
34
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
35
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
36 PLUGIN_INFO = {
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
37 "name": "NAT port mapping",
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
38 "import_name": "NAT-PORT",
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
39 "type": C.PLUG_TYPE_MISC,
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
40 "main": "NatPort",
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
41 "handler": "no",
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
42 "description": _("""Automatic NAT port mapping using UPnP"""),
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
43 }
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
44
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
45 STARTING_PORT = 6000 # starting point to automatically find a port
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
46 DEFAULT_DESC = u'SaT port mapping' # we don't use "à" here as some bugged NAT don't manage charset correctly
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
47
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
48
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
49 class MappingError(Exception):
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
50 pass
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
51
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
52
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
53 class NatPort(object):
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
54 # TODO: refresh data if a new connection is detected (see plugin_misc_ip)
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
55
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
56 def __init__(self, host):
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
57 log.info(_("plugin NAT Port initialization"))
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
58 self.host = host
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
59 self._external_ip = None
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
60 self._initialised = defer.Deferred()
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
61 self._upnp = miniupnpc.UPnP() # will be None if no device is available
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
62 self._upnp.discoverdelay=200
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
63 self._mutex = threading.Lock() # used to protect access to self._upnp
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
64 self._starting_port_cache = None # used to cache the first available port
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
65 self._to_unmap = [] # list of tuples (ext_port, protocol) of ports to unmap on unload
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
66 discover_d = threads.deferToThread(self._discover)
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
67 discover_d.chainDeferred(self._initialised)
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
68 self._initialised.addErrback(self._init_failed)
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
69
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
70 def unload(self):
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
71 if self._to_unmap:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
72 log.info(u"Cleaning mapped ports")
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
73 return threads.deferToThread(self._unmapPortsBlocking)
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
74
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
75 def _init_failed(self, failure_):
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
76 e = failure_.trap(exceptions.NotFound, exceptions.FeatureNotFound)
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
77 if e == exceptions.FeatureNotFound:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
78 log.info(u"UPnP-IGD seems to be not activated on the device")
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
79 else:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
80 log.info(u"UPnP-IGD not available")
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
81 self._upnp = None
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
82
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
83 def _discover(self):
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
84 devices = self._upnp.discover()
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
85 if devices:
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
86 log.info(u"{nb} UPnP-IGD device(s) found".format(nb=devices))
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
87 else:
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
88 log.info(u"Can't find UPnP-IGD device on the local network")
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
89 raise failure.Failure(exceptions.NotFound())
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
90 self._upnp.selectigd()
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
91 try:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
92 self._external_ip = self._upnp.externalipaddress()
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
93 except Exception:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
94 raise failure.Failure(exceptions.FeatureNotFound())
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
95
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
96 def getIP(self, local=False):
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
97 """Return IP address found with UPnP-IGD
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
98
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
99 @param local(bool): True to get external IP address, False to get local network one
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
100 @return (None, str): found IP address, or None of something got wrong
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
101 """
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
102 def getIP(dummy):
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
103 if self._upnp is None:
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
104 return None
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
105 # lanaddr can be the empty string if not found,
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
106 # we need to return None in this case
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
107 return (self._upnp.lanaddr or None) if local else self._external_ip
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
108 return self._initialised.addCallback(getIP)
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
109
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
110 def _unmapPortsBlocking(self):
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
111 """Unmap ports mapped in this session"""
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
112 self._mutex.acquire()
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
113 try:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
114 for port, protocol in self._to_unmap:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
115 log.info(u"Unmapping port {}".format(port))
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
116 unmapping = self._upnp.deleteportmapping(
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
117 # the last parameter is remoteHost, we don't use it
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
118 port, protocol, '')
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
119
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
120 if not unmapping:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
121 log.error(u"Can't unmap port {port} ({protocol})".format(
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
122 port=port, protocol=protocol))
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
123 del self._to_unmap[:]
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
124 finally:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
125 self._mutex.release()
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
126
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
127 def _mapPortBlocking(self, int_port, ext_port, protocol, desc):
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
128 """Internal blocking method to map port
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
129
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
130 @param int_port(int): internal port to use
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
131 @param ext_port(int): external port to use, or None to find one automatically
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
132 @param protocol(str): 'TCP' or 'UDP'
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
133 @param desc(str): description of the mapping
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
134 @param return(int, None): external port used in case of success, otherwise None
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
135 """
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
136 # we use mutex to avoid race condition if 2 threads
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
137 # try to acquire a port at the same time
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
138 self._mutex.acquire()
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
139 try:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
140 if ext_port is None:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
141 # find a free port
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
142 starting_port = self._starting_port_cache
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
143 ext_port = STARTING_PORT if starting_port is None else starting_port
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
144 ret = self._upnp.getspecificportmapping(ext_port, protocol)
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
145 while ret != None and ext_port < 65536:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
146 ext_port += 1
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
147 ret = self._upnp.getspecificportmapping(ext_port, protocol)
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
148 if starting_port is None:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
149 # XXX: we cache the first successfuly found external port
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
150 # to avoid testing again the first series the next time
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
151 self._starting_port_cache = ext_port
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
152
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
153 mapping = self._upnp.addportmapping(
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
154 # the last parameter is remoteHost, we don't use it
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
155 ext_port, protocol, self._upnp.lanaddr, int_port, desc, '')
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
156
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
157 if not mapping:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
158 raise MappingError
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
159 else:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
160 self._to_unmap.append((ext_port, protocol))
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
161 finally:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
162 self._mutex.release()
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
163
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
164 return ext_port
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
165
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
166 def mapPort(self, int_port, ext_port=None, protocol='TCP', desc=DEFAULT_DESC):
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
167 """Add a port mapping
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
168
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
169 @param int_port(int): internal port to use
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
170 @param ext_port(int,None): external port to use, or None to find one automatically
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
171 @param protocol(str): 'TCP' or 'UDP'
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
172 @param desc(unicode): description of the mapping
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
173 Some UPnP IGD devices have broken encoding. It's probably a good idea to avoid non-ascii chars here
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
174 @return (D(int, None)): external port used in case of success, otherwise None
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
175 """
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
176 if self._upnp is None:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
177 return defer.succeed(None)
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
178 def mappingCb(ext_port):
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
179 log.info(u"{protocol} mapping from {int_port} to {ext_port} successful".format(
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
180 protocol = protocol,
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
181 int_port = int_port,
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
182 ext_port = ext_port,
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
183 ))
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
184 return ext_port
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
185 def mappingEb(failure):
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
186 failure.trap(MappingError)
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
187 log.warning(u"Can't map internal {int_port}".format(int_port=int_port))
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
188 d = threads.deferToThread(self._mapPortBlocking, int_port, ext_port, protocol, desc)
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
189 d.addCallbacks(mappingCb, mappingEb)
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
190 return d