#include /* udp(7) */ /* TODO: random XID */ #include #include #include /* poll(2) */ #include /* socket(2) */ #include #include /* close(2) */ #include /* perror(3) */ #include /* open(2) */ #include #include /* errno(3) */ #include /* byteorder(3) */ #include /* strlen(3) */ #include "domain2name.c" #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MAXDOMAIN 255 #define QUERYDOMAIN "http://sijanec.eu/link?r=.sijanec.eu." #define EXPECTEDA 93.103.235.126 #define HELP "find recursive DNS resolvers on IPv4 networks\n" \ "%s [-h] [-d domain] [-a ipv4] network1 [network2 [network3 ...]]\n" \ " -a Specify the IPv4 of the -d domain to be used instead of getaddrinfo(3).\n" \ " -d Specify the domain name to be used in queries that has a single A record.\n" \ " -h Show this help and exit.\n" \ "Networks are specified as domain names or IPv4 addresses followed by a forward slash\n" \ "and the netmask in decimal (0-32) or dot notation. For example: example.com/32. \n" \ "When scanning the Internet please make sure that you specify your own domain instead\n" \ "of the default one (f@sijanec.eu-http://sijanec.eu/link?r=.sijanec.eu). Make sure it\n" \ "somehow contains your contact information so the network/server administrator when\n" \ "looking at the logs will be able to contact you.\n" /* DNS PACKET: HEADER (12 bytes) QUESTION ANSWER AUTHORITY ADDITIONAL DEFINITIONS: (those appear somewhere in the packet, packet does not start with definitions!) LABLEN 8 bits: first two bits zero, then 6 bits length of label POINTER 8 bits: first two bits one, then 6 bits as offset from first byte of packet STRING a single byte for defining length (0-256 - all eight bits) and then string of chars DOMAIN i.) one or more LABLEN followed by ASCII label (no dots) end with either LABLEN 0 or a POINTER that points to some LABLEN somewhere else in the packet -OR-: ii.) a POINTER that points to some LABLEN somewhere else in the packet HEADER: XID 16 bits: random string to be matched in response to prevent cache poisoning / QR 1 bit : what type is this packet? 0 query 1 response | OPCODE 4 bits: type of query 0 std 1 invrs 2 srvst 3-15 reserved 1 byte AA 1 bit : is response authoritative? 0 no 1 yes | TC 1 bit : was response truncated? 0 no 1 yes \ RD 1 bit : does query desire recursion? 0 no 1 yes / RA 1 bit : does response server recurse? 0 no 1 yes 1 byte Z 3 bits: reserved for future 0 only option \ RCODE 4 bits: error condition 0 ok 1 fmter 2 srvfa 3 nxdom 4 N/I 5 forbidden QDCOUNT 16 bits: number of questions ANCOUNT 16 bits: number of answers NSCOUNT 16 bits: authority section (where to ask for actual response - NS RECORDS) ARCOUNT 16 bits: additional section (glue records) QUESTION: QNAME DOMAIN QTYPE 16 bits: 1 A 2 NS 5 CNAME 6 SOA 10 NULL 12 PTR 13 HINFO 15 MX 16 TXT ... QCLASS 16 bits: 1 INTERNET 2 CSNET (obsolete) 3 CHAOS 4 HESIOD 255 ANY ... ANSWER: NAME DOMAIN TYPE same as QTYPE CLASS same description as QCLASS, except class 255 ANY is not allowed here RDLEN 16 bits: length of RDATA field - this is a number 0-65536, no two zero bits RDATA A: 4 bytes IP address NS: DOMAIN CNAME: DOMAIN SOA: NAME-ns1 NAME-email 32b-serial 32b-refresh 32b-retry 32b-expire 32b-nxdomainttl NULL: any data up to RDLEN PTR: DOMAIN HINFO: STRING-CPU, STRING-OS MX: 16 bit preference, NAME-like domain TXT: one or more STRING */ enum qr { Query, Response }; enum opcode { Query, Iquery, Stauts }; enum rcode { Success, Format_error, Servfail, Nxdomain, Ni, Forbidden }; enum type { A = 1, Ns, Md, Mf, Cname, Soa, Mb, Mg, Mr, Null, Wks, Ptr, Hinfo, Minfo, Mx, Txt } enum class { In = 1, Cs, Ch, He, Any = 255 } struct header { uint16_t xid; enum qr qr : 1; enum opcode opcode : 4; unsigned int aa : 1; unsigned int tc : 1; unsigned int rd : 1; unsigned int ra : 1; unsigned int z : 3; enum rcode rcode : 4; uint16_t qdcount; uint16_t ancount; uint16_t nscount; uint16_t arcount; }; struct question { char * qname; enum qtype qtype : 16; enum qclass qclass : 16; }; struct answer { char * name; enum class class : 16; uint16_t rdlen; char * rdata; } int main (int argc, char ** argv) { int r = 0; while (t) { switch (getopt(argc, argv, ":a:d:h")) { /* TODO */ case 'h': printf(HELP, argv[0]); r = 0; goto r; case -1: d = optarg; if (!d || !*d) { fprintf(stderr, "specify the domain :: " HELP, argv[0]); r = 4; goto r; } if (strlen(d) > MAXDOMAIN) { fprintf(stderr, "domain is too long - max "#MAXDOMAIN".\n"); r = 5; goto r; } t = 0; break; case '?': fprintf(stderr, "unknown option :: " HELP, argv[0]); if (db != -1) close(db); r = 6; goto r; case ':': fprintf(stderr, "missing option argument :: " HELP, argv[0]); r = 7; goto r; } } r: if (s != -1) if (close(s)) perror("close(s)"); } return r; } int handle () { char buf[512]; /* we don't support EDNS0 here, so max DNS packet is 512 bytes */ recvfrom(); } int query () { sendto(); } int erase (char * dbfn, int dbfd, char * db) { }