From e2bb5952ebcc4fedc19202aa5183d75136815de8 Mon Sep 17 00:00:00 2001 From: sirMackk Date: Sat, 7 Mar 2015 19:22:09 -0500 Subject: [PATCH] Updates from issue #54 - obtaining local ip, logging, standalone --- src/util/UpnpPunch.py | 85 +++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/src/util/UpnpPunch.py b/src/util/UpnpPunch.py index 28d75ef1..29b694af 100644 --- a/src/util/UpnpPunch.py +++ b/src/util/UpnpPunch.py @@ -4,7 +4,7 @@ from gevent import monkey monkey.patch_socket() -import re, urllib2, httplib +import re, urllib2, httplib, logging from urlparse import urlparse from xml.dom.minidom import parseString @@ -16,7 +16,7 @@ from xml.dom.minidom import parseString remove_whitespace = re.compile(r'>\s*<') -def _m_search_ssdp(): +def _m_search_ssdp(local_ip): """ Broadcast a UDP SSDP M-SEARCH packet and return response. """ @@ -32,13 +32,17 @@ def _m_search_ssdp(): ) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + sock.bind((local_ip, 10000)) + sock.sendto(ssdp_request, ('239.255.255.250', 1900)) sock.settimeout(5) try: return sock.recv(2048) - except socket.error: + except socket.error, err: # no reply from IGD, possibly no IGD on LAN + logging.debug("UDP SSDP M-SEARCH send error using ip %s: %s" % (local_ip, err)) return False @@ -102,12 +106,11 @@ def _get_local_ip(): return s.getsockname()[0] -def _create_soap_message(port, description="UPnPPunch", protocol="TCP", +def _create_soap_message(local_ip, port, description="UPnPPunch", protocol="TCP", upnp_schema='WANIPConnection'): """ Build a SOAP AddPortMapping message. """ - current_ip = _get_local_ip() soap_message = """ @@ -125,7 +128,7 @@ def _create_soap_message(port, description="UPnPPunch", protocol="TCP", """.format(port=port, protocol=protocol, - host_ip=current_ip, + host_ip=local_ip, description=description, upnp_schema=upnp_schema) return remove_whitespace.sub('><', soap_message) @@ -138,9 +141,11 @@ def _parse_for_errors(soap_response): err_msg = _node_val( err_dom.getElementsByTagName('errorDescription')[0] ) + logging.error('SOAP request error: {0} - {1}'.format(err_code, err_msg)) raise Exception( 'SOAP request error: {0} - {1}'.format(err_code, err_msg) ) + return False else: return True @@ -171,35 +176,59 @@ def open_port(port=15441, desc="UpnpPunch"): Attempt to forward a port using UPnP. """ - idg_response = _m_search_ssdp() + local_ips = [_get_local_ip()] + try: + local_ips += socket.gethostbyname_ex('')[2] # Get ip by '' hostrname not supported on all platform + except: + pass - if not idg_response: - return False + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(('8.8.8.8', 0)) # Using google dns route + local_ips.append(s.getsockname()[0]) + except: + pass - location = _retrieve_location_from_ssdp(idg_response) + local_ips = list(set(local_ips)) # Delete duplicates + logging.debug("Found local ips: %s" % local_ips) - if not location: - return False + for local_ip in local_ips: + logging.debug("Trying using local ip: %s" % local_ip) + idg_response = _m_search_ssdp(local_ip) - parsed = _parse_igd_profile( - _retrieve_igd_profile(location) - ) + if not idg_response: + logging.debug("No IGD response") + continue - if not parsed: - return False + location = _retrieve_location_from_ssdp(idg_response) - control_url, upnp_schema = parsed + if not location: + logging.debug("No location") + continue - soap_messages = [_create_soap_message(port, desc, proto, upnp_schema) - for proto in ['TCP', 'UDP']] + parsed = _parse_igd_profile( + _retrieve_igd_profile(location) + ) - requests = [gevent.spawn( - _send_soap_request, location, upnp_schema, control_url, message - ) for message in soap_messages] + if not parsed: + logging.debug("IGD parse error using location %s" % repr(location)) + continue - gevent.joinall(requests, timeout=3) + control_url, upnp_schema = parsed - if all(requests): - return True - else: - return False + soap_messages = [_create_soap_message(local_ip, port, desc, proto, upnp_schema) + for proto in ['TCP', 'UDP']] + + requests = [gevent.spawn( + _send_soap_request, location, upnp_schema, control_url, message + ) for message in soap_messages] + + gevent.joinall(requests, timeout=3) + + if all([request.value for request in requests]): + return True + return False + +if __name__ == "__main__": + logging.getLogger().setLevel(logging.DEBUG) + print open_port(15441, "ZeroNet")