#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (c) 2014 clowwindy # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import socket import struct import common _request_count = 1 # rfc1035 # 1 1 1 1 1 1 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | ID | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # |QR| Opcode |AA|TC|RD|RA| Z | RCODE | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | QDCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | ANCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | NSCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | ARCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ QTYPE_ANY = 255 QTYPE_A = 1 QTYPE_AAAA = 28 QCLASS_IN = 1 def convert_address(address): address = address.strip('.') labels = address.split('.') results = [] for label in labels: l = len(label) if l > 63: return None results.append(chr(l)) results.append(label) results.append('\0') return ''.join(results) 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) _request_count += 1 return header + addr + qtype_qclass 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 def resolve(address, callback): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.SOL_UDP) req = pack_request(address) if req is None: # TODO return sock.sendto(req, ('8.8.8.8', 53)) res, addr = sock.recvfrom(1024) parsed_res = unpack_response(res) callback(parsed_res) def test(): def _callback(address): print address resolve('www.google.com', _callback) if __name__ == '__main__': test()