annotate sat/plugins/plugin_misc_nat_port.py @ 4043:9641ce286e07

core (main): if `keep_id` is set in `action_new`, use it as `action_id`
author Goffi <goffi@goffi.org>
date Mon, 15 May 2023 16:20:58 +0200
parents 524856bd7b19
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
1 #!/usr/bin/env python3
3137
559a625a236b fixed shebangs
Goffi <goffi@goffi.org>
parents: 3136
diff changeset
2
1536
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
3479
be6d91572633 date update
Goffi <goffi@goffi.org>
parents: 3137
diff changeset
5 # Copyright (C) 2009-2021 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
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
23
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
24 log = getLogger(__name__)
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
25 from sat.core import exceptions
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
26 from twisted.internet import threads
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
27 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
28 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
29 import threading
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
30
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
31 try:
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
32 import miniupnpc
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
33 except ImportError:
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
34 raise exceptions.MissingModule(
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
35 "Missing module MiniUPnPc, please download/install it (and its Python binding) at http://miniupnp.free.fr/ (or use pip install miniupnpc)"
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
36 )
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
37
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
38
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
39 PLUGIN_INFO = {
2145
33c8c4973743 core (plugins): added missing contants + use of new constants in PLUGIN_INFO
Goffi <goffi@goffi.org>
parents: 1934
diff changeset
40 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
41 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
42 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
43 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
44 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
45 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
46 }
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
47
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
48 STARTING_PORT = 6000 # starting point to automatically find a port
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
49 DEFAULT_DESC = (
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
50 "SaT port mapping"
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
51 ) # we don't use "à" here as some bugged NAT don't manage charset correctly
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
52
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
53
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
54 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
55 pass
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
56
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
57
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
58 class NatPort(object):
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
59 # 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
60
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
61 def __init__(self, host):
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
62 log.info(_("plugin NAT Port initialization"))
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
63 self.host = host
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
64 self._external_ip = None
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
65 self._initialised = defer.Deferred()
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
66 self._upnp = miniupnpc.UPnP() # will be None if no device is available
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
67 self._upnp.discoverdelay = 200
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
68 self._mutex = threading.Lock() # used to protect access to self._upnp
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
69 self._starting_port_cache = None # used to cache the first available port
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
70 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
71 discover_d = threads.deferToThread(self._discover)
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
72 discover_d.chainDeferred(self._initialised)
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
73 self._initialised.addErrback(self._init_failed)
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
74
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
75 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
76 if self._to_unmap:
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
77 log.info("Cleaning mapped ports")
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
78 return threads.deferToThread(self._unmap_ports_blocking)
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
79
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
80 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
81 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
82 if e == exceptions.FeatureNotFound:
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
83 log.info("UPnP-IGD seems to be not activated on the device")
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
84 else:
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
85 log.info("UPnP-IGD not available")
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
86 self._upnp = None
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
87
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
88 def _discover(self):
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
89 devices = self._upnp.discover()
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
90 if devices:
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
91 log.info("{nb} UPnP-IGD device(s) found".format(nb=devices))
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
92 else:
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
93 log.info("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
94 raise failure.Failure(exceptions.NotFound())
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
95 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
96 try:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
97 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
98 except Exception:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
99 raise failure.Failure(exceptions.FeatureNotFound())
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
100
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
101 def get_ip(self, local=False):
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
102 """Return IP address found with UPnP-IGD
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
103
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
104 @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
105 @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
106 """
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
107
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
108 def get_ip(__):
1536
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
109 if self._upnp is None:
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
110 return None
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
111 # lanaddr can be the empty string if not found,
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
112 # we need to return None in this case
04a13d9ae265 plugin nat-port: NAT port first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
113 return (self._upnp.lanaddr or None) if local else self._external_ip
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
114
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
115 return self._initialised.addCallback(get_ip)
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
116
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
117 def _unmap_ports_blocking(self):
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
118 """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
119 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
120 try:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
121 for port, protocol in self._to_unmap:
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
122 log.info("Unmapping port {}".format(port))
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
123 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
124 # the last parameter is remoteHost, we don't use it
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
125 port,
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
126 protocol,
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
127 "",
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
128 )
1554
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 if not unmapping:
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
131 log.error(
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
132 "Can't unmap port {port} ({protocol})".format(
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
133 port=port, protocol=protocol
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
134 )
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
135 )
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
136 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
137 finally:
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.release()
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
139
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
140 def _map_port_blocking(self, int_port, ext_port, protocol, desc):
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
141 """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
142
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
143 @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
144 @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
145 @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
146 @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
147 @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
148 """
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
149 # 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
150 # 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
151 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
152 try:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
153 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
154 # 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
155 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
156 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
157 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
158 while ret != None and ext_port < 65536:
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
159 ext_port += 1
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
160 ret = self._upnp.getspecificportmapping(ext_port, protocol)
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
161 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
162 # 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
163 # 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
164 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
165
2488
78c7992a26ed plugin NAT-port: raise MappingError if something get wrong during "addportmapping" + log unexpected errors (i.e. everything else than MappingError)
Goffi <goffi@goffi.org>
parents: 2483
diff changeset
166 try:
78c7992a26ed plugin NAT-port: raise MappingError if something get wrong during "addportmapping" + log unexpected errors (i.e. everything else than MappingError)
Goffi <goffi@goffi.org>
parents: 2483
diff changeset
167 mapping = self._upnp.addportmapping(
78c7992a26ed plugin NAT-port: raise MappingError if something get wrong during "addportmapping" + log unexpected errors (i.e. everything else than MappingError)
Goffi <goffi@goffi.org>
parents: 2483
diff changeset
168 # the last parameter is remoteHost, we don't use it
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
169 ext_port,
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
170 protocol,
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
171 self._upnp.lanaddr,
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
172 int_port,
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
173 desc,
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
174 "",
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
175 )
2488
78c7992a26ed plugin NAT-port: raise MappingError if something get wrong during "addportmapping" + log unexpected errors (i.e. everything else than MappingError)
Goffi <goffi@goffi.org>
parents: 2483
diff changeset
176 except Exception as e:
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
177 log.error(_("addportmapping error: {msg}").format(msg=e))
2488
78c7992a26ed plugin NAT-port: raise MappingError if something get wrong during "addportmapping" + log unexpected errors (i.e. everything else than MappingError)
Goffi <goffi@goffi.org>
parents: 2483
diff changeset
178 raise failure.Failure(MappingError())
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
179
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
180 if not mapping:
2488
78c7992a26ed plugin NAT-port: raise MappingError if something get wrong during "addportmapping" + log unexpected errors (i.e. everything else than MappingError)
Goffi <goffi@goffi.org>
parents: 2483
diff changeset
181 raise failure.Failure(MappingError())
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
182 else:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
183 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
184 finally:
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
185 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
186
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
187 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
188
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
189 def map_port(self, int_port, ext_port=None, protocol="TCP", desc=DEFAULT_DESC):
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
190 """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
191
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
192 @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
193 @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
194 @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
195 @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
196 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
197 @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
198 """
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
199 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
200 return defer.succeed(None)
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
201
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
202 def mapping_cb(ext_port):
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
203 log.info(
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
204 "{protocol} mapping from {int_port} to {ext_port} successful".format(
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
205 protocol=protocol, int_port=int_port, ext_port=ext_port
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
206 )
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
207 )
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
208 return ext_port
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
209
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
210 def mapping_eb(failure_):
2488
78c7992a26ed plugin NAT-port: raise MappingError if something get wrong during "addportmapping" + log unexpected errors (i.e. everything else than MappingError)
Goffi <goffi@goffi.org>
parents: 2483
diff changeset
211 failure_.trap(MappingError)
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
212 log.warning("Can't map internal {int_port}".format(int_port=int_port))
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
213
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
214 def mapping_unknown_eb(failure_):
3028
ab2696e34d29 Python 3 port:
Goffi <goffi@goffi.org>
parents: 2771
diff changeset
215 log.error(_("error while trying to map ports: {msg}").format(msg=failure_))
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
216
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
217 d = threads.deferToThread(
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
218 self._map_port_blocking, int_port, ext_port, protocol, desc
2624
56f94936df1e code style reformatting using black
Goffi <goffi@goffi.org>
parents: 2562
diff changeset
219 )
4037
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
220 d.addCallbacks(mapping_cb, mapping_eb)
524856bd7b19 massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents: 3559
diff changeset
221 d.addErrback(mapping_unknown_eb)
1554
e281ed2c21db plugin NAT port: added UPnP IGD mapping + automatic unmapping on backend shut down
Goffi <goffi@goffi.org>
parents: 1536
diff changeset
222 return d