comparison src/plugins/plugin_misc_ip.py @ 1566:ec3848916ee8

plugin ip: implemented XEP-0279 for external ip retrieval + fixed bad exception handling
author Goffi <goffi@goffi.org>
date Sun, 08 Nov 2015 14:44:30 +0100
parents eb8aae35085b
children d5f59ba166fe
comparison
equal deleted inserted replaced
1565:d86685c0c019 1566:ec3848916ee8
26 from twisted.internet import defer 26 from twisted.internet import defer
27 from twisted.internet import reactor 27 from twisted.internet import reactor
28 from twisted.internet import protocol 28 from twisted.internet import protocol
29 from twisted.internet import endpoints 29 from twisted.internet import endpoints
30 from twisted.internet import error as internet_error 30 from twisted.internet import error as internet_error
31 from zope.interface import implements
32 from wokkel import disco, iwokkel
33 from twisted.words.protocols.jabber.xmlstream import XMPPHandler
31 import urlparse 34 import urlparse
32 try: 35 try:
33 import netifaces 36 import netifaces
34 except ImportError: 37 except ImportError:
35 log.warning(u"netifaces is not available, it help discovering IPs, you can install it on https://pypi.python.org/pypi/netifaces") 38 log.warning(u"netifaces is not available, it help discovering IPs, you can install it on https://pypi.python.org/pypi/netifaces")
38 41
39 PLUGIN_INFO = { 42 PLUGIN_INFO = {
40 "name": "IP discovery", 43 "name": "IP discovery",
41 "import_name": "IP", 44 "import_name": "IP",
42 "type": C.PLUG_TYPE_MISC, 45 "type": C.PLUG_TYPE_MISC,
46 "protocols": ["XEP-0279"],
43 "recommendations": ["NAT-PORT"], 47 "recommendations": ["NAT-PORT"],
44 "main": "IPPlugin", 48 "main": "IPPlugin",
45 "handler": "no", 49 "handler": "yes",
46 "description": _("""This plugin help to discover our external IP address.""") 50 "description": _("""This plugin help to discover our external IP address.""")
47 } 51 }
48 52
49 GET_IP_PAGE = "http://www.goffi.org/sat_tools/get_ip.php" # This page must only return external IP of the requester 53 GET_IP_PAGE = "http://www.goffi.org/sat_tools/get_ip.php" # This page must only return external IP of the requester
50 GET_IP_LABEL = D_(u"Allow external get IP") 54 GET_IP_LABEL = D_(u"Allow external get IP")
60 Do you agree to do this request ? 64 Do you agree to do this request ?
61 """).format( 65 """).format(
62 page = GET_IP_PAGE, 66 page = GET_IP_PAGE,
63 domain = urlparse.urlparse(GET_IP_PAGE).netloc, 67 domain = urlparse.urlparse(GET_IP_PAGE).netloc,
64 app_name = C.APP_NAME) 68 app_name = C.APP_NAME)
69 NS_IP_CHECK = "urn:xmpp:sic:1"
65 70
66 PARAMS = """ 71 PARAMS = """
67 <params> 72 <params>
68 <general> 73 <general>
69 <category name="{category}"> 74 <category name="{category}">
75 80
76 81
77 class IPPlugin(object): 82 class IPPlugin(object):
78 # TODO: refresh IP if a new connection is detected 83 # TODO: refresh IP if a new connection is detected
79 # TODO: manage IPv6 when implemented in SàT 84 # TODO: manage IPv6 when implemented in SàT
80 # TODO: implement XEP-0279
81 85
82 def __init__(self, host): 86 def __init__(self, host):
83 log.info(_("plugin IP discovery initialization")) 87 log.info(_("plugin IP discovery initialization"))
84 self.host = host 88 self.host = host
85 host.memory.updateParams(PARAMS) 89 host.memory.updateParams(PARAMS)
93 97
94 # XXX: cache is kept until SàT is restarted 98 # XXX: cache is kept until SàT is restarted
95 # if IP may have changed, use self.refreshIP 99 # if IP may have changed, use self.refreshIP
96 self._external_ip_cache = None 100 self._external_ip_cache = None
97 self._local_ip_cache = None 101 self._local_ip_cache = None
102
103 def getHandler(self, profile):
104 return IPPlugin_handler()
98 105
99 def refreshIP(self): 106 def refreshIP(self):
100 # FIXME: use a trigger instead ? 107 # FIXME: use a trigger instead ?
101 self._external_ip_cache = None 108 self._external_ip_cache = None
102 self._local_ip_cache = None 109 self._local_ip_cache = None
171 return local_ip 178 return local_ip
172 179
173 d = endpoints.connectProtocol(point, protocol.Protocol()) 180 d = endpoints.connectProtocol(point, protocol.Protocol())
174 d.addCallback(gotConnection) 181 d.addCallback(gotConnection)
175 return d 182 return d
176
177 183
178 @defer.inlineCallbacks 184 @defer.inlineCallbacks
179 def getLocalIPs(self, profile): 185 def getLocalIPs(self, profile):
180 """Try do discover local area network IPs 186 """Try do discover local area network IPs
181 187
226 if not allow_get_ip: 232 if not allow_get_ip:
227 defer.returnValue(addresses) 233 defer.returnValue(addresses)
228 234
229 try: 235 try:
230 ip_tuple = yield self._getIPFromExternal(GET_IP_PAGE) 236 ip_tuple = yield self._getIPFromExternal(GET_IP_PAGE)
231 except Exception as internet_error.DNSLookupError: 237 except (internet_error.DNSLookupError, internet_error.TimeoutError):
232 log.warning(u"Can't access Domain Name System") 238 log.warning(u"Can't access Domain Name System")
233 defer.returnValue(addresses) 239 defer.returnValue(addresses)
234 self._insertFirst(addresses, ip_tuple.local) 240 self._insertFirst(addresses, ip_tuple.local)
235 defer.returnValue(addresses) 241 defer.returnValue(addresses)
236 242
237
238 @defer.inlineCallbacks 243 @defer.inlineCallbacks
239 def getExternalIP(self, profile): 244 def getExternalIP(self, profile):
240 """Try to discover external IP 245 """Try to discover external IP
241 246
242 @param profile: %(doc_profile)s 247 @param profile: %(doc_profile)s
243 @return (deferred): external IP address or None if it can't be discovered 248 @return (deferred): external IP address or None if it can't be discovered
244 """ 249 """
245 if self._external_ip_cache is not None: 250 if self._external_ip_cache is not None:
246 defer.returnValue(self._external_ip_cache) 251 defer.returnValue(self._external_ip_cache)
247 252
248 # we first try with NAT-Port 253 # we first try with XEP-0279
254 if self.host.hasFeature(NS_IP_CHECK, profile=profile):
255 log.debug(u"Server IP Check available, we use it to retrieve our IP")
256 client = self.host.getClient(profile)
257 iq_elt = client.IQ("get")
258 iq_elt.addElement((NS_IP_CHECK, 'address'))
259 result_elt = yield iq_elt.send()
260 try:
261 address_elt = result_elt.elements(NS_IP_CHECK, 'address').next()
262 ip_elt = address_elt.elements(NS_IP_CHECK,'ip').next()
263 except StopIteration:
264 log.warning(u"Server returned invalid result on XEP-0279 request, we ignore it")
265 else:
266 # FIXME: server IP may not be the same as external IP (server can be on local machine or network)
267 # IP should be checked to see if we have a local one, and rejected in this case
268 external_ip = str(ip_elt)
269 log.debug(u"External IP found: {}".format(external_ip))
270 self._external_ip_cache = external_ip
271 defer.returnValue(self._external_ip_cache)
272
273 # then with NAT-Port
249 if self._nat is not None: 274 if self._nat is not None:
250 nat_ip = yield self._nat.getIP() 275 nat_ip = yield self._nat.getIP()
251 if nat_ip is not None: 276 if nat_ip is not None:
252 self._external_ip_cache = nat_ip 277 self._external_ip_cache = nat_ip
253 defer.returnValue(nat_ip) 278 defer.returnValue(nat_ip)
254 279
255 # then by requesting external website 280 # and finally by requesting external website
256 allow_get_ip = yield self._externalAllowed(profile) 281 allow_get_ip = yield self._externalAllowed(profile)
257 try: 282 try:
258 ip = (yield webclient.getPage(GET_IP_PAGE)) if allow_get_ip else None 283 ip = (yield webclient.getPage(GET_IP_PAGE)) if allow_get_ip else None
259 except internet_error.DNSLookupError: 284 except (internet_error.DNSLookupError, internet_error.TimeoutError):
260 log.warning(u"Can't access Domain Name System") 285 log.warning(u"Can't access Domain Name System")
261 ip = None 286 ip = None
262 else: 287 else:
263 self._external_ip_cache = ip 288 self._external_ip_cache = ip
264 defer.returnValue(ip) 289 defer.returnValue(ip)
290
291
292 class IPPlugin_handler(XMPPHandler):
293 implements(iwokkel.IDisco)
294
295 def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
296 return [disco.DiscoFeature(NS_IP_CHECK)]
297
298 def getDiscoItems(self, requestor, target, nodeIdentifier=''):
299 return []