docs: authors: add Doug as minor contr. (thanks)
[netsniff-ng.git] / src / dns.c
blob5f9203cbad7f7fb7af1ee442a16c32faf0443de1
1 /*
2 * Mausezahn - A fast versatile traffic generator
3 * Copyright (C) 2008 Herbert Haas
4 *
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License version 2 as published by the
7 * Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12 * details.
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html
21 ////////////////////////////////////////////////////////////////////
23 // DNS: Only UDP-based here
24 //
25 ////////////////////////////////////////////////////////////////////
27 #include "mz.h"
28 #include "cli.h"
31 #define MZ_DNS_HELP \
32 "| DNS type: Send Domain Name System Messages.\n" \
33 "|\n" \
34 "| Generally there are two interesting general DNS messages: queries and answers. The easiest\n" \
35 "| way is to use the following syntax:\n" \
36 "|\n" \
37 "| query|q = <name>[:<type>] ............. where type is per default \"A\"\n" \
38 "| (and class is always \"IN\")\n" \
39 "|\n" \
40 "| answer|a = [<type>:<ttl>:]<rdata> ...... ttl is per default 0.\n" \
41 "| = [<type>:<ttl>:]<rdata>/[<type>:<ttl>:]<rdata>/...\n" \
42 "|\n" \
43 "| Note: If you only use the 'query' option then a query is sent. If you additonally add\n" \
44 "| an 'answer' option then an answer is sent.\n" \
45 "|\n" \
46 "| Examples: \n" \
47 "|\n" \
48 "| q = www.xyz.com\n" \
49 "| q = www.xyz.com, a=192.168.1.10\n" \
50 "| q = www.xyz.com, a=A:3600:192.168.1.10\n" \
51 "| q = www.xyz.com, a=CNAME:3600:abc.com/A:3600:192.168.1.10\n" \
52 "|\n" \
53 "| Note: <type> can be: A, CNAME, or any integer\n" \
54 "|\n" \
55 "|\n" \
56 "| OPTIONAL parameter hacks: (if you don't know what you do this might cause invalid packets)\n" \
57 "|\n" \
58 "| Parameter Description query / reply)\n" \
59 "| -------------------------------------------------------------------------------------\n" \
60 "|\n" \
61 "| request/response|reply ..... flag only request / n.a. \n" \
62 "| id ......................... packet id (0-65535) random / random\n" \
63 "| opcode (or op) ............. accepts values 0..15 or one of std / 0 \n" \
64 "| these keywords: \n" \
65 "| = std ................... Standard Query\n" \
66 "| = inv ................... Inverse Query\n" \
67 "| = sts ................... Server Status Request\n" \
68 "| aa or !aa .................. Authoritative Answer UNSET / SET\n" \
69 "| tc or !tc .................. Truncation UNSET / UNSET\n" \
70 "| rd or !rd .................. Recursion Desired SET / SET\n" \
71 "| ra or !ra .................. Recursion Available UNSET / SET\n" \
72 "| z .......................... Reserved (takes values 0..7) 0 / 0 \n" \
73 "| (z=2...authenticated)\n" \
74 "| rcode ...................... Response Code (0..15); interesting 0 / 0 \n" \
75 "| values are:\n" \
76 "| = 0 ...................... No Error Condition\n" \
77 "| = 1 ...................... Unable to interprete query due to format error\n" \
78 "| = 2 ...................... Unable to process due to server failure\n" \
79 "| = 3 ...................... Name in query does not exist\n" \
80 "| = 4 ...................... Type of query not supported\n" \
81 "| = 5 ...................... Query refused\n" \
82 "|\n" \
83 "| Count values (values 0..65535) will be set automatically! You should not set these\n" \
84 "| values manually except you are interested in invalid packets.\n" \
85 "| qdcount (or qdc) ........... Number of entries in question section 1 / 1\n" \
86 "| ancount (or anc) ........... Number of RRs in answer records section 0 / 1\n" \
87 "| nscount (or nsc) ........... Number of name server RRs in authority 0 / 0\n" \
88 "| records section\n" \
89 "| arcount (or arc) ........... Number of RRs in additional records section 0 / 0\n" \
90 "\n"
93 int dns_get_query (char* argval);
94 int dns_get_answer (char* argval);
98 // Note: I do NOT use libnet here (had problems with bugs there...)
99 int create_dns_packet ()
102 char *token, *tokenptr, argval[MAX_PAYLOAD_SIZE];
104 int i=0,j=0;
106 unsigned char *x;
107 u_int16_t tmp;
110 // 16 bit values:
111 u_int8_t
112 dns_id0 =0, // DNS packet ID
113 dns_id1 =0,
114 dns_flags0 =0, // consists of the flags below
115 dns_flags1 =0,
116 dns_num_q0 =0, // number of questions
117 dns_num_q1 =0,
118 dns_num_ans0 =0, // number of answer resource records
119 dns_num_ans1 =0,
120 dns_num_aut0 =0, // number of authority resource records
121 dns_num_aut1 =0,
122 dns_num_add0 =0, // number of additional resource records
123 dns_num_add1 =0,
124 dns_type0 =0,
125 dns_type1 =0;
128 // bit fields for dns_flags1: Q/R(1), OPCODE(4), AA(1), TC(1), RD(1)
129 // bit fields for dns_flags0: RA(1), Z(3), RCODE(4)
130 u_int8_t
131 dns_flags_qr, // 1 bit
132 dns_flags_opcode, // 4 bits
133 dns_flags_aa, // 1 bit
134 dns_flags_tc, // 1 bit
135 dns_flags_rd, // 1 bit
136 // ---- next byte -----
137 dns_flags_ra, // 1 bit
138 dns_flags_z, // 3 bits
139 dns_flags_rcode; // 4 bits
142 u_int8_t
143 dns_packet[MAX_PAYLOAD_SIZE], // finally the whole packet with all sections
144 section[MAX_PAYLOAD_SIZE]; // contains only a section (intermediately)
145 u_int32_t
146 dns_packet_s;
150 if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==DNS) )
152 if (mz_port)
154 cli_print(gcli, "%s", MZ_DNS_HELP);
155 return -1;
157 else
159 fprintf(stderr,"\n"
160 MAUSEZAHN_VERSION
161 "\n%s", MZ_DNS_HELP);
162 exit(0);
167 // general defaults:
168 // TODO: define globals in case dns is called by external functions!)
169 // MOST SAFEST AND EASIEST METHOD: define tx.dns_xxxx for default-initialization
171 dns_id0 = 0x42; // dns_id0 = (u_int8_t) ( ((float) rand()/RAND_MAX)*255);
172 dns_id1 = 0x42;
174 dns_flags_qr = 0; // request
175 dns_flags_opcode = 0; // 'standard query' (also for response!)
177 dns_type0 = 1; // A record
178 dns_type1 = 0;
181 i=0;
184 /////////////////////////////////////////////////////////////////////////////////
185 // Evaluate CLI parameters:
188 // Handle the query //
190 if ( (getarg(tx.arg_string,"query", argval)==1) ||
191 (getarg(tx.arg_string,"q", argval)==1) )
194 (void) dns_get_query (argval); // returns the length in byte dns_num_q0=1;
196 // copy the result from gbuf to our local buffer 'section':
197 for (j=0;j<gbuf_s;j++)
199 section[j]=gbuf[j];
202 i = gbuf_s;
204 // Set defaults if not already set by callee.
205 // !! But ONLY set these if there is no additional answer section
206 // !! because then the answer section should set the defaults !!!
207 if ( (getarg(tx.arg_string,"answer", NULL)==0) && // no answer
208 (getarg(tx.arg_string,"a", NULL)==0) )
210 if (!tx.dp) tx.dp = 53;
211 if (!tx.sp) tx.sp = 42000;
215 // These are the defaults for a query:
216 dns_flags_aa = 1; // authoritative answer
217 dns_flags_tc = 0; // not truncated
218 dns_flags_rd = 1; // recursion desired
219 dns_flags_ra = 0; // recursion available
220 dns_flags_z = 0; // FYI: if 010 = 2 = authenticated
221 dns_flags_rcode = 0; // no errors
222 dns_num_q0 = 1; // number of questions
227 // Handle the answer:
229 // answer|a = <name>[:<type>[:<class>]]/[<ttl>:]<rdata>\n"
230 if ( (getarg(tx.arg_string,"answer", argval)==1) ||
231 (getarg(tx.arg_string,"a", argval)==1) )
234 // In case there are multiple answer sections seperate them with / or |
235 token = strtok_r(argval,"/|",&tokenptr);
238 //then the corresponding answer section:
239 //first create a pointer to the <name>:
240 section[i]=0xc0; // a pointer always starts with MSB=11xxxxxx xxxxxxx = 0xc0
241 i++;
242 section[i]=0x0c; // this number always points to the first query entry
243 i++;
244 //then add rdata
245 dns_num_ans0 += dns_get_answer (token);
246 //NOTE: 'i' points to the next free byte in section[] (see the query handling above)
247 for (j=0;j<gbuf_s;j++)
249 section[j+i]=gbuf[j];
251 i=i+gbuf_s; // so 'i' again points to the next free byte in section[]
252 } while ( (token = strtok_r(NULL,"/|",&tokenptr))!=NULL);
254 if (!tx.sp) tx.sp = 53;
255 if (!tx.dp) tx.dp = 42000; // should be set by user
256 dns_flags_qr = 1; // response
257 dns_flags_aa = 0; // no authoritative answer
258 dns_flags_tc = 0; // not truncated
259 dns_flags_rd = 1; // recursion desired
260 dns_flags_ra = 0; // recursion not available
261 dns_flags_z = 0; // FYI: if 010 = 2 = authenticated
262 dns_flags_rcode = 0; // no errors
267 // *** NOTE ***
268 // Now 'i' contains the number of DNS payload bytes = valid bytes in section[]
272 ///////////////////////////////////////////////////////////////////////////////////////////////
273 // Now let's handle the optional other commands, if some user really changed them...
277 if (getarg(tx.arg_string,"id",argval)==1)
279 tmp = (u_int16_t) str2int (argval);
280 x = (unsigned char*) &tmp;
282 dns_id1 = *x;
283 x++;
284 dns_id0 = *x;
288 if ( (getarg(tx.arg_string,"opcode", argval)==1) || (getarg(tx.arg_string,"op", argval)==1))
290 if (strncmp(argval,"std",3)==0) // standard query
292 dns_flags_opcode = 0;
294 else if (strncmp(argval,"inv",3)==0) // inverse query
296 dns_flags_opcode = 1;
298 else if (strncmp(argval,"sts",3)==0) // status server request
300 dns_flags_opcode = 2;
302 else // specified as integer
304 dns_flags_opcode = (u_int8_t) str2int (argval);
305 if (dns_flags_opcode > 15)
307 if (!quiet)
309 fprintf(stderr, "mz/dns: [Warning] Opcode cannot exceed 15 => will reduce to 15!\n");
311 dns_flags_opcode = 15;
319 if (getarg(tx.arg_string,"aa",NULL)==1)
321 dns_flags_aa = 1;
324 if (getarg(tx.arg_string,"!aa",NULL)==1)
326 dns_flags_aa = 0;
329 if (getarg(tx.arg_string,"tc",NULL)==1)
331 dns_flags_tc = 1;
334 if (getarg(tx.arg_string,"!tc",NULL)==1)
336 dns_flags_tc = 0;
339 if (getarg(tx.arg_string,"rd",NULL)==1)
341 dns_flags_rd = 1;
344 if (getarg(tx.arg_string,"!rd",NULL)==1)
346 dns_flags_rd = 0;
349 if (getarg(tx.arg_string,"ra",NULL)==1)
351 dns_flags_ra = 1;
354 if (getarg(tx.arg_string,"!ra",NULL)==1)
356 dns_flags_ra = 0;
359 if (getarg(tx.arg_string,"z", argval)==1)
361 dns_flags_z = (u_int8_t) str2int (argval);
362 if (dns_flags_z > 7)
364 if (!quiet)
366 fprintf(stderr, "mz/dns: [Warning] z cannot exceed 7 => will reduce to 7!\n");
368 dns_flags_z = 7;
374 if (getarg(tx.arg_string,"rcode", argval)==1)
376 dns_flags_rcode = (u_int8_t) str2int (argval);
377 if (dns_flags_rcode > 15)
379 if (!quiet)
381 fprintf(stderr, "mz/dns: [Warning] rcode cannot exceed 15 => will reduce to 15!\n");
383 dns_flags_rcode = 7;
388 if ( (getarg(tx.arg_string,"qdcount", argval)==1) ||
389 (getarg(tx.arg_string,"qdc", argval)==1) ||
390 (getarg(tx.arg_string,"qc", argval)==1) )
393 tmp = (u_int16_t) str2int (argval);
394 x = (unsigned char*) &tmp;
395 dns_num_q1 = *x;
396 x++;
397 dns_num_q0 = *x;
400 if ( (getarg(tx.arg_string,"ancount", argval)==1) ||
401 (getarg(tx.arg_string,"anc", argval)==1) )
403 tmp = (u_int16_t) str2int (argval);
404 x = (unsigned char*) &tmp;
405 dns_num_ans1 = *x;
406 x++;
407 dns_num_ans0 = *x;
410 if ( (getarg(tx.arg_string,"nscount", argval)==1) ||
411 (getarg(tx.arg_string,"nsc", argval)==1) )
413 tmp = (u_int16_t) str2int (argval);
414 x = (unsigned char*) &tmp;
415 dns_num_aut1 = *x;
416 x++;
417 dns_num_aut0 = *x;
420 if ( (getarg(tx.arg_string,"arcount", argval)==1) ||
421 (getarg(tx.arg_string,"arc", argval)==1) )
423 tmp = (u_int16_t) str2int (argval);
424 x = (unsigned char*) &tmp;
425 dns_num_add1 = *x;
426 x++;
427 dns_num_add0 = *x;
431 // End of optional parameter handling
433 ///////////////////////////////////////////////////////////////////////////////////////////////
438 /////////////////////////////////////////////////////////
439 // Now put all together i. e. create the UDP payload
441 // bit fields for dns_flags1: Q/R(1), OPCODE(4), AA(1), TC(1), RD(1)
442 // bit fields for dns_flags0: RA(1), Z(3), RCODE(4)
444 // 7 6 5 4 3 2 1 0
445 // +--+--+--+--+--+--+--+--+
446 // |QR| OPCODE |AA|TC|RD|
447 // +--+--+--+--+--+--+--+--+
450 // 7 6 5 4 3 2 1 0
451 // +--+--+--+--+--+--+--+--+
452 // |RA| Z | RCODE |
453 // +--+--+--+--+--+--+--+--+
456 //// Flags: MSB
457 dns_flags_qr <<= 7;
458 dns_flags1 |= dns_flags_qr;
460 dns_flags_opcode <<= 3;
461 dns_flags1 |= dns_flags_opcode;
463 dns_flags_aa <<= 2;
464 dns_flags1 |= dns_flags_aa;
466 dns_flags_tc <<= 1;
467 dns_flags1 |= dns_flags_tc;
469 dns_flags1 |= dns_flags_rd;
471 //// Flags: LSB
473 dns_flags_ra <<= 7;
474 dns_flags0 |= dns_flags_ra;
476 dns_flags_z <<= 4;
477 dns_flags0 |= dns_flags_z;
479 dns_flags0 |= dns_flags_rcode;
481 //// Add header bytes to dns_packet:
483 dns_packet[0]=dns_id1;
484 dns_packet[1]=dns_id0;
486 dns_packet[2]=dns_flags1;
487 dns_packet[3]=dns_flags0;
489 dns_packet[4]=dns_num_q1;
490 dns_packet[5]=dns_num_q0;
492 dns_packet[6]=dns_num_ans1;
493 dns_packet[7]=dns_num_ans0;
495 dns_packet[8]=dns_num_aut1;
496 dns_packet[9]=dns_num_aut0;
498 dns_packet[10]=dns_num_add1;
499 dns_packet[11]=dns_num_add0;
501 //// Add sections to dns_packet:
504 for (j=0; j<i; j++)
506 dns_packet[12+j] = section[j];
510 //////////////////////////////////////////////////////////
512 dns_packet_s = i+12;
513 tx.udp_payload_s = dns_packet_s;
515 // copy the dns_paylod to the udp_payload
517 for (j=0; j<tx.udp_payload_s; j++)
519 tx.udp_payload[j] = dns_packet[j];
522 tx.udp_len = 8 + tx.udp_payload_s;
524 return dns_packet_s;
529 ////////////////////////////////////////////////////////////////////////////////////////////
530 // Accepts a string like "www.perihel.at:A" or "www.perihel.at"
531 // and creates a valid query section using the global gbuf[] and gbuf_s
533 // query|q = <name>[:<type>]\n"
534 // Return value:
535 // number of queries (currently only 1 query accepted,
536 // hence return value is 1 on success or 0 upon failure
538 int dns_get_query(char* argval)
540 char *token, *field, *saveptr1=NULL, *saveptr2=NULL;
541 int i,j, cnt;
542 u_int16_t tmp;
543 unsigned char *x;
545 i=0;
547 // now get first field: <name>
548 field = strtok_r(argval, ":", &saveptr1);
550 // decompose <name> into labels:
551 token = strtok_r(field, ".", &saveptr2);
553 do // loop through all labels
555 cnt = strlen(token);
556 gbuf[i] = cnt;
557 i++;
558 for (j=i; j<(i+cnt);j++)
560 gbuf[j] = *token;
561 token++;
563 i+=cnt;
565 } while ( (token = strtok_r(NULL, ".", &saveptr2)) != NULL);
567 gbuf[i]=0x00;
568 i++; // (always point to next empty byte)
571 // lets see if <type> has also been specified:
572 if ( (field = strtok_r(NULL, ":", &saveptr1)) !=NULL)
574 if ( (strncmp(field, "A",1)==0) || (strncmp(field, "a",1)==0) )
576 tmp = 1;
578 else
580 tmp = (u_int16_t) str2int (field);
583 x = (unsigned char*) &tmp;
585 gbuf[i] = *(x+1);
586 i++;
587 gbuf[i] = *x;
588 i++;
590 else // use default type=A
592 gbuf[i] = 0x00; i++;
593 gbuf[i] = 0x01; i++;
596 // finally add the class=IN:
597 gbuf[i] = 0x00; i++;
598 gbuf[i] = 0x01; i++;
600 // this is the number of used bytes:
601 gbuf_s = i;
603 //////// TEST
605 for (j=0; j<i; j++)
607 printf("%02x \n",gbuf[j]);
609 printf("i=%u\n",i);
612 return 1;
621 // Given a label (e. g. www.google.com) creates correct bytes in *buf
622 // and returns number of bytes created.
623 // NOTE: Label MUST NOT be longer than 512 characters.
625 int dns_process_label(char* label, u_int8_t *buf)
627 char *saveptr=NULL, *token;
628 int i=0, j=0, cnt=0, avoid_buffer_overflow=0;
630 token = strtok_r(label, ".", &saveptr);
632 do // loop through all labels
634 cnt = strlen(token);
635 i++;
636 *buf = cnt;
637 buf++;
638 avoid_buffer_overflow++;
639 for (j=0; j<cnt ;j++)
641 *buf = *token;
642 buf++;
643 avoid_buffer_overflow++;
644 if (avoid_buffer_overflow == 512) return 512;
645 token++;
647 i+=cnt;
649 } while ( (token = strtok_r(NULL, ".", &saveptr)) != NULL);
650 *buf=0x00;
651 i++; // number of total bytes written
652 return i;
659 // Accepts a valid triple of type:ttl:rdata and writes anything in gbuf[] and gbuf_s.
661 // Syntax examples:
663 // CNAME:3600:abc.com => Depending on type the rdata must be handled differently
664 // A:86400:192.168.1.33 => Up to 3 parameters
665 // A:192.168.1.33 => TTL may be omitted, then TTL=0
666 // 192.168.1.44 => Single parameter can only be an A record
668 // Other TYPES than A and CNAME are currently not supported and therefore the user must
669 // specify RDATA in hex.
672 int dns_get_answer(char* argval)
674 char *field, *saveptr1=NULL;
675 char field1[512], field2[512], field3[512];
676 int i, len, num_params;
677 u_int16_t TYPE=1; // A
678 u_int8_t *ptrTYPE;
679 u_int32_t TTL=0;
680 u_int8_t *ptrTTL;
681 u_int16_t RDLEN;
682 u_int8_t *ptrRDLEN;
683 u_int8_t rdata[512];
685 field1[0]='\0';
686 field2[0]='\0';
687 field3[0]='\0';
689 len = strlen (argval);
691 // determine number of occurences of ':'
692 num_params=1;
693 for (i=0; i<len; i++)
695 if (argval[i]==':') num_params++;
697 if (num_params>3) return 0; // Error!
699 // now get the fields (type, ttl, rdata)
700 field = strtok_r(argval, ":", &saveptr1);
701 strncpy(field1, field, 512);
702 if (num_params>1) // 2 or 3
704 field = strtok_r(NULL, ":", &saveptr1);
705 strncpy(field2, field, 512);
706 if (num_params==3)
708 field = strtok_r(NULL, ":", &saveptr1);
709 strncpy(field3, field, 512);
714 // Now we have all parameters in field1, field2, and field3.
715 // But field2 and/or field3 might be empty.
717 switch (num_params)
719 case 1: // only RDATA specified
720 strncpy(field3, field1, 512);
721 strcpy(field1, "A");
722 strcpy(field2, "0");
723 break;
724 case 2: // TYPE and RDATA
725 strncpy(field3, field2, 512);
726 strcpy(field2, "0");
727 break;
730 //CHECK:
731 //printf("fields: [%s] [%s] [%s]\n",field1,field2,field3);
733 //////////////////////////////////////////////////////////////////////
734 // Now create the whole answer section: Type, Class, TTL, RDLEN, RDATA
736 //// TYPE
737 if ( (strcmp(field1,"CNAME")==0) ||
738 (strcmp(field1,"cname")==0) )
740 TYPE=5;
741 gbuf[0]=0x00;
742 gbuf[1]=0x05;
744 else if ( (strcmp(field1,"A")==0) ||
745 (strcmp(field1,"a")==0) )
747 TYPE=1;
748 gbuf[0]=0x00;
749 gbuf[1]=0x01;
751 else // type must be given as number
753 TYPE = (u_int16_t) str2int(field1);
754 ptrTYPE = (u_int8_t*) &TYPE;
755 gbuf[0]=*(ptrTYPE+1);
756 gbuf[1]=*(ptrTYPE);
760 //// CLASS = IN = 0x00 01
761 gbuf[2]= 0x00; gbuf[3]=0x01;
763 //// TTL
764 TTL = (u_int32_t) str2int(field2);
765 ptrTTL = (u_int8_t*) &TTL;
766 gbuf[4]= *(ptrTTL+3);
767 gbuf[5]= *(ptrTTL+2);
768 gbuf[6]= *(ptrTTL+1);
769 gbuf[7]= *(ptrTTL+0);
772 //// RDLEN and RDATA
773 if (TYPE==1) // A
775 RDLEN = num2hex(field3, rdata); // should be 4 if IP address
776 if (RDLEN!=4)
778 fprintf(stderr," mz/dns_get_answer: [WARNING] RDATA of A record should contain an IPv4 address (4 bytes).\n");
781 else if (TYPE==5) // CNAME
783 RDLEN = dns_process_label (field3, rdata);
784 if (RDLEN==0)
786 fprintf(stderr," mz/dns_get_answer: [WARNING] RDATA must contain a domain name.\n");
789 else // Any other type
791 RDLEN = str2hex(field3, rdata, 512); // should be 4 if IP address
794 ptrRDLEN = (u_int8_t*) &RDLEN;
795 gbuf[8] = *(ptrRDLEN+1);
796 gbuf[9] = *(ptrRDLEN+0);
799 // finally write rdata
800 for (i=0; i<RDLEN; i++)
802 gbuf[10+i] = rdata[i];
804 gbuf_s = 10+RDLEN;
806 //////// TEST
808 for (i=0; i<gbuf_s; i++)
810 printf("%02x \n",gbuf[i]);
812 printf("i=%u\n",i);
815 return 1;