2 * Mausezahn - A fast versatile traffic generator
3 * Copyright (C) 2008 Herbert Haas
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.
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
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
25 ////////////////////////////////////////////////////////////////////
32 "| DNS type: Send Domain Name System Messages.\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" \
37 "| query|q = <name>[:<type>] ............. where type is per default \"A\"\n" \
38 "| (and class is always \"IN\")\n" \
40 "| answer|a = [<type>:<ttl>:]<rdata> ...... ttl is per default 0.\n" \
41 "| = [<type>:<ttl>:]<rdata>/[<type>:<ttl>:]<rdata>/...\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" \
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" \
53 "| Note: <type> can be: A, CNAME, or any integer\n" \
56 "| OPTIONAL parameter hacks: (if you don't know what you do this might cause invalid packets)\n" \
58 "| Parameter Description query / reply)\n" \
59 "| -------------------------------------------------------------------------------------\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" \
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" \
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" \
92 static u_int8_t gbuf
[MAX_PAYLOAD_SIZE
]; // This is only a generic global buffer to handover data more easily
93 static u_int32_t gbuf_s
;
95 int dns_get_query (char* argval
);
96 int dns_get_answer (char* argval
);
100 // Note: I do NOT use libnet here (had problems with bugs there...)
101 int create_dns_packet(void)
104 char *token
, *tokenptr
, argval
[MAX_PAYLOAD_SIZE
];
114 dns_id0
=0, // DNS packet ID
116 dns_flags0
=0, // consists of the flags below
118 dns_num_q0
=0, // number of questions
120 dns_num_ans0
=0, // number of answer resource records
122 dns_num_aut0
=0, // number of authority resource records
124 dns_num_add0
=0, // number of additional resource records
130 // bit fields for dns_flags1: Q/R(1), OPCODE(4), AA(1), TC(1), RD(1)
131 // bit fields for dns_flags0: RA(1), Z(3), RCODE(4)
133 dns_flags_qr
, // 1 bit
134 dns_flags_opcode
, // 4 bits
135 dns_flags_aa
, // 1 bit
136 dns_flags_tc
, // 1 bit
137 dns_flags_rd
, // 1 bit
138 // ---- next byte -----
139 dns_flags_ra
, // 1 bit
140 dns_flags_z
, // 3 bits
141 dns_flags_rcode
; // 4 bits
145 dns_packet
[MAX_PAYLOAD_SIZE
], // finally the whole packet with all sections
146 section
[MAX_PAYLOAD_SIZE
]; // contains only a section (intermediately)
152 if ( (getarg(tx
.arg_string
,"help", NULL
)==1) && (mode
==DNS
) )
156 cli_print(gcli
, "%s", MZ_DNS_HELP
);
163 "\n%s", MZ_DNS_HELP
);
170 // TODO: define globals in case dns is called by external functions!)
171 // MOST SAFEST AND EASIEST METHOD: define tx.dns_xxxx for default-initialization
173 dns_id0
= 0x42; // dns_id0 = (u_int8_t) ( ((float) rand()/RAND_MAX)*255);
176 dns_flags_qr
= 0; // request
177 dns_flags_opcode
= 0; // 'standard query' (also for response!)
179 dns_type0
= 1; // A record
186 /////////////////////////////////////////////////////////////////////////////////
187 // Evaluate CLI parameters:
190 // Handle the query //
192 if ( (getarg(tx
.arg_string
,"query", argval
)==1) ||
193 (getarg(tx
.arg_string
,"q", argval
)==1) )
196 (void) dns_get_query (argval
); // returns the length in byte dns_num_q0=1;
198 // copy the result from gbuf to our local buffer 'section':
199 for (j
=0;j
<gbuf_s
;j
++)
206 // Set defaults if not already set by callee.
207 // !! But ONLY set these if there is no additional answer section
208 // !! because then the answer section should set the defaults !!!
209 if ( (getarg(tx
.arg_string
,"answer", NULL
)==0) && // no answer
210 (getarg(tx
.arg_string
,"a", NULL
)==0) )
212 if (!tx
.dp
) tx
.dp
= 53;
213 if (!tx
.sp
) tx
.sp
= 42000;
217 // These are the defaults for a query:
218 dns_flags_aa
= 1; // authoritative answer
219 dns_flags_tc
= 0; // not truncated
220 dns_flags_rd
= 1; // recursion desired
221 dns_flags_ra
= 0; // recursion available
222 dns_flags_z
= 0; // FYI: if 010 = 2 = authenticated
223 dns_flags_rcode
= 0; // no errors
224 dns_num_q0
= 1; // number of questions
229 // Handle the answer:
231 // answer|a = <name>[:<type>[:<class>]]/[<ttl>:]<rdata>\n"
232 if ( (getarg(tx
.arg_string
,"answer", argval
)==1) ||
233 (getarg(tx
.arg_string
,"a", argval
)==1) )
236 // In case there are multiple answer sections seperate them with / or |
237 token
= strtok_r(argval
,"/|",&tokenptr
);
240 //then the corresponding answer section:
241 //first create a pointer to the <name>:
242 section
[i
]=0xc0; // a pointer always starts with MSB=11xxxxxx xxxxxxx = 0xc0
244 section
[i
]=0x0c; // this number always points to the first query entry
247 dns_num_ans0
+= dns_get_answer (token
);
248 //NOTE: 'i' points to the next free byte in section[] (see the query handling above)
249 for (j
=0;j
<gbuf_s
;j
++)
251 section
[j
+i
]=gbuf
[j
];
253 i
=i
+gbuf_s
; // so 'i' again points to the next free byte in section[]
254 } while ( (token
= strtok_r(NULL
,"/|",&tokenptr
))!=NULL
);
256 if (!tx
.sp
) tx
.sp
= 53;
257 if (!tx
.dp
) tx
.dp
= 42000; // should be set by user
258 dns_flags_qr
= 1; // response
259 dns_flags_aa
= 0; // no authoritative answer
260 dns_flags_tc
= 0; // not truncated
261 dns_flags_rd
= 1; // recursion desired
262 dns_flags_ra
= 0; // recursion not available
263 dns_flags_z
= 0; // FYI: if 010 = 2 = authenticated
264 dns_flags_rcode
= 0; // no errors
270 // Now 'i' contains the number of DNS payload bytes = valid bytes in section[]
274 ///////////////////////////////////////////////////////////////////////////////////////////////
275 // Now let's handle the optional other commands, if some user really changed them...
279 if (getarg(tx
.arg_string
,"id",argval
)==1)
281 tmp
= (u_int16_t
) str2int (argval
);
282 x
= (unsigned char*) &tmp
;
290 if ( (getarg(tx
.arg_string
,"opcode", argval
)==1) || (getarg(tx
.arg_string
,"op", argval
)==1))
292 if (strncmp(argval
,"std",3)==0) // standard query
294 dns_flags_opcode
= 0;
296 else if (strncmp(argval
,"inv",3)==0) // inverse query
298 dns_flags_opcode
= 1;
300 else if (strncmp(argval
,"sts",3)==0) // status server request
302 dns_flags_opcode
= 2;
304 else // specified as integer
306 dns_flags_opcode
= (u_int8_t
) str2int (argval
);
307 if (dns_flags_opcode
> 15)
311 fprintf(stderr
, "mz/dns: [Warning] Opcode cannot exceed 15 => will reduce to 15!\n");
313 dns_flags_opcode
= 15;
321 if (getarg(tx
.arg_string
,"aa",NULL
)==1)
326 if (getarg(tx
.arg_string
,"!aa",NULL
)==1)
331 if (getarg(tx
.arg_string
,"tc",NULL
)==1)
336 if (getarg(tx
.arg_string
,"!tc",NULL
)==1)
341 if (getarg(tx
.arg_string
,"rd",NULL
)==1)
346 if (getarg(tx
.arg_string
,"!rd",NULL
)==1)
351 if (getarg(tx
.arg_string
,"ra",NULL
)==1)
356 if (getarg(tx
.arg_string
,"!ra",NULL
)==1)
361 if (getarg(tx
.arg_string
,"z", argval
)==1)
363 dns_flags_z
= (u_int8_t
) str2int (argval
);
368 fprintf(stderr
, "mz/dns: [Warning] z cannot exceed 7 => will reduce to 7!\n");
376 if (getarg(tx
.arg_string
,"rcode", argval
)==1)
378 dns_flags_rcode
= (u_int8_t
) str2int (argval
);
379 if (dns_flags_rcode
> 15)
383 fprintf(stderr
, "mz/dns: [Warning] rcode cannot exceed 15 => will reduce to 15!\n");
390 if ( (getarg(tx
.arg_string
,"qdcount", argval
)==1) ||
391 (getarg(tx
.arg_string
,"qdc", argval
)==1) ||
392 (getarg(tx
.arg_string
,"qc", argval
)==1) )
395 tmp
= (u_int16_t
) str2int (argval
);
396 x
= (unsigned char*) &tmp
;
402 if ( (getarg(tx
.arg_string
,"ancount", argval
)==1) ||
403 (getarg(tx
.arg_string
,"anc", argval
)==1) )
405 tmp
= (u_int16_t
) str2int (argval
);
406 x
= (unsigned char*) &tmp
;
412 if ( (getarg(tx
.arg_string
,"nscount", argval
)==1) ||
413 (getarg(tx
.arg_string
,"nsc", argval
)==1) )
415 tmp
= (u_int16_t
) str2int (argval
);
416 x
= (unsigned char*) &tmp
;
422 if ( (getarg(tx
.arg_string
,"arcount", argval
)==1) ||
423 (getarg(tx
.arg_string
,"arc", argval
)==1) )
425 tmp
= (u_int16_t
) str2int (argval
);
426 x
= (unsigned char*) &tmp
;
433 // End of optional parameter handling
435 ///////////////////////////////////////////////////////////////////////////////////////////////
440 /////////////////////////////////////////////////////////
441 // Now put all together i. e. create the UDP payload
443 // bit fields for dns_flags1: Q/R(1), OPCODE(4), AA(1), TC(1), RD(1)
444 // bit fields for dns_flags0: RA(1), Z(3), RCODE(4)
447 // +--+--+--+--+--+--+--+--+
448 // |QR| OPCODE |AA|TC|RD|
449 // +--+--+--+--+--+--+--+--+
453 // +--+--+--+--+--+--+--+--+
455 // +--+--+--+--+--+--+--+--+
460 dns_flags1
|= dns_flags_qr
;
462 dns_flags_opcode
<<= 3;
463 dns_flags1
|= dns_flags_opcode
;
466 dns_flags1
|= dns_flags_aa
;
469 dns_flags1
|= dns_flags_tc
;
471 dns_flags1
|= dns_flags_rd
;
476 dns_flags0
|= dns_flags_ra
;
479 dns_flags0
|= dns_flags_z
;
481 dns_flags0
|= dns_flags_rcode
;
483 //// Add header bytes to dns_packet:
485 dns_packet
[0]=dns_id1
;
486 dns_packet
[1]=dns_id0
;
488 dns_packet
[2]=dns_flags1
;
489 dns_packet
[3]=dns_flags0
;
491 dns_packet
[4]=dns_num_q1
;
492 dns_packet
[5]=dns_num_q0
;
494 dns_packet
[6]=dns_num_ans1
;
495 dns_packet
[7]=dns_num_ans0
;
497 dns_packet
[8]=dns_num_aut1
;
498 dns_packet
[9]=dns_num_aut0
;
500 dns_packet
[10]=dns_num_add1
;
501 dns_packet
[11]=dns_num_add0
;
503 //// Add sections to dns_packet:
508 dns_packet
[12+j
] = section
[j
];
512 //////////////////////////////////////////////////////////
515 tx
.udp_payload_s
= dns_packet_s
;
517 // copy the dns_paylod to the udp_payload
519 for (j
=0; j
<tx
.udp_payload_s
; j
++)
521 tx
.udp_payload
[j
] = dns_packet
[j
];
524 tx
.udp_len
= 8 + tx
.udp_payload_s
;
531 ////////////////////////////////////////////////////////////////////////////////////////////
532 // Accepts a string like "www.perihel.at:A" or "www.perihel.at"
533 // and creates a valid query section using the global gbuf[] and gbuf_s
535 // query|q = <name>[:<type>]\n"
537 // number of queries (currently only 1 query accepted,
538 // hence return value is 1 on success or 0 upon failure
540 int dns_get_query(char* argval
)
542 char *token
, *field
, *saveptr1
=NULL
, *saveptr2
=NULL
;
549 // now get first field: <name>
550 field
= strtok_r(argval
, ":", &saveptr1
);
552 // decompose <name> into labels:
553 token
= strtok_r(field
, ".", &saveptr2
);
555 do // loop through all labels
560 for (j
=i
; j
<(i
+cnt
);j
++)
567 } while ( (token
= strtok_r(NULL
, ".", &saveptr2
)) != NULL
);
570 i
++; // (always point to next empty byte)
573 // lets see if <type> has also been specified:
574 if ( (field
= strtok_r(NULL
, ":", &saveptr1
)) !=NULL
)
576 if ( (strncmp(field
, "A",1)==0) || (strncmp(field
, "a",1)==0) )
582 tmp
= (u_int16_t
) str2int (field
);
585 x
= (unsigned char*) &tmp
;
592 else // use default type=A
598 // finally add the class=IN:
602 // this is the number of used bytes:
609 printf("%02x \n",gbuf[j]);
623 // Given a label (e. g. www.google.com) creates correct bytes in *buf
624 // and returns number of bytes created.
625 // NOTE: Label MUST NOT be longer than 512 characters.
627 int dns_process_label(char* label
, u_int8_t
*buf
)
629 char *saveptr
=NULL
, *token
;
630 int i
=0, j
=0, cnt
=0, avoid_buffer_overflow
=0;
632 token
= strtok_r(label
, ".", &saveptr
);
634 do // loop through all labels
640 avoid_buffer_overflow
++;
641 for (j
=0; j
<cnt
;j
++)
645 avoid_buffer_overflow
++;
646 if (avoid_buffer_overflow
== 512) return 512;
651 } while ( (token
= strtok_r(NULL
, ".", &saveptr
)) != NULL
);
653 i
++; // number of total bytes written
661 // Accepts a valid triple of type:ttl:rdata and writes anything in gbuf[] and gbuf_s.
665 // CNAME:3600:abc.com => Depending on type the rdata must be handled differently
666 // A:86400:192.168.1.33 => Up to 3 parameters
667 // A:192.168.1.33 => TTL may be omitted, then TTL=0
668 // 192.168.1.44 => Single parameter can only be an A record
670 // Other TYPES than A and CNAME are currently not supported and therefore the user must
671 // specify RDATA in hex.
674 int dns_get_answer(char* argval
)
676 char *field
, *saveptr1
=NULL
;
677 char field1
[512], field2
[512], field3
[512];
678 int i
, len
, num_params
;
679 u_int16_t TYPE
=1; // A
691 len
= strlen (argval
);
693 // determine number of occurences of ':'
695 for (i
=0; i
<len
; i
++)
697 if (argval
[i
]==':') num_params
++;
699 if (num_params
>3) return 0; // Error!
701 // now get the fields (type, ttl, rdata)
702 field
= strtok_r(argval
, ":", &saveptr1
);
703 strncpy(field1
, field
, 512);
704 if (num_params
>1) // 2 or 3
706 field
= strtok_r(NULL
, ":", &saveptr1
);
707 strncpy(field2
, field
, 512);
710 field
= strtok_r(NULL
, ":", &saveptr1
);
711 strncpy(field3
, field
, 512);
716 // Now we have all parameters in field1, field2, and field3.
717 // But field2 and/or field3 might be empty.
721 case 1: // only RDATA specified
722 strncpy(field3
, field1
, 512);
726 case 2: // TYPE and RDATA
727 strncpy(field3
, field2
, 512);
733 //printf("fields: [%s] [%s] [%s]\n",field1,field2,field3);
735 //////////////////////////////////////////////////////////////////////
736 // Now create the whole answer section: Type, Class, TTL, RDLEN, RDATA
739 if ( (strcmp(field1
,"CNAME")==0) ||
740 (strcmp(field1
,"cname")==0) )
746 else if ( (strcmp(field1
,"A")==0) ||
747 (strcmp(field1
,"a")==0) )
753 else // type must be given as number
755 TYPE
= (u_int16_t
) str2int(field1
);
756 ptrTYPE
= (u_int8_t
*) &TYPE
;
757 gbuf
[0]=*(ptrTYPE
+1);
762 //// CLASS = IN = 0x00 01
763 gbuf
[2]= 0x00; gbuf
[3]=0x01;
766 TTL
= (u_int32_t
) str2int(field2
);
767 ptrTTL
= (u_int8_t
*) &TTL
;
768 gbuf
[4]= *(ptrTTL
+3);
769 gbuf
[5]= *(ptrTTL
+2);
770 gbuf
[6]= *(ptrTTL
+1);
771 gbuf
[7]= *(ptrTTL
+0);
777 RDLEN
= num2hex(field3
, rdata
); // should be 4 if IP address
780 fprintf(stderr
," mz/dns_get_answer: [WARNING] RDATA of A record should contain an IPv4 address (4 bytes).\n");
783 else if (TYPE
==5) // CNAME
785 RDLEN
= dns_process_label (field3
, rdata
);
788 fprintf(stderr
," mz/dns_get_answer: [WARNING] RDATA must contain a domain name.\n");
791 else // Any other type
793 RDLEN
= str2hex(field3
, rdata
, 512); // should be 4 if IP address
796 ptrRDLEN
= (u_int8_t
*) &RDLEN
;
797 gbuf
[8] = *(ptrRDLEN
+1);
798 gbuf
[9] = *(ptrRDLEN
+0);
801 // finally write rdata
802 for (i
=0; i
<RDLEN
; i
++)
804 gbuf
[10+i
] = rdata
[i
];
810 for (i=0; i<gbuf_s; i++)
812 printf("%02x \n",gbuf[i]);