File information | |||
---|---|---|---|
Filename: | dns.c | Uploaded: | Sat, 6th Mar 2010 21:58:28 |
Size (bytes): | 20.8 KiB | md5 checksum: | fe80ded8a7ad67018f1e81fe7e997625 |
Uploader | doug | Download: | dns.c |
Description: |
Simple DNS query implementation. It's not completed to what I want it to do once I've completely finished it, but it is working properly now. Mail me if you spot anything error wise. I'm already aware of certain insecurities in the code. This is just to show how it works at the moment. Updated 2009-08-02: Added switches rcCtTh and added ability to do things like reverse lookups. |
/* * author: kay ~ irc.nullnetwork.net * date: 2006-08-31 * * simple DNS query implementation. it's not completed to what i want it to * do once i've completely finished it, but it is working properly now. mail * me if you spot anything error wise. i'm already aware of certain * insecurities in the code. this is just to show how it works at the moment. * * Updated 2009-08-02: Added switches rcCtTh and added ability to do things like * reverse lookups. * * Example usage: * ./dns nullnetwork.net * ./dns -r 89.200.138.209 * ./dns -t 12 -C 1 209.138.200.89.in-addr.arpa * ./dns -T PTR -C IN 209.138.200.89.in-addr.arpa */ #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <stdio.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <getopt.h> #include <unistd.h> #define PORT 53 #define NS "208.67.222.222" #define BUFLEN 511 #define QTYPE_A 1 #define QTYPE_NS 2 #define QTYPE_MD 3 #define QTYPE_MF 4 #define QTYPE_CNAME 5 #define QTYPE_SOA 6 #define QTYPE_MB 7 #define QTYPE_MG 8 #define QTYPE_MR 9 #define QTYPE_NULL 10 #define QTYPE_WKS 11 #define QTYPE_PTR 12 #define QTYPE_HINFO 13 #define QTYPE_MINFO 14 #define QTYPE_MX 15 #define QTYPE_TXT 16 #define QTYPE_AXFR 252 #define QTYPE_MAILB 253 #define QTYPE_MAILA 254 #define QTYPE_OTHER 255 #define QCLASS_IN 1 #define QCLASS_CS 2 #define QCLASS_CH 3 #define QCLASS_HS 4 #define QCLASS_OTHER 255 #define PGREEN(x) fwrite("\x1B[01;32m" x "\x1B[00m\n", 1, 14+sizeof(x), stdout); #define PRED(x) fwrite("\x1B[01;31m" x "\x1B[00m\n", 1, 14+sizeof(x), stdout); #define PGREY(x) fwrite("\x1B[01;30m" x "\x1B[00m\n", 1, 14+sizeof(x), stdout); #define PWHITE(x) fwrite("\x1B[01;29m" x "\x1B[00m\n", 1, 14+sizeof(x), stdout); #define PYELLOW(x) fwrite("\x1B[01;33m" x "\x1B[00m\n", 1, 14+sizeof(x), stdout); #define PBLUE(x) fwrite("\x1B[01;34m" x "\x1B[00m\n", 1, 14+sizeof(x), stdout); #define PPURPLE(x) fwrite("\x1B[01;35m" x "\x1B[00m\n", 1, 14+sizeof(x), stdout); struct dns_header { short int id; // 2 bytes short int flags; /* bit n | meaning 0 | QR: query responce 1-4 | Opcode: query opcode (0 - standard query, 1 - inverse query, 2 = server status req., 3-15 reserved) 5 | AA: authoritive answer 6 | TC: Truncation 7 | RD: Recursion desired 8 | RA: recursion available 9-11 | Z: Reserved 12-15 | RCODE: responce code (0 - no error, 1 - format error, 2 - server failure, 3 - name error, 4 - not implemented, 5 - refused, 6-16 - reserved) */ short int qdcount; short int ancount; short int nscount; short int arcount; }; struct dns_question { char * qname; // query name short int qtype; short int qclass; }; struct dns_resource { char * name; // if name & 0xC0 == 0xC0 then its a pointer to name & 0x3FFF short int type; short int class; unsigned int ttl; unsigned short int rdlength; char * rdata; // note this is variable length, given by rdlength }; typedef struct dns_header dns_header; typedef struct dns_question dns_question; typedef struct dns_resource dns_answer; typedef struct dns_resource dns_authority; typedef struct dns_resource dns_additional; #define SET_QR(x) x|=0x8 #define UNSET_QR(x) x~=0x8 struct rdata_mx { short int preference; char * exchange; }; struct rdata_soa { char * mname; char * rname; unsigned int serial; int refresh; int retry; int expire; unsigned int minimum; }; int sendallto(int fd, char * data, int datalen, struct sockaddr_in * dest) { int sendlen = datalen, num_of_bytes; while (sendlen > 0) { if ((num_of_bytes = sendto(fd, data + (datalen - sendlen), sendlen, 0, (struct sockaddr *)dest, sizeof(struct sockaddr))) == -1) { return -1; } sendlen -= num_of_bytes; } return 0; } int make_query(int fd, short int flags, struct sockaddr_in dest_addr, dns_question query) { dns_header qhead; char buf[BUFLEN]; int buflen = 0; int octetlen = 0; char * dotptr = NULL; char * hostptr = query.qname; memset(buf, 0, BUFLEN); qhead.id = htons(0x1337); qhead.flags = htons(flags); qhead.qdcount = htons(0x0001); qhead.ancount = 0; qhead.nscount = 0; qhead.arcount = 0; buflen = sizeof(qhead); memcpy(buf, (void*)&qhead, buflen); do { dotptr = strchr(hostptr, '.'); if (!dotptr) octetlen = strchr(hostptr,'\0') - hostptr; else octetlen = dotptr - hostptr; buf[buflen++] = octetlen; strncpy(buf + buflen, hostptr, octetlen); buflen += octetlen; hostptr = dotptr + 1; } while(dotptr); memset(buf + buflen, 0, 2); buflen += 2; //qtype query.qtype = query.qtype; query.qclass = query.qclass; memcpy((void*)buf + buflen, (void*)&(query.qtype), sizeof(short)); buflen += 2; //qclass memcpy((void*)buf + buflen, (void*)&(query.qclass), sizeof(short)); buflen += 2; sendallto(fd, buf, buflen, &dest_addr); return 0; } char * translate_qtype(int qt) { switch(qt) { case QTYPE_A: return "A"; case QTYPE_NS: return "NS"; case QTYPE_MD: return "MD"; case QTYPE_MF: return "MF"; case QTYPE_CNAME: return "CNAME"; case QTYPE_SOA: return "SOA"; case QTYPE_MB: return "MB"; case QTYPE_MG: return "MG"; case QTYPE_MR: return "MR"; case QTYPE_NULL: return "NULL"; case QTYPE_WKS: return "WKS"; case QTYPE_PTR: return "PTR"; case QTYPE_HINFO: return "HINFO"; case QTYPE_MINFO: return "MINFO"; case QTYPE_MX: return "MX"; case QTYPE_TXT: return "TXT"; case QTYPE_AXFR: return "AXFR"; case QTYPE_MAILB: return "MAILB"; case QTYPE_MAILA: return "MAILA"; case QTYPE_OTHER: return "*"; default: return NULL; } } int translate_from_qtype(char * type) { if (strcmp(type, "A") == 0) { return QTYPE_A; } else if (strcmp(type, "NS") == 0) { return QTYPE_NS; } else if (strcmp(type, "MD") == 0) { return QTYPE_MD; } else if (strcmp(type, "MF") == 0) { return QTYPE_MF; } else if (strcmp(type, "CNAME") == 0) { return QTYPE_CNAME; } else if (strcmp(type, "SOA") == 0) { return QTYPE_SOA; } else if (strcmp(type, "MB") == 0) { return QTYPE_MB; } else if (strcmp(type, "MG") == 0) { return QTYPE_MG; } else if (strcmp(type, "MR") == 0) { return QTYPE_MR; } else if (strcmp(type, "NULL") == 0) { return QTYPE_NULL; } else if (strcmp(type, "WKS") == 0) { return QTYPE_WKS; } else if (strcmp(type, "PTR") == 0) { return QTYPE_PTR; } else if (strcmp(type, "HINFO") == 0) { return QTYPE_HINFO; } else if (strcmp(type, "MINFO") == 0) { return QTYPE_MINFO; } else if (strcmp(type, "MX") == 0) { return QTYPE_MX; } else if (strcmp(type, "TXT") == 0) { return QTYPE_TXT; } else if (strcmp(type, "AXFR") == 0) { return QTYPE_AXFR; } else if (strcmp(type, "MAILB") == 0) { return QTYPE_MAILB; } else if (strcmp(type, "MAILA") == 0) { return QTYPE_MAILA; } else if (strcmp(type, "*") == 0) { return QTYPE_OTHER; } else { return -1; } } char * translate_qclass(int qc) { switch(qc) { case QCLASS_IN: return "IN"; case QCLASS_CS: return "CS"; case QCLASS_CH: return "CH"; case QCLASS_HS: return "HS"; case QCLASS_OTHER: return "*"; default: return NULL; } } int translate_from_qclass(char * class) { if (strcmp(class, "IN") == 0) { return QCLASS_IN; } else if (strcmp(class, "CS") == 0) { return QCLASS_CS; } else if (strcmp(class, "CH") == 0) { return QCLASS_CH; } else if (strcmp(class, "HS") == 0) { return QCLASS_HS; } else if (strcmp(class, "*") == 0) { return QCLASS_OTHER; } else { return -1; } } char * translate_rcode(int rc) { switch(rc) { case 0: return "No Error"; case 1: return "Format Error"; case 2: return "Server Failure"; case 3: return "Name Error"; case 4: return "Not Implemented"; case 5: return "Refused"; default: return "Reserved"; } } char * translate_opcode(int oc) { switch(oc) { case 0: return "Standard Query"; case 1: return "Inverse Query"; case 2: return "Server Status Request"; default: return "Reserved"; } } // ---[data extraction routines]--- char * get_charstring(char * buf, int * offset) { char * str = (char*)malloc(256); int length; length = buf[(*offset)++]; strncpy(str, buf + (*offset), length); *offset += length; return str; } char * get_name(char * data, int * start) { char * name = (char*)malloc(64); int name_len = 0; short int offset = *start; unsigned short int octetlen = 0; short int ptroffset = 0; int set_start = 1; if(!name) { return NULL; } memset(name, 0, 64); do { if ((data[offset] & 0xC0) == 0xC0) { ptroffset = 0; ptroffset |= (data[offset++] & 0x3F); ptroffset <<= 8; ptroffset |= (data[offset++] & 0xFF); if (set_start) { *start = offset; } offset = ptroffset; set_start = 0; } octetlen = data[offset++]; if (octetlen) { if (name_len) { name[name_len++] = '.'; } strncpy(name + name_len, data + offset, octetlen); name_len += octetlen; offset += octetlen; } } while(octetlen); if (set_start) { *start = offset; } return name; } // polymorphism, where for art thou short int readshortintvalue(char * buf, int * offset) { short int bufint = 0; memcpy((void*)&(bufint), buf + *offset, sizeof(short int)); *offset += sizeof(short int); return ntohs(bufint); } int readintvalue(char * buf, int * offset) { int bufint = 0; memcpy((void*)&(bufint), buf + *offset, sizeof(int)); *offset += sizeof(int); return ntohl(bufint); } // ---[ rdata functions ]--- struct rdata_mx * get_rdata_mx(char * buf, int * offset) { struct rdata_mx * mx = (struct rdata_mx *)calloc(1, sizeof(struct rdata_mx)); if (!mx) { return NULL; } memcpy((void*)&(mx->preference), buf + *offset, sizeof(short)); *offset += sizeof(short); mx->exchange = get_name(buf, offset); return mx; } struct rdata_soa * get_rdata_soa(char * buf, int * offset) { struct rdata_soa * soa = (struct rdata_soa *)calloc(1, sizeof(struct rdata_soa)); if (!soa) { return NULL; } soa->mname = get_name(buf, offset); soa->rname = get_name(buf, offset); soa->serial = readintvalue(buf, offset); soa->refresh = readintvalue(buf, offset); soa->retry = readintvalue(buf, offset); soa->expire = readintvalue(buf, offset); soa->minimum = readintvalue(buf, offset); return soa; } struct dns_resource * get_resource(char * buf, int * start) { struct dns_resource * dns_res = (struct dns_resource *)calloc(1, sizeof(struct dns_resource)); int offset = * start; char * name, * txt; struct in_addr inet_addr; struct rdata_mx * mx; struct rdata_soa * soa; if(!dns_res) { return NULL; } name = get_name(buf, &offset); dns_res->name = name; dns_res->type = readshortintvalue(buf, &offset); dns_res->class = readshortintvalue(buf, &offset); dns_res->ttl = readintvalue(buf, &offset); dns_res->rdlength = readshortintvalue(buf, &offset); printf("dns_res {\n"); printf(" name='%s'\n", dns_res->name); printf(" type=%d (Type: %s)\n", dns_res->type, translate_qtype(dns_res->type)); printf(" class=0x%X (Class: %s)\n", dns_res->class, translate_qclass(dns_res->class)); printf(" ttl=%d seconds\n", dns_res->ttl); printf(" rdlength=0x%X\n", dns_res->rdlength); //dns_ans.rdata switch (dns_res->type) { case QTYPE_MB: case QTYPE_MD: case QTYPE_MF: case QTYPE_MG: case QTYPE_MR: case QTYPE_NS: case QTYPE_PTR: case QTYPE_CNAME: // domain-name name = get_name(buf, &offset); printf(" rdatas='%s' when type is %s\n", name, translate_qtype(dns_res->type)); dns_res->rdata = name; break; case QTYPE_WKS: // break; case QTYPE_A: memcpy((void*)&(inet_addr.s_addr), buf + offset, sizeof(int)); offset += sizeof(int); printf(" rdata=0x%X (%s)\n", inet_addr.s_addr, inet_ntoa(inet_addr)); dns_res->rdata = (char*)inet_addr.s_addr; break; case QTYPE_TXT: txt = get_charstring(buf, &offset); printf(" rdata='%s'\n", txt); break; case QTYPE_SOA: soa = get_rdata_soa(buf, &offset); dns_res->rdata = (char*)soa; printf(" rdata {\n"); printf(" mname='%s'\n", soa->mname); printf(" rname='%s'\n", soa->rname); printf(" serial=%d\n", soa->serial); printf(" refresh=%d seconds\n", soa->refresh); printf(" retry=%d seconds\n", soa->retry); printf(" expire=%d seconds\n", soa->expire); printf(" minimum=%d seconds\n", soa->minimum); printf(" }\n"); break; case QTYPE_NULL: // this is an experimental type, so I'll just ignore it printf(" # rdata ignored due to type being experimental\n"); break; case QTYPE_MX: // preference // exchange (domain-name) mx = get_rdata_mx(buf, &offset); dns_res->rdata = (char*)mx; printf(" rdata {\n"); printf(" preference=%d\n", mx->preference); printf(" exchange='%s'\n", mx->exchange); printf(" }\n"); break; case QTYPE_MINFO: // rmailbx (domain-name) // emailbx (domain-name) break; case QTYPE_HINFO: // cpu (char-string) // os (char-string) break; default: printf("Unknown type\n"); } printf("}\n"); *start = offset; return dns_res; } int read_responce(int fd, struct sockaddr_in dest_addr) { int num_of_bytes = 0; char buf[BUFLEN]; unsigned int buflen = 0; dns_header dns_head; dns_question dns_quest; dns_authority * dns_auth; dns_answer * dns_ans; dns_additional * dns_add; int offset = 0; char * name; int i; unsigned short int qr, opcode, aa, tc, rd, ra, z, rcode; if ((num_of_bytes = recvfrom(fd, buf, BUFLEN, 0, (struct sockaddr *)&dest_addr, &buflen)) == -1) { perror("recvfrom() error"); return 1; } printf("%d bytes received\n", num_of_bytes); memcpy((void*)&dns_head, buf, sizeof(dns_header)); dns_head.id = ntohs(dns_head.id); dns_head.flags = ntohs(dns_head.flags); dns_head.qdcount = ntohs(dns_head.qdcount); dns_head.ancount = ntohs(dns_head.ancount); dns_head.nscount = ntohs(dns_head.nscount); dns_head.arcount = ntohs(dns_head.arcount); qr = (dns_head.flags & 0x8000); opcode = (dns_head.flags & 0x7800) >> 11; aa = (dns_head.flags & 0x0400); tc = (dns_head.flags & 0x0200); rd = (dns_head.flags & 0x0100); ra = (dns_head.flags & 0x0080); z = (dns_head.flags & 0x0070) >> 4; rcode = (dns_head.flags & 0x000F); printf("dns_head {\n"); printf(" id=0x%X\n", dns_head.id); printf(" flags=0x%hX {\n", dns_head.flags); printf(" qr=%u (%s)\n", (qr ? 1 : 0), (qr ? "is a Query Responce" : "is not a Query Responce")); printf(" opcode=0x%X (%s)\n", opcode, translate_opcode(opcode)); printf(" aa=%u (%s)\n", (aa ? 1 : 0), (aa ? "is the Authoritive Server" : "is not the Authoritive Server")); printf(" tc=%u%s\n", (tc ? 1 : 0), (tc ? " ( is Truncated)" : "")); printf(" rd=%u%s\n", (rd ? 1 : 0), (rd ? " (Recursion Desired)" : "")); printf(" ra=%u%s\n", (ra ? 1 : 0), (ra ? " (Recursion available)" : "")); printf(" z=0x%X\n", z); printf(" rcode=0x%X (%s)\n", rcode, translate_rcode(rcode)); printf(" }\n"); printf(" qdcount=%u\n", dns_head.qdcount); printf(" ancount=%u\n", dns_head.ancount); printf(" nscount=%u\n", dns_head.nscount); printf(" arcount=%u\n", dns_head.arcount); printf("}\n"); /* bit n | meaning 0 | QR: query responce 1-4 | Opcode: query opcode (0 - standard query, 1 - inverse query, 2 = server status req., 3-15 reserved) 5 | AA: authoritive answer 6 | TC: Truncation 7 | RD: Recursion desired 8 | RA: recursion available 9-11 | Z: Reserved 12-15 | RCODE: responce code (0 - no error, 1 - format error, 2 - server failure, 3 - name error, 4 - not implemented, 5 - refused, 6-16 - reserved) */ // get question information offset = sizeof(dns_header); // lets get qname name = get_name(buf, &offset); dns_quest.qname = name; //add_qname(name, offset); // get qtype and qclass memcpy((void*)&(dns_quest.qtype), buf + offset, sizeof(short int)); offset += sizeof(short int); dns_quest.qtype = ntohs(dns_quest.qtype); memcpy((void*)&(dns_quest.qclass), buf + offset, sizeof(short int)); offset += sizeof(short int); dns_quest.qclass = ntohs(dns_quest.qclass); printf("dns_quest {\n"); printf(" qname='%s'\n", dns_quest.qname); printf(" qtype=%d (Type: %s)\n", dns_quest.qtype, translate_qtype(dns_quest.qtype)); printf(" qclass=%d (Class: %s)\n", dns_quest.qclass, translate_qclass(dns_quest.qclass)); printf("}\n"); PBLUE("Answers:"); for(i =0; i < dns_head.ancount; i++) { //get Answers dns_ans = get_resource(buf, &offset); } PPURPLE("Authoritative nameservers:"); for(i =0; i < dns_head.arcount; i++) { //get Authoritative Nameservers dns_auth = get_resource(buf, &offset); } PPURPLE("Additional records:"); for(i =0; i < dns_head.arcount; i++) { //get Additional Records dns_add = get_resource(buf, &offset); } return 0; } void usage(char * arg0) { printf("Usage: %s [-r] [-t type] [-c class] name...\n", arg0); printf(" -r Perform a reverse lookup. In this instance the name\n"); printf(" argument should be an IP address.\n"); printf(" -t Manually specify a DNS query type using a numeric argument.\n"); printf(" -T Manually specify a DNS query type using a string argument.\n"); printf(" Valid inputs are: A, NS, MD, MF, CNAME, SOA, MB, MG, MR,\n"); printf(" NULL, WKS, PTR, HINFO, MINFO, MX, TXT, AXFR, MAILB, MAILA,\n"); printf(" and '*'\n"); printf(" -c Manually specify a DNS query class using a numeric argument.\n"); printf(" -C Manually specify a DNS query class using a string argument.\n"); printf(" Valid inputs are: IN, CS, CH, HS, and '*'\n"); printf(" -h Display this help.\n"); } int main(int argc, char ** argv) { int fd; struct sockaddr_in dest_addr; dns_question q; int c; char * name; int index; short int qclass = QCLASS_IN; short int qtype = QTYPE_A; short int flags = 0x0100; int opt_r = 0; if (argc < 2) { printf("Not enough arguments. Please supply domain to lookup\n"); exit(1); } /* * flags bits: bit n | meaning 0 | QR: query responce 1-4 | Opcode: query opcode (0 - standard query, 1 - inverse query, 2 = server status req., 3-15 reserved) 5 | AA: authoritive answer 6 | TC: Truncation 7 | RD: Recursion desired 8 | RA: recursion available 9-11 | Z: Reserved 12-15 | RCODE: responce code (0 - no error, 1 - format error, 2 - server failure, 3 - name error, 4 - not implemented, 5 - refused, 6-16 - reserved) */ //0000100100000000 while ((c = getopt (argc, argv, "rt:T:c:C:n:h")) != -1) { switch (c) { case 'r': opt_r = 1; qtype = QTYPE_PTR; break; case 't': qtype = atoi(optarg); break; case 'T': qtype = translate_from_qtype(optarg); if (qtype == -1) { usage(argv[0]); printf("Error: Failed to translate qtype '%s'\n", optarg); } break; case 'c': qclass = atoi(optarg); break; case 'C': qclass = translate_from_qclass(optarg); if (qclass == -1) { usage(argv[0]); printf("Error: Failed to translate qclass '%s'\n", optarg); } break; case 'n': name = optarg; break; case 'h': usage(argv[0]); return 1; } } PGREEN("My DNS Implementation"); for (index = optind; index < argc; index++) { if (opt_r) { int name_len = strlen(argv[index]); struct in_addr inp; unsigned long int s_addr; unsigned long int s_addr_out = 0; inet_aton(argv[index], &inp); s_addr = inp.s_addr; s_addr_out |= (s_addr & 0xFF000000) >> 24; s_addr_out |= (s_addr & 0x00FF0000) >> 8; s_addr_out |= (s_addr & 0x0000FF00) << 8; s_addr_out |= (s_addr & 0x000000FF) << 24; inp.s_addr = s_addr_out; name = (char*)calloc(name_len + 13 + 1, sizeof(char)); strncpy(name, (char*)inet_ntoa(inp), name_len); strncat(name, ".in-addr.arpa", 13); } else { name = argv[index]; } fd = socket( AF_INET, SOCK_DGRAM, 0 ); if (fd <= 0) { perror("socket() error"); return 1; } dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(PORT); dest_addr.sin_addr.s_addr = inet_addr( NS ); memset(&(dest_addr.sin_zero), '\0', 8); q.qtype = qtype; q.qclass = qclass; q.qname = name; make_query(fd, flags, dest_addr, q); read_responce(fd, dest_addr); close(fd); } return 0; }