annotate src/plugins/plugin_misc_nat-port.py @ 2266:084a75b8aa7a

tools (common/template): changed blog_date filter to date_days Instead of returning the whole sentence, date_days only return the number of days between now and the given timestamp. This way the template author can choose how to use it, and translation can be done template side.
author Goffi <goffi@goffi.org>
date Sat, 24 Jun 2017 21:47:06 +0200
parents 33c8c4973743
children 8b37a62336c3
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1934
2daf7b4c6756 use of /usr/bin/env instead of /usr/bin/python in shebang
Goffi <goffi@goffi.org>
parents: 1766
diff changeset
1 #!/usr/bin/env python2
1536
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
1766
d17772b0fe22 copyright update
Goffi <goffi@goffi.org>
parents: 1562
diff changeset
5 # Copyright (C) 2009-2016 Jérôme Poisson (goffi@goffi.org)
1536
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 = {
2145
33c8c4973743 core (plugins): added missing contants + use of new constants in PLUGIN_INFO
Goffi <goffi@goffi.org>
parents: 1934
diff changeset
37 C.PI_NAME: "NAT port mapping",
33c8c4973743 core (plugins): added missing contants + use of new constants in PLUGIN_INFO
Goffi <goffi@goffi.org>
parents: 1934
diff changeset
38 C.PI_IMPORT_NAME: "NAT-PORT",
33c8c4973743 core (plugins): added missing contants + use of new constants in PLUGIN_INFO
Goffi <goffi@goffi.org>
parents: 1934
diff changeset
39 C.PI_TYPE: C.PLUG_TYPE_MISC,
33c8c4973743 core (plugins): added missing contants + use of new constants in PLUGIN_INFO
Goffi <goffi@goffi.org>
parents: 1934
diff changeset
40 C.PI_MAIN: "NatPort",
33c8c4973743 core (plugins): added missing contants + use of new constants in PLUGIN_INFO
Goffi <goffi@goffi.org>
parents: 1934
diff changeset
41 C.PI_HANDLER: "no",
33c8c4973743 core (plugins): added missing contants + use of new constants in PLUGIN_INFO
Goffi <goffi@goffi.org>
parents: 1934
diff changeset
42 C.PI_DESCRIPTION: _("""Automatic NAT port mapping using UPnP"""),
1536
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