|
|
@ -29,6 +29,20 @@ import common |
|
|
|
_request_count = 1 |
|
|
|
|
|
|
|
# rfc1035 |
|
|
|
# format |
|
|
|
# +---------------------+ |
|
|
|
# | Header | |
|
|
|
# +---------------------+ |
|
|
|
# | Question | the question for the name server |
|
|
|
# +---------------------+ |
|
|
|
# | Answer | RRs answering the question |
|
|
|
# +---------------------+ |
|
|
|
# | Authority | RRs pointing toward an authority |
|
|
|
# +---------------------+ |
|
|
|
# | Additional | RRs holding additional information |
|
|
|
# +---------------------+ |
|
|
|
# |
|
|
|
# header |
|
|
|
# 1 1 1 1 1 1 |
|
|
|
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
|
|
|
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|
|
@ -48,10 +62,22 @@ _request_count = 1 |
|
|
|
QTYPE_ANY = 255 |
|
|
|
QTYPE_A = 1 |
|
|
|
QTYPE_AAAA = 28 |
|
|
|
QTYPE_CNAME = 5 |
|
|
|
QCLASS_IN = 1 |
|
|
|
|
|
|
|
|
|
|
|
def convert_address(address): |
|
|
|
def parse_ip(addrtype, data, length, offset): |
|
|
|
if addrtype == QTYPE_A: |
|
|
|
return socket.inet_ntop(socket.AF_INET, data[offset:offset + length]) |
|
|
|
elif addrtype == QTYPE_AAAA: |
|
|
|
return socket.inet_ntop(socket.AF_INET6, data[offset:offset + length]) |
|
|
|
elif addrtype == QTYPE_CNAME: |
|
|
|
return parse_name(data, offset, length)[1] |
|
|
|
else: |
|
|
|
return data |
|
|
|
|
|
|
|
|
|
|
|
def pack_address(address): |
|
|
|
address = address.strip('.') |
|
|
|
labels = address.split('.') |
|
|
|
results = [] |
|
|
@ -68,32 +94,137 @@ def convert_address(address): |
|
|
|
def pack_request(address): |
|
|
|
global _request_count |
|
|
|
header = struct.pack('!HBBHHHH', _request_count, 1, 0, 1, 0, 0, 0) |
|
|
|
addr = convert_address(address) |
|
|
|
qtype_qclass = struct.pack('!HH', QTYPE_A, QCLASS_IN) |
|
|
|
addr = pack_address(address) |
|
|
|
qtype_qclass = struct.pack('!HH', QTYPE_ANY, QCLASS_IN) |
|
|
|
_request_count += 1 |
|
|
|
if _request_count > 65535: |
|
|
|
_request_count = 1 |
|
|
|
return header + addr + qtype_qclass |
|
|
|
|
|
|
|
|
|
|
|
def parse_name(data, offset, length=512): |
|
|
|
p = offset |
|
|
|
if (ord(data[offset]) & (128 + 64)) == (128 + 64): |
|
|
|
# pointer |
|
|
|
pointer = struct.unpack('!H', data[offset:offset + 2])[0] |
|
|
|
pointer = pointer & 0x3FFF |
|
|
|
if pointer == offset: |
|
|
|
return (0, None) |
|
|
|
return (2, parse_name(data, pointer)) |
|
|
|
else: |
|
|
|
labels = [] |
|
|
|
l = ord(data[p]) |
|
|
|
while l > 0 and p < offset + length: |
|
|
|
if (l & (128 + 64)) == (128 + 64): |
|
|
|
# pointer |
|
|
|
pointer = struct.unpack('!H', data[p:p + 2])[0] |
|
|
|
pointer = pointer & 0x3FFF |
|
|
|
# if pointer == offset: |
|
|
|
# return (0, None) |
|
|
|
r = parse_name(data, pointer) |
|
|
|
labels.append(r[1]) |
|
|
|
p += 2 |
|
|
|
# pointer is the end |
|
|
|
return (p - offset + 1, '.'.join(labels)) |
|
|
|
else: |
|
|
|
labels.append(data[p + 1:p + 1 + l]) |
|
|
|
p += 1 + l |
|
|
|
l = ord(data[p]) |
|
|
|
return (p - offset + 1, '.'.join(labels)) |
|
|
|
|
|
|
|
|
|
|
|
# rfc1035 |
|
|
|
# record |
|
|
|
# 1 1 1 1 1 1 |
|
|
|
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
|
|
|
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|
|
|
# | | |
|
|
|
# / / |
|
|
|
# / NAME / |
|
|
|
# | | |
|
|
|
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|
|
|
# | TYPE | |
|
|
|
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|
|
|
# | CLASS | |
|
|
|
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|
|
|
# | TTL | |
|
|
|
# | | |
|
|
|
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|
|
|
# | RDLENGTH | |
|
|
|
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| |
|
|
|
# / RDATA / |
|
|
|
# / / |
|
|
|
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|
|
|
def parse_record(data, offset, question=False): |
|
|
|
len, name = parse_name(data, offset) |
|
|
|
# TODO |
|
|
|
assert len |
|
|
|
if not question: |
|
|
|
record_type, record_class, record_ttl, record_rdlength = struct.unpack( |
|
|
|
'!HHiH', data[offset + len:offset + len + 10] |
|
|
|
) |
|
|
|
ip = parse_ip(record_type, data, record_rdlength, offset + len + 10) |
|
|
|
return len + 10 + record_rdlength, \ |
|
|
|
(name, ip, record_type, record_class, record_ttl) |
|
|
|
else: |
|
|
|
record_type, record_class = struct.unpack( |
|
|
|
'!HH', data[offset + len:offset + len + 4] |
|
|
|
) |
|
|
|
return len + 4, (name, None, record_type, record_class, None, None) |
|
|
|
|
|
|
|
|
|
|
|
def unpack_response(data): |
|
|
|
if len(data) > 12: |
|
|
|
header = struct.unpack('!HBBHHHH', data[:12]) |
|
|
|
res_id = header[0] |
|
|
|
res_qr = header[1] & 128 |
|
|
|
res_tc = header[1] & 2 |
|
|
|
res_ra = header[2] & 128 |
|
|
|
res_rcode = header[2] & 15 |
|
|
|
res_qdcount = header[3] |
|
|
|
res_ancount = header[4] |
|
|
|
res_nscount = header[5] |
|
|
|
res_arcount = header[6] |
|
|
|
print header |
|
|
|
# TODO detect address type |
|
|
|
print data |
|
|
|
print data.encode('hex') |
|
|
|
return data |
|
|
|
try: |
|
|
|
if len(data) >= 12: |
|
|
|
header = struct.unpack('!HBBHHHH', data[:12]) |
|
|
|
res_id = header[0] |
|
|
|
res_qr = header[1] & 128 |
|
|
|
res_tc = header[1] & 2 |
|
|
|
res_ra = header[2] & 128 |
|
|
|
res_rcode = header[2] & 15 |
|
|
|
# TODO check tc and rcode |
|
|
|
assert res_tc == 0 |
|
|
|
assert res_rcode == 0 |
|
|
|
res_qdcount = header[3] |
|
|
|
res_ancount = header[4] |
|
|
|
res_nscount = header[5] |
|
|
|
res_arcount = header[6] |
|
|
|
qds = [] |
|
|
|
ans = [] |
|
|
|
nss = [] |
|
|
|
ars = [] |
|
|
|
offset = 12 |
|
|
|
for i in xrange(0, res_qdcount): |
|
|
|
l, r = parse_record(data, offset, True) |
|
|
|
offset += l |
|
|
|
if r: |
|
|
|
qds.append(r) |
|
|
|
for i in xrange(0, res_ancount): |
|
|
|
l, r = parse_record(data, offset) |
|
|
|
offset += l |
|
|
|
if r: |
|
|
|
ans.append(r) |
|
|
|
for i in xrange(0, res_nscount): |
|
|
|
l, r = parse_record(data, offset) |
|
|
|
offset += l |
|
|
|
if r: |
|
|
|
nss.append(r) |
|
|
|
for i in xrange(0, res_arcount): |
|
|
|
l, r = parse_record(data, offset) |
|
|
|
offset += l |
|
|
|
if r: |
|
|
|
ars.append(r) |
|
|
|
|
|
|
|
return ans |
|
|
|
|
|
|
|
except Exception as e: |
|
|
|
import traceback |
|
|
|
traceback.print_exc() |
|
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
def resolve(address, callback): |
|
|
|
# TODO async |
|
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.SOL_UDP) |
|
|
|
req = pack_request(address) |
|
|
|
if req is None: |
|
|
@ -109,7 +240,13 @@ def test(): |
|
|
|
def _callback(address): |
|
|
|
print address |
|
|
|
|
|
|
|
resolve('www.twitter.com', _callback) |
|
|
|
resolve('www.google.com', _callback) |
|
|
|
resolve('ipv6.google.com', _callback) |
|
|
|
resolve('ipv6.l.google.com', _callback) |
|
|
|
resolve('www.baidu.com', _callback) |
|
|
|
resolve('www.a.shifen.com', _callback) |
|
|
|
resolve('m.baidu.jp', _callback) |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|