comparison sat/plugins/plugin_misc_nat-port.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 26edcf3a30eb
children 378188abe941
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 from sat.core.i18n import _ 20 from sat.core.i18n import _
21 from sat.core.constants import Const as C 21 from sat.core.constants import Const as C
22 from sat.core.log import getLogger 22 from sat.core.log import getLogger
23
23 log = getLogger(__name__) 24 log = getLogger(__name__)
24 from sat.core import exceptions 25 from sat.core import exceptions
25 from twisted.internet import threads 26 from twisted.internet import threads
26 from twisted.internet import defer 27 from twisted.internet import defer
27 from twisted.python import failure 28 from twisted.python import failure
28 import threading 29 import threading
29 30
30 try: 31 try:
31 import miniupnpc 32 import miniupnpc
32 except ImportError: 33 except ImportError:
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)") 34 raise exceptions.MissingModule(
35 u"Missing module MiniUPnPc, please download/install it (and its Python binding) at http://miniupnp.free.fr/ (or use pip install miniupnpc)"
36 )
34 37
35 38
36 PLUGIN_INFO = { 39 PLUGIN_INFO = {
37 C.PI_NAME: "NAT port mapping", 40 C.PI_NAME: "NAT port mapping",
38 C.PI_IMPORT_NAME: "NAT-PORT", 41 C.PI_IMPORT_NAME: "NAT-PORT",
40 C.PI_MAIN: "NatPort", 43 C.PI_MAIN: "NatPort",
41 C.PI_HANDLER: "no", 44 C.PI_HANDLER: "no",
42 C.PI_DESCRIPTION: _("""Automatic NAT port mapping using UPnP"""), 45 C.PI_DESCRIPTION: _("""Automatic NAT port mapping using UPnP"""),
43 } 46 }
44 47
45 STARTING_PORT = 6000 # starting point to automatically find a port 48 STARTING_PORT = 6000 # starting point to automatically find a port
46 DEFAULT_DESC = u'SaT port mapping' # we don't use "à" here as some bugged NAT don't manage charset correctly 49 DEFAULT_DESC = (
50 u"SaT port mapping"
51 ) # we don't use "à" here as some bugged NAT don't manage charset correctly
47 52
48 53
49 class MappingError(Exception): 54 class MappingError(Exception):
50 pass 55 pass
51 56
56 def __init__(self, host): 61 def __init__(self, host):
57 log.info(_("plugin NAT Port initialization")) 62 log.info(_("plugin NAT Port initialization"))
58 self.host = host 63 self.host = host
59 self._external_ip = None 64 self._external_ip = None
60 self._initialised = defer.Deferred() 65 self._initialised = defer.Deferred()
61 self._upnp = miniupnpc.UPnP() # will be None if no device is available 66 self._upnp = miniupnpc.UPnP() # will be None if no device is available
62 self._upnp.discoverdelay=200 67 self._upnp.discoverdelay = 200
63 self._mutex = threading.Lock() # used to protect access to self._upnp 68 self._mutex = threading.Lock() # used to protect access to self._upnp
64 self._starting_port_cache = None # used to cache the first available port 69 self._starting_port_cache = None # used to cache the first available port
65 self._to_unmap = [] # list of tuples (ext_port, protocol) of ports to unmap on unload 70 self._to_unmap = [] # list of tuples (ext_port, protocol) of ports to unmap on unload
66 discover_d = threads.deferToThread(self._discover) 71 discover_d = threads.deferToThread(self._discover)
67 discover_d.chainDeferred(self._initialised) 72 discover_d.chainDeferred(self._initialised)
68 self._initialised.addErrback(self._init_failed) 73 self._initialised.addErrback(self._init_failed)
69 74
70 def unload(self): 75 def unload(self):
97 """Return IP address found with UPnP-IGD 102 """Return IP address found with UPnP-IGD
98 103
99 @param local(bool): True to get external IP address, False to get local network one 104 @param local(bool): True to get external IP address, False to get local network one
100 @return (None, str): found IP address, or None of something got wrong 105 @return (None, str): found IP address, or None of something got wrong
101 """ 106 """
107
102 def getIP(dummy): 108 def getIP(dummy):
103 if self._upnp is None: 109 if self._upnp is None:
104 return None 110 return None
105 # lanaddr can be the empty string if not found, 111 # lanaddr can be the empty string if not found,
106 # we need to return None in this case 112 # we need to return None in this case
107 return (self._upnp.lanaddr or None) if local else self._external_ip 113 return (self._upnp.lanaddr or None) if local else self._external_ip
114
108 return self._initialised.addCallback(getIP) 115 return self._initialised.addCallback(getIP)
109 116
110 def _unmapPortsBlocking(self): 117 def _unmapPortsBlocking(self):
111 """Unmap ports mapped in this session""" 118 """Unmap ports mapped in this session"""
112 self._mutex.acquire() 119 self._mutex.acquire()
113 try: 120 try:
114 for port, protocol in self._to_unmap: 121 for port, protocol in self._to_unmap:
115 log.info(u"Unmapping port {}".format(port)) 122 log.info(u"Unmapping port {}".format(port))
116 unmapping = self._upnp.deleteportmapping( 123 unmapping = self._upnp.deleteportmapping(
117 # the last parameter is remoteHost, we don't use it 124 # the last parameter is remoteHost, we don't use it
118 port, protocol, '') 125 port,
126 protocol,
127 "",
128 )
119 129
120 if not unmapping: 130 if not unmapping:
121 log.error(u"Can't unmap port {port} ({protocol})".format( 131 log.error(
122 port=port, protocol=protocol)) 132 u"Can't unmap port {port} ({protocol})".format(
133 port=port, protocol=protocol
134 )
135 )
123 del self._to_unmap[:] 136 del self._to_unmap[:]
124 finally: 137 finally:
125 self._mutex.release() 138 self._mutex.release()
126 139
127 def _mapPortBlocking(self, int_port, ext_port, protocol, desc): 140 def _mapPortBlocking(self, int_port, ext_port, protocol, desc):
141 # find a free port 154 # find a free port
142 starting_port = self._starting_port_cache 155 starting_port = self._starting_port_cache
143 ext_port = STARTING_PORT if starting_port is None else starting_port 156 ext_port = STARTING_PORT if starting_port is None else starting_port
144 ret = self._upnp.getspecificportmapping(ext_port, protocol) 157 ret = self._upnp.getspecificportmapping(ext_port, protocol)
145 while ret != None and ext_port < 65536: 158 while ret != None and ext_port < 65536:
146 ext_port += 1 159 ext_port += 1
147 ret = self._upnp.getspecificportmapping(ext_port, protocol) 160 ret = self._upnp.getspecificportmapping(ext_port, protocol)
148 if starting_port is None: 161 if starting_port is None:
149 # XXX: we cache the first successfuly found external port 162 # XXX: we cache the first successfuly found external port
150 # to avoid testing again the first series the next time 163 # to avoid testing again the first series the next time
151 self._starting_port_cache = ext_port 164 self._starting_port_cache = ext_port
152 165
153 try: 166 try:
154 mapping = self._upnp.addportmapping( 167 mapping = self._upnp.addportmapping(
155 # the last parameter is remoteHost, we don't use it 168 # the last parameter is remoteHost, we don't use it
156 ext_port, protocol, self._upnp.lanaddr, int_port, desc, '') 169 ext_port,
170 protocol,
171 self._upnp.lanaddr,
172 int_port,
173 desc,
174 "",
175 )
157 except Exception as e: 176 except Exception as e:
158 log.error(_(u"addportmapping error: {msg}").format(msg=e)) 177 log.error(_(u"addportmapping error: {msg}").format(msg=e))
159 raise failure.Failure(MappingError()) 178 raise failure.Failure(MappingError())
160 179
161 if not mapping: 180 if not mapping:
165 finally: 184 finally:
166 self._mutex.release() 185 self._mutex.release()
167 186
168 return ext_port 187 return ext_port
169 188
170 def mapPort(self, int_port, ext_port=None, protocol='TCP', desc=DEFAULT_DESC): 189 def mapPort(self, int_port, ext_port=None, protocol="TCP", desc=DEFAULT_DESC):
171 """Add a port mapping 190 """Add a port mapping
172 191
173 @param int_port(int): internal port to use 192 @param int_port(int): internal port to use
174 @param ext_port(int,None): external port to use, or None to find one automatically 193 @param ext_port(int,None): external port to use, or None to find one automatically
175 @param protocol(str): 'TCP' or 'UDP' 194 @param protocol(str): 'TCP' or 'UDP'
177 Some UPnP IGD devices have broken encoding. It's probably a good idea to avoid non-ascii chars here 196 Some UPnP IGD devices have broken encoding. It's probably a good idea to avoid non-ascii chars here
178 @return (D(int, None)): external port used in case of success, otherwise None 197 @return (D(int, None)): external port used in case of success, otherwise None
179 """ 198 """
180 if self._upnp is None: 199 if self._upnp is None:
181 return defer.succeed(None) 200 return defer.succeed(None)
201
182 def mappingCb(ext_port): 202 def mappingCb(ext_port):
183 log.info(u"{protocol} mapping from {int_port} to {ext_port} successful".format( 203 log.info(
184 protocol = protocol, 204 u"{protocol} mapping from {int_port} to {ext_port} successful".format(
185 int_port = int_port, 205 protocol=protocol, int_port=int_port, ext_port=ext_port
186 ext_port = ext_port, 206 )
187 )) 207 )
188 return ext_port 208 return ext_port
209
189 def mappingEb(failure_): 210 def mappingEb(failure_):
190 failure_.trap(MappingError) 211 failure_.trap(MappingError)
191 log.warning(u"Can't map internal {int_port}".format(int_port=int_port)) 212 log.warning(u"Can't map internal {int_port}".format(int_port=int_port))
213
192 def mappingUnknownEb(failure_): 214 def mappingUnknownEb(failure_):
193 log.error(_(u"error while trying to map ports: {msg}").format(msg=failure_)) 215 log.error(_(u"error while trying to map ports: {msg}").format(msg=failure_))
194 d = threads.deferToThread(self._mapPortBlocking, int_port, ext_port, protocol, desc) 216
217 d = threads.deferToThread(
218 self._mapPortBlocking, int_port, ext_port, protocol, desc
219 )
195 d.addCallbacks(mappingCb, mappingEb) 220 d.addCallbacks(mappingCb, mappingEb)
196 d.addErrback(mappingUnknownEb) 221 d.addErrback(mappingUnknownEb)
197 return d 222 return d