1536
|
1 #!/usr/bin/python |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 # SAT plugin for NAT port mapping |
|
5 # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Jérôme Poisson (goffi@goffi.org) |
|
6 |
|
7 # This program is free software: you can redistribute it and/or modify |
|
8 # it under the terms of the GNU Affero General Public License as published by |
|
9 # the Free Software Foundation, either version 3 of the License, or |
|
10 # (at your option) any later version. |
|
11 |
|
12 # This program is distributed in the hope that it will be useful, |
|
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
15 # GNU Affero General Public License for more details. |
|
16 |
|
17 # You should have received a copy of the GNU Affero General Public License |
|
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
19 |
|
20 from sat.core.i18n import _ |
|
21 from sat.core.constants import Const as C |
|
22 from sat.core.log import getLogger |
|
23 log = getLogger(__name__) |
|
24 from sat.core import exceptions |
|
25 from twisted.internet import threads |
|
26 from twisted.internet import defer |
|
27 |
|
28 try: |
|
29 import miniupnpc |
|
30 except ImportError: |
|
31 raise exceptions.MissingModule(u"Missing module MiniUPnPc, please download/install it (and its Python binding) at http://miniupnp.free.fr/") |
|
32 |
|
33 |
|
34 PLUGIN_INFO = { |
|
35 "name": "NAT port mapping", |
|
36 "import_name": "NAT-PORT", |
|
37 "type": C.PLUG_TYPE_MISC, |
|
38 "main": "NatPort", |
|
39 "handler": "no", |
|
40 "description": _("""Automatic NAT port mapping using UPnP"""), |
|
41 } |
|
42 |
|
43 |
|
44 class NatPort(object): |
|
45 # TODO: refresh data if a new connection is detected (see plugin_misc_ip) |
|
46 |
|
47 def __init__(self, host): |
|
48 log.info(_("plugin NAT Port initialization")) |
|
49 self.host = host |
|
50 self._external_ip = None |
|
51 self._initialised = defer.Deferred() |
|
52 self._upnp = miniupnpc.UPnP() # will be None if no device is available |
|
53 self._upnp.discoverdelay=200 |
|
54 discover_d = threads.deferToThread(self._discover) |
|
55 discover_d.chainDeferred(self._initialised) |
|
56 self._initialised.addErrback(self._init_failed) |
|
57 |
|
58 def _init_failed(self, failure): |
|
59 log.info(u"UPnP-GID not available") |
|
60 self._upnp = None |
|
61 |
|
62 def _discover(self): |
|
63 devices = self._upnp.discover() |
|
64 if devices: |
|
65 log.info(u"{nb} UPnP-IGD device(s) found".format(nb=devices)) |
|
66 else: |
|
67 log.info(u"Can't find UPnP-IGD device on the local network") |
|
68 raise exceptions.NotFound |
|
69 self._upnp.selectigd() |
|
70 self._external_ip = self._upnp.externalipaddress() |
|
71 |
|
72 def getIP(self, local=False): |
|
73 """Return IP address found with UPnP-IGD |
|
74 |
|
75 @param local(bool): True to get external IP address, False to get local network one |
|
76 @return (None, str): found IP address, or None of something got wrong |
|
77 """ |
|
78 def getIP(dummy): |
|
79 if self._upnp is None: |
|
80 return None |
|
81 # lanaddr can be the empty string if not found, |
|
82 # we need to return None in this case |
|
83 return (self._upnp.lanaddr or None) if local else self._external_ip |
|
84 return self._initialised.addCallback(getIP) |