From 44118fb56e07d3c7d2c7d76aa5e8bc5268d4fac7 Mon Sep 17 00:00:00 2001 From: Feng-Zihao Date: Mon, 21 Jul 2014 14:10:57 +0800 Subject: [PATCH 1/2] End the endless loop. --- stun/__init__.py | 227 ++++++++++++++++++++++++----------------------- 1 file changed, 116 insertions(+), 111 deletions(-) diff --git a/stun/__init__.py b/stun/__init__.py index dc0c0f5..afe37a6 100644 --- a/stun/__init__.py +++ b/stun/__init__.py @@ -83,8 +83,12 @@ def enable_logging(): RestricPortNAT = "Restric Port NAT" SymmetricNAT = "Symmetric NAT" ChangedAddressError = "Meet an error, when do Test1 on Changed IP and Port" +StunServertNotAccessible = "Stun host not accessible" +class BindErrorResponseException(Exception): + pass + def _initialize(): items = dictAttrToVal.items() for i in xrange(len(items)): @@ -98,7 +102,6 @@ def gen_tran_id(): a = '' for i in xrange(32): a += random.choice('0123456789ABCDEF') - #return binascii.a2b_hex(a) return a @@ -110,73 +113,71 @@ def stun_test(sock, host, port, source_ip, source_port, send_data=""): tranid = gen_tran_id() str_data = ''.join([BindRequestMsg, str_len, tranid, send_data]) data = binascii.a2b_hex(str_data) - recvCorr = False - while not recvCorr: - recieved = False - count = 3 - while not recieved: - log.debug("sendto %s" % str((host, port))) - try: - sock.sendto(data, (host, port)) - except socket.gaierror: + recieved = False + count = 3 + while not recieved: + log.debug("sendto %s" % str((host, port))) + try: + sock.sendto(data, (host, port)) + except socket.gaierror: + retVal['Resp'] = False + return retVal + try: + buf, addr = sock.recvfrom(2048) + log.debug("recvfrom: %s" % str(addr)) + recieved = True + except Exception: + recieved = False + if count > 1: + count -= 1 + else: retVal['Resp'] = False return retVal - try: - buf, addr = sock.recvfrom(2048) - log.debug("recvfrom: %s" % str(addr)) - recieved = True - except Exception: - recieved = False - if count > 0: - count -= 1 - else: - retVal['Resp'] = False - return retVal - msgtype = binascii.b2a_hex(buf[0:2]) - bind_resp_msg = dictValToMsgType[msgtype] == "BindResponseMsg" - tranid_match = tranid.upper() == binascii.b2a_hex(buf[4:20]).upper() - if bind_resp_msg and tranid_match: - recvCorr = True - retVal['Resp'] = True - len_message = int(binascii.b2a_hex(buf[2:4]), 16) - len_remain = len_message - base = 20 - while len_remain: - attr_type = binascii.b2a_hex(buf[base:(base + 2)]) - attr_len = int(binascii.b2a_hex(buf[(base + 2):(base + 4)]), - 16) - if attr_type == MappedAddress: - port = int(binascii.b2a_hex(buf[base + 6:base + 8]), 16) - ip = ".".join([ - str(int(binascii.b2a_hex(buf[base + 8:base + 9]), 16)), - str(int(binascii.b2a_hex(buf[base + 9:base + 10]), 16)), - str(int(binascii.b2a_hex(buf[base + 10:base + 11]), 16)), - str(int(binascii.b2a_hex(buf[base + 11:base + 12]), 16))]) - retVal['ExternalIP'] = ip - retVal['ExternalPort'] = port - if attr_type == SourceAddress: - port = int(binascii.b2a_hex(buf[base + 6:base + 8]), 16) - ip = ".".join([ - str(int(binascii.b2a_hex(buf[base + 8:base + 9]), 16)), - str(int(binascii.b2a_hex(buf[base + 9:base + 10]), 16)), - str(int(binascii.b2a_hex(buf[base + 10:base + 11]), 16)), - str(int(binascii.b2a_hex(buf[base + 11:base + 12]), 16))]) - retVal['SourceIP'] = ip - retVal['SourcePort'] = port - if attr_type == ChangedAddress: - port = int(binascii.b2a_hex(buf[base + 6:base + 8]), 16) - ip = ".".join([ - str(int(binascii.b2a_hex(buf[base + 8:base + 9]), 16)), - str(int(binascii.b2a_hex(buf[base + 9:base + 10]), 16)), - str(int(binascii.b2a_hex(buf[base + 10:base + 11]), 16)), - str(int(binascii.b2a_hex(buf[base + 11:base + 12]), 16))]) - retVal['ChangedIP'] = ip - retVal['ChangedPort'] = port - #if attr_type == ServerName: - #serverName = buf[(base+4):(base+4+attr_len)] - base = base + 4 + attr_len - len_remain = len_remain - (4 + attr_len) - #s.close() + msgtype = binascii.b2a_hex(buf[0:2]) + bind_resp_msg = dictValToMsgType[msgtype] == "BindResponseMsg" + tranid_match = tranid.upper() == binascii.b2a_hex(buf[4:20]).upper() + if bind_resp_msg and tranid_match: + retVal['Resp'] = True + len_message = int(binascii.b2a_hex(buf[2:4]), 16) + len_remain = len_message + base = 20 + while len_remain: + attr_type = binascii.b2a_hex(buf[base:(base + 2)]) + attr_len = int(binascii.b2a_hex(buf[(base + 2):(base + 4)]), + 16) + if attr_type == MappedAddress: + port = int(binascii.b2a_hex(buf[base + 6:base + 8]), 16) + ip = ".".join([ + str(int(binascii.b2a_hex(buf[base + 8:base + 9]), 16)), + str(int(binascii.b2a_hex(buf[base + 9:base + 10]), 16)), + str(int(binascii.b2a_hex(buf[base + 10:base + 11]), 16)), + str(int(binascii.b2a_hex(buf[base + 11:base + 12]), 16))]) + retVal['ExternalIP'] = ip + retVal['ExternalPort'] = port + if attr_type == SourceAddress: + port = int(binascii.b2a_hex(buf[base + 6:base + 8]), 16) + ip = ".".join([ + str(int(binascii.b2a_hex(buf[base + 8:base + 9]), 16)), + str(int(binascii.b2a_hex(buf[base + 9:base + 10]), 16)), + str(int(binascii.b2a_hex(buf[base + 10:base + 11]), 16)), + str(int(binascii.b2a_hex(buf[base + 11:base + 12]), 16))]) + retVal['SourceIP'] = ip + retVal['SourcePort'] = port + if attr_type == ChangedAddress: + port = int(binascii.b2a_hex(buf[base + 6:base + 8]), 16) + ip = ".".join([ + str(int(binascii.b2a_hex(buf[base + 8:base + 9]), 16)), + str(int(binascii.b2a_hex(buf[base + 9:base + 10]), 16)), + str(int(binascii.b2a_hex(buf[base + 10:base + 11]), 16)), + str(int(binascii.b2a_hex(buf[base + 11:base + 12]), 16))]) + retVal['ChangedIP'] = ip + retVal['ChangedPort'] = port + base = base + 4 + attr_len + len_remain = len_remain - (4 + attr_len) + else: + retVal = None + if retVal is None: + raise BindErrorResponseException return retVal @@ -185,59 +186,63 @@ def get_nat_type(s, source_ip, source_port, stun_host=None, stun_port=3478): port = stun_port log.debug("Do Test1") resp = False - if stun_host: - ret = stun_test(s, stun_host, port, source_ip, source_port) - resp = ret['Resp'] - else: - for stun_host in stun_servers_list: - log.debug('Trying STUN host: %s' % stun_host) + typ = StunServertNotAccessible + try: + if stun_host: ret = stun_test(s, stun_host, port, source_ip, source_port) resp = ret['Resp'] - if resp: - break - if not resp: - return Blocked, ret - log.debug("Result: %s" % ret) - exIP = ret['ExternalIP'] - exPort = ret['ExternalPort'] - changedIP = ret['ChangedIP'] - changedPort = ret['ChangedPort'] - if ret['ExternalIP'] == source_ip: - changeRequest = ''.join([ChangeRequest, '0004', "00000006"]) - ret = stun_test(s, stun_host, port, source_ip, source_port, - changeRequest) - if ret['Resp']: - typ = OpenInternet else: - typ = SymmetricUDPFirewall - else: - changeRequest = ''.join([ChangeRequest, '0004', "00000006"]) - log.debug("Do Test2") - ret = stun_test(s, stun_host, port, source_ip, source_port, - changeRequest) + for stun_host in stun_servers_list: + log.debug('Trying STUN host: %s' % stun_host) + ret = stun_test(s, stun_host, port, source_ip, source_port) + resp = ret['Resp'] + if resp: + break + if not resp: + return Blocked, ret log.debug("Result: %s" % ret) - if ret['Resp']: - typ = FullCone + exIP = ret['ExternalIP'] + exPort = ret['ExternalPort'] + changedIP = ret['ChangedIP'] + changedPort = ret['ChangedPort'] + if ret['ExternalIP'] == source_ip: + changeRequest = ''.join([ChangeRequest, '0004', "00000006"]) + ret = stun_test(s, stun_host, port, source_ip, source_port, + changeRequest) + if ret['Resp']: + typ = OpenInternet + else: + typ = SymmetricUDPFirewall else: - log.debug("Do Test1") - ret = stun_test(s, changedIP, changedPort, source_ip, source_port) + changeRequest = ''.join([ChangeRequest, '0004', "00000006"]) + log.debug("Do Test2") + ret = stun_test(s, stun_host, port, source_ip, source_port, + changeRequest) log.debug("Result: %s" % ret) - if not ret['Resp']: - typ = ChangedAddressError + if ret['Resp']: + typ = FullCone else: - if exIP == ret['ExternalIP'] and exPort == ret['ExternalPort']: - changePortRequest = ''.join([ChangeRequest, '0004', - "00000002"]) - log.debug("Do Test3") - ret = stun_test(s, changedIP, port, source_ip, source_port, - changePortRequest) - log.debug("Result: %s" % ret) - if ret['Resp'] == True: - typ = RestricNAT - else: - typ = RestricPortNAT + log.debug("Do Test1") + ret = stun_test(s, changedIP, changedPort, source_ip, source_port) + log.debug("Result: %s" % ret) + if not ret['Resp']: + typ = ChangedAddressError else: - typ = SymmetricNAT + if exIP == ret['ExternalIP'] and exPort == ret['ExternalPort']: + changePortRequest = ''.join([ChangeRequest, '0004', + "00000002"]) + log.debug("Do Test3") + ret = stun_test(s, changedIP, port, source_ip, source_port, + changePortRequest) + log.debug("Result: %s" % ret) + if ret['Resp'] == True: + typ = RestricNAT + else: + typ = RestricPortNAT + else: + typ = SymmetricNAT + except BindErrorResponseException: + pass return typ, ret From a0a7fc0431d85e5381a7252f6502087a33a2cdc3 Mon Sep 17 00:00:00 2001 From: Feng-Zihao Date: Mon, 21 Jul 2014 15:30:32 +0800 Subject: [PATCH 2/2] Fix logic which no ChangeAddress field in response. --- stun/__init__.py | 116 +++++++++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/stun/__init__.py b/stun/__init__.py index afe37a6..2598cd9 100644 --- a/stun/__init__.py +++ b/stun/__init__.py @@ -83,12 +83,9 @@ def enable_logging(): RestricPortNAT = "Restric Port NAT" SymmetricNAT = "Symmetric NAT" ChangedAddressError = "Meet an error, when do Test1 on Changed IP and Port" -StunServertNotAccessible = "Stun host not accessible" +NotEnoughEvidence = "No enough evidence to judge NAT type" -class BindErrorResponseException(Exception): - pass - def _initialize(): items = dictAttrToVal.items() for i in xrange(len(items)): @@ -105,7 +102,7 @@ def gen_tran_id(): return a -def stun_test(sock, host, port, source_ip, source_port, send_data=""): +def stun_test(sock, host, port, source_ip, source_port, send_data="", retry=3): retVal = {'Resp': False, 'ExternalIP': None, 'ExternalPort': None, 'SourceIP': None, 'SourcePort': None, 'ChangedIP': None, 'ChangedPort': None} @@ -114,7 +111,6 @@ def stun_test(sock, host, port, source_ip, source_port, send_data=""): str_data = ''.join([BindRequestMsg, str_len, tranid, send_data]) data = binascii.a2b_hex(str_data) recieved = False - count = 3 while not recieved: log.debug("sendto %s" % str((host, port))) try: @@ -128,13 +124,15 @@ def stun_test(sock, host, port, source_ip, source_port, send_data=""): recieved = True except Exception: recieved = False - if count > 1: - count -= 1 + if retry > 0: + retry -= 1 else: retVal['Resp'] = False return retVal + log.debug("sock: %s" % str(addr)) msgtype = binascii.b2a_hex(buf[0:2]) bind_resp_msg = dictValToMsgType[msgtype] == "BindResponseMsg" + log.debug("msgtype : %s" % dictValToMsgType[msgtype]) tranid_match = tranid.upper() == binascii.b2a_hex(buf[4:20]).upper() if bind_resp_msg and tranid_match: retVal['Resp'] = True @@ -175,9 +173,7 @@ def stun_test(sock, host, port, source_ip, source_port, send_data=""): base = base + 4 + attr_len len_remain = len_remain - (4 + attr_len) else: - retVal = None - if retVal is None: - raise BindErrorResponseException + retVal['Resp'] = False return retVal @@ -186,63 +182,63 @@ def get_nat_type(s, source_ip, source_port, stun_host=None, stun_port=3478): port = stun_port log.debug("Do Test1") resp = False - typ = StunServertNotAccessible - try: - if stun_host: + + if stun_host: + ret = stun_test(s, stun_host, port, source_ip, source_port) + resp = ret['Resp'] + else: + for stun_host in stun_servers_list: + log.debug('Trying STUN host: %s' % stun_host) ret = stun_test(s, stun_host, port, source_ip, source_port) resp = ret['Resp'] + if resp: + break + if not resp: + return Blocked, ret + log.debug("Result: %s" % ret) + exIP = ret['ExternalIP'] + exPort = ret['ExternalPort'] + changedIP = ret['ChangedIP'] + changedPort = ret['ChangedPort'] + if changedIP is None or changedPort is None: + typ = NotEnoughEvidence + elif ret['ExternalIP'] == source_ip: + changeRequest = ''.join([ChangeRequest, '0004', "00000006"]) + ret = stun_test(s, stun_host, port, source_ip, source_port, + changeRequest) + if ret['Resp']: + typ = OpenInternet else: - for stun_host in stun_servers_list: - log.debug('Trying STUN host: %s' % stun_host) - ret = stun_test(s, stun_host, port, source_ip, source_port) - resp = ret['Resp'] - if resp: - break - if not resp: - return Blocked, ret + typ = SymmetricUDPFirewall + else: + changeRequest = ''.join([ChangeRequest, '0004', "00000006"]) + log.debug("Do Test2") + ret = stun_test(s, stun_host, port, source_ip, source_port, + changeRequest) + log.debug("=========") log.debug("Result: %s" % ret) - exIP = ret['ExternalIP'] - exPort = ret['ExternalPort'] - changedIP = ret['ChangedIP'] - changedPort = ret['ChangedPort'] - if ret['ExternalIP'] == source_ip: - changeRequest = ''.join([ChangeRequest, '0004', "00000006"]) - ret = stun_test(s, stun_host, port, source_ip, source_port, - changeRequest) - if ret['Resp']: - typ = OpenInternet - else: - typ = SymmetricUDPFirewall + if ret['Resp']: + typ = FullCone else: - changeRequest = ''.join([ChangeRequest, '0004', "00000006"]) - log.debug("Do Test2") - ret = stun_test(s, stun_host, port, source_ip, source_port, - changeRequest) + log.debug("Do Test1") + ret = stun_test(s, changedIP, changedPort, source_ip, source_port) log.debug("Result: %s" % ret) - if ret['Resp']: - typ = FullCone + if not ret['Resp']: + typ = ChangedAddressError else: - log.debug("Do Test1") - ret = stun_test(s, changedIP, changedPort, source_ip, source_port) - log.debug("Result: %s" % ret) - if not ret['Resp']: - typ = ChangedAddressError - else: - if exIP == ret['ExternalIP'] and exPort == ret['ExternalPort']: - changePortRequest = ''.join([ChangeRequest, '0004', - "00000002"]) - log.debug("Do Test3") - ret = stun_test(s, changedIP, port, source_ip, source_port, - changePortRequest) - log.debug("Result: %s" % ret) - if ret['Resp'] == True: - typ = RestricNAT - else: - typ = RestricPortNAT + if exIP == ret['ExternalIP'] and exPort == ret['ExternalPort']: + changePortRequest = ''.join([ChangeRequest, '0004', + "00000002"]) + log.debug("Do Test3") + ret = stun_test(s, changedIP, port, source_ip, source_port, + changePortRequest) + log.debug("Result: %s" % ret) + if ret['Resp']: + typ = RestricNAT else: - typ = SymmetricNAT - except BindErrorResponseException: - pass + typ = RestricPortNAT + else: + typ = SymmetricNAT return typ, ret