#!/usr/bin/python from socket import * from dpkt.dns import * import dpkt.ethernet, struct, time, sys EtherType_IPv6 = 0x86DD Protocol_TCP = 6 Protocol_UDP = 17 Protocol_IPv6_Frag = 44 Protocol_ICMPv6 = 58 ICMPv6Type_PTB = 2 DNSFlags_TC = 2 try: d = socket(AF_INET6, SOCK_DGRAM) # To prevent ICMPv6 d.bind(('', 53)) # dst port unreachable except: pass s = socket(AF_PACKET, SOCK_RAW, EtherType_IPv6) # Open raw IPv6 socket s.bind(( 'eth0' if len(sys.argv) == 1 else sys.argv[1] # On eth0 or the given , EtherType_IPv6)) # interface. frag_id = int(time.time()) # Fragment id while True: frame = s.recv(1500 + 14) # max packet size + # ethernet header size eth = dpkt.ethernet.Ethernet(frame) # We asked for IPv6! assert eth.type == EtherType_IPv6, "Non IPv6!" # Expect IPv6! ipv6 = eth.data if ipv6.nxt != Protocol_UDP: continue # Skip when not UDP udp = ipv6.data if udp.dport != 53: continue # Skip when not DNS dns = DNS(udp.data) if dns.qr != DNS_Q \ or dns.opcode != DNS_QUERY \ or len(dns.qd) != 1: continue # Skip when not a query dns.op |= DNS_AA # Turn query packet dns.rcode = DNS_RCODE_NOERR # into a response dns.qr = DNS_R # packet. dns.op &= ~(DNS_AD|DNS_CD) dns.ar = list() # No OPT leftovers... qname = dns.qd[0].name qname_l = qname.lower().split('.') qcls = dns.qd[0].cls qtype = dns.qd[0].type pkt_sz = [int(l[1:]) for l in qname_l if len(l) > 1 and l[0] == 'p' and l[1:].isdigit()] pkt_sz = pkt_sz[0] if pkt_sz else None frag_sz = [int(l[1:]) for l in qname_l if len(l) > 1 and l[0] == 'f' and l[1:].isdigit()] frag_sz = frag_sz[0] if frag_sz else None if qtype == DNS_AAAA: dns.an.append(DNS.RR( cls = qcls, type = qtype, name = qname , ip6 = ipv6.dst if qname_l[0] == 'ns' else ipv6.src , ttl = 30 )) elif qtype == DNS_NS: dns.an.append(DNS.RR( cls = qcls, type = qtype, name = qname , nsname = 'ns.' + qname , ttl = 30 )) dns.ar.append(DNS.RR( cls = qcls, type = DNS_AAAA , name = 'ns.' + qname, ip6 = ipv6.dst , ttl = 30 )) elif qtype == DNS_SOA: dns.an.append(DNS.RR( cls = qcls, type = qtype, name = qname , mname = 'ns.' + qname , rname = 'willem.nlnetlabs.nl' , serial = int(time.time()) , refresh = 40 , retry = 10 , expire = 100 , minimum = 5 , ttl = 30 )) elif qtype == DNS_TXT: dns.an.append(DNS.RR( cls = qcls, type = qtype, name = qname , text = [ ( 'received at %s from [%s].%d' + ' for [%s].%d' ) % ( time.strftime('%Y-%m-%d %H:%M:%S.') + ('%.2f' % time.time())[-2:] + time.strftime(' %Z') , inet_ntop(AF_INET6, ipv6.src) , udp.sport , inet_ntop(AF_INET6, ipv6.dst) , udp.dport )] , ttl = 30 )) for once in (1,): if pkt_sz is None: break if pkt_sz <= 0 or pkt_sz > 65535: break cur_sz = len(str(dns)) + 48 while cur_sz < pkt_sz: if pkt_sz - cur_sz <= 41: txt = [ 'padding.....................'[ :(pkt_sz - cur_sz - 13)]] else: txt = [ '%5d more bytes' % (pkt_sz - cur_sz) ] dns.an.append(DNS.RR( cls = qcls, type = qtype , name = qname , text = txt , ttl = 30 )) cur_sz = len(str(dns)) + 48 elif qname_l[0] == 'ns': dns.ns.append(DNS.RR( cls = qcls, type = DNS_SOA, name = qname[3:] , mname = qname , rname = 'willem.nlnetlabs.nl' , serial = int(time.time()) , refresh = 40 , retry = 10 , expire = 100 , minimum = 5 , ttl = 30 )) ## -------------------------------------------------------------------- log_xtra = '' eth.src, eth.dst = eth.dst, eth.src udp.data = str(dns) udp.ulen = len(udp.data) + 8 # Including header size udp.sport, udp.dport = udp.dport, udp.sport # Swap ports udp.sum = 0 # Reset to recalculate ipv6.data = udp ipv6.nxt = Protocol_UDP ipv6.plen = len(ipv6.data) # Reuse header ipv6.src, ipv6.dst = ipv6.dst, ipv6.src # swap addresses str(ipv6) # Set UDP checksum udpdata = str(ipv6.data) # UDP data + checksum for once in (1,): if frag_sz is not None: if frag_sz >= 56 and frag_sz <= 1496 \ and frag_sz % 8 == 0: frag_sz -= 48 break if '2frags' in qname_l: frag_sz = (len(udpdata) + 15) / 16 * 8 if frag_sz >= 8 and frag_sz <= 1448: break if len(udpdata) > 1460: frag_sz = 1448 elif '1frag' in qname_l and len(udpdata) <= 1452: frag_sz = len(udpdata) else: frag_sz = 0 if frag_sz: offset = 0 while True: more = int(offset + frag_sz < len(udpdata)) frag = chr(Protocol_UDP) \ + '\x00' \ + struct.pack('!H', offset + more)\ + struct.pack('I', frag_id)[:4] ipv6.data = frag + udpdata[offset:offset+frag_sz] ipv6.plen = len(ipv6.data) ipv6.nxt = Protocol_IPv6_Frag log_xtra += ' (%d..%d)' % ( offset , offset + ipv6.plen - 8) if not more: break eth.data = ipv6 s.send(str(eth)) offset += frag_sz frag_id += 1 frag_id %= 1 << 32 eth.data = ipv6 s.send(str(eth)) dns = DNS(udp.data) print "%s Sent %s. TYPE%.3d to %s%s" % ( time.strftime('%Y-%m-%d %H:%M:%S.')+ ('%.2f' % time.time())[-2:], qname, qtype, inet_ntop(AF_INET6, ipv6.dst), log_xtra )