def __init__(ctx): ctx.question = untwisted.oneMany() ctx.answer = untwisted.oneMany() ctx.authority = untwisted.oneMany() ctx.additional = untwisted.oneMany()
def request(server, messageMethod=binding): try: transport = yield udp.connect(server, 'stun')() # Avoid error: service/proto not found except socket.error: transport = yield udp.connect(server, 3478)() request = message() request.messageMethod = messageMethod request.messageClass = 0x0000 request.magicCookie = '\x21\x12\xa4\x42' request.transactionId = os.urandom(12) transport.write(str(request)) recv = yield transport.recv() response = message() response.messageMethod = ord(recv[0]) << 6 & 0xf800 | ord(recv[1]) >> 1 & 0xf0 | ord(recv[1]) & 0xf response.messageClass = ord(recv[0]) << 8 & 0x100 | ord(recv[1]) & 0x10 response.magicCookie = recv[4:8] response.transactionId = recv[8:20] recv = recv[20:] while recv: type = ord(recv[0]) << 8 | ord(recv[1]) length = ord(recv[2]) << 8 | ord(recv[3]) itm = attribute(recv[4:4 + length]) # Each STUN attribute MUST end on a 32-bit boundary recv = recv[4 + length - length % -4:] if type in (MAPPED_ADDRESS, ALTERNATE_SERVER): # 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |0 0 0 0 0 0 0 0| Family | Port | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # | Address (32 bits or 128 bits) | # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ itm.family = ord(itm.value[1]) itm.port = ord(itm.value[2]) << 8 | ord(itm.value[3]) if IPv4 == itm.family: itm.address = socket.inet_ntop(socket.AF_INET, itm.value[4:]) elif IPv6 == itm.family: itm.address = socket.inet_ntop(socket.AF_INET6, itm.value[4:]) elif ERROR_CODE == type: # 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Reserved, should be 0 |Class| Number | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Reason Phrase (variable) ... # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ itm['class'] = ord(itm.value[2]) itm.number = ord(itm.value[3]) itm.reasonPhrase = itm.value[4:] elif UNKNOWN_ATTRIBUTES == type: # 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Attribute 1 Type | Attribute 2 Type | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Attribute 3 Type | Attribute 4 Type ... # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ itm.attributeType = untwisted.oneMany(*(ord(itm.value[offset]) << 8 | ord(itm.value[offset + 1]) for offset in range(0, len(itm.value), 2))) elif XOR_MAPPED_ADDRESS == type: # 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |0 0 0 0 0 0 0 0| Family | X-Port | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # | X-Address (32 bits or 128 bits) | # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ itm.family = ord(itm.value[1]) itm.port = (ord(itm.value[2]) ^ ord(response.magicCookie[0])) << 8 | ord(itm.value[3]) ^ ord(response.magicCookie[1]) if IPv4 == itm.family: itm.address = socket.inet_ntop(socket.AF_INET, ''.join(chr(ord(address) ^ ord(magicCookie)) for address, magicCookie in zip(itm.value[4:], response.magicCookie))) elif IPv6 == itm.family: itm.address = socket.inet_ntop(socket.AF_INET6, ''.join(chr(ord(address) ^ ord(magicCookie)) for address, magicCookie in zip(itm.value[4:], response.magicCookie + response.transactionId))) response.attribute.append(type, itm) if 0x0100 != response.messageClass: raise response #return ... raise StopIteration(response)
def request(server, messageMethod=binding): try: transport = yield udp.connect(server, 'stun')() # Avoid error: service/proto not found except socket.error: transport = yield udp.connect(server, 3478)() request = message() request.messageMethod = messageMethod request.messageClass = 0x0000 request.magicCookie = '\x21\x12\xa4\x42' request.transactionId = os.urandom(12) transport.write(str(request)) recv = yield transport.recv() response = message() response.messageMethod = ord(recv[0]) << 6 & 0xf800 | ord( recv[1]) >> 1 & 0xf0 | ord(recv[1]) & 0xf response.messageClass = ord(recv[0]) << 8 & 0x100 | ord(recv[1]) & 0x10 response.magicCookie = recv[4:8] response.transactionId = recv[8:20] recv = recv[20:] while recv: type = ord(recv[0]) << 8 | ord(recv[1]) length = ord(recv[2]) << 8 | ord(recv[3]) itm = attribute(recv[4:4 + length]) # Each STUN attribute MUST end on a 32-bit boundary recv = recv[4 + length - length % -4:] if type in (MAPPED_ADDRESS, ALTERNATE_SERVER): # 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |0 0 0 0 0 0 0 0| Family | Port | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # | Address (32 bits or 128 bits) | # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ itm.family = ord(itm.value[1]) itm.port = ord(itm.value[2]) << 8 | ord(itm.value[3]) if IPv4 == itm.family: itm.address = socket.inet_ntop(socket.AF_INET, itm.value[4:]) elif IPv6 == itm.family: itm.address = socket.inet_ntop(socket.AF_INET6, itm.value[4:]) elif ERROR_CODE == type: # 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Reserved, should be 0 |Class| Number | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Reason Phrase (variable) ... # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ itm['class'] = ord(itm.value[2]) itm.number = ord(itm.value[3]) itm.reasonPhrase = itm.value[4:] elif UNKNOWN_ATTRIBUTES == type: # 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Attribute 1 Type | Attribute 2 Type | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Attribute 3 Type | Attribute 4 Type ... # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ itm.attributeType = untwisted.oneMany( *(ord(itm.value[offset]) << 8 | ord(itm.value[offset + 1]) for offset in range(0, len(itm.value), 2))) elif XOR_MAPPED_ADDRESS == type: # 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |0 0 0 0 0 0 0 0| Family | X-Port | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # | X-Address (32 bits or 128 bits) | # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ itm.family = ord(itm.value[1]) itm.port = (ord(itm.value[2]) ^ ord(response.magicCookie[0])) << 8 | ord( itm.value[3]) ^ ord(response.magicCookie[1]) if IPv4 == itm.family: itm.address = socket.inet_ntop( socket.AF_INET, ''.join( chr(ord(address) ^ ord(magicCookie)) for address, magicCookie in zip( itm.value[4:], response.magicCookie))) elif IPv6 == itm.family: itm.address = socket.inet_ntop( socket.AF_INET6, ''.join( chr(ord(address) ^ ord(magicCookie)) for address, magicCookie in zip( itm.value[4:], response.magicCookie + response.transactionId))) response.attribute.append(type, itm) if 0x0100 != response.messageClass: raise response #return ... raise StopIteration(response)