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" \
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
];
112 dns_id0
=0, // DNS packet ID
114 dns_flags0
=0, // consists of the flags below
116 dns_num_q0
=0, // number of questions
118 dns_num_ans0
=0, // number of answer resource records
120 dns_num_aut0
=0, // number of authority resource records
122 dns_num_add0
=0, // number of additional resource records
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)
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
143 dns_packet
[MAX_PAYLOAD_SIZE
], // finally the whole packet with all sections
144 section
[MAX_PAYLOAD_SIZE
]; // contains only a section (intermediately)
150 if ( (getarg(tx
.arg_string
,"help", NULL
)==1) && (mode
==DNS
) )
154 cli_print(gcli
, "%s", MZ_DNS_HELP
);
161 "\n%s", MZ_DNS_HELP
);
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);
174 dns_flags_qr
= 0; // request
175 dns_flags_opcode
= 0; // 'standard query' (also for response!)
177 dns_type0
= 1; // A record
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
++)
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
242 section
[i
]=0x0c; // this number always points to the first query entry
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
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
;
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)
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)
324 if (getarg(tx
.arg_string
,"!aa",NULL
)==1)
329 if (getarg(tx
.arg_string
,"tc",NULL
)==1)
334 if (getarg(tx
.arg_string
,"!tc",NULL
)==1)
339 if (getarg(tx
.arg_string
,"rd",NULL
)==1)
344 if (getarg(tx
.arg_string
,"!rd",NULL
)==1)
349 if (getarg(tx
.arg_string
,"ra",NULL
)==1)
354 if (getarg(tx
.arg_string
,"!ra",NULL
)==1)
359 if (getarg(tx
.arg_string
,"z", argval
)==1)
361 dns_flags_z
= (u_int8_t
) str2int (argval
);
366 fprintf(stderr
, "mz/dns: [Warning] z cannot exceed 7 => will reduce to 7!\n");
374 if (getarg(tx
.arg_string
,"rcode", argval
)==1)
376 dns_flags_rcode
= (u_int8_t
) str2int (argval
);
377 if (dns_flags_rcode
> 15)
381 fprintf(stderr
, "mz/dns: [Warning] rcode cannot exceed 15 => will reduce to 15!\n");
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
;
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
;
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
;
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
;
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)
445 // +--+--+--+--+--+--+--+--+
446 // |QR| OPCODE |AA|TC|RD|
447 // +--+--+--+--+--+--+--+--+
451 // +--+--+--+--+--+--+--+--+
453 // +--+--+--+--+--+--+--+--+
458 dns_flags1
|= dns_flags_qr
;
460 dns_flags_opcode
<<= 3;
461 dns_flags1
|= dns_flags_opcode
;
464 dns_flags1
|= dns_flags_aa
;
467 dns_flags1
|= dns_flags_tc
;
469 dns_flags1
|= dns_flags_rd
;
474 dns_flags0
|= dns_flags_ra
;
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:
506 dns_packet
[12+j
] = section
[j
];
510 //////////////////////////////////////////////////////////
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
;
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"
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
;
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
558 for (j
=i
; j
<(i
+cnt
);j
++)
565 } while ( (token
= strtok_r(NULL
, ".", &saveptr2
)) != NULL
);
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) )
580 tmp
= (u_int16_t
) str2int (field
);
583 x
= (unsigned char*) &tmp
;
590 else // use default type=A
596 // finally add the class=IN:
600 // this is the number of used bytes:
607 printf("%02x \n",gbuf[j]);
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
638 avoid_buffer_overflow
++;
639 for (j
=0; j
<cnt
;j
++)
643 avoid_buffer_overflow
++;
644 if (avoid_buffer_overflow
== 512) return 512;
649 } while ( (token
= strtok_r(NULL
, ".", &saveptr
)) != NULL
);
651 i
++; // number of total bytes written
659 // Accepts a valid triple of type:ttl:rdata and writes anything in gbuf[] and gbuf_s.
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
689 len
= strlen (argval
);
691 // determine number of occurences of ':'
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);
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.
719 case 1: // only RDATA specified
720 strncpy(field3
, field1
, 512);
724 case 2: // TYPE and RDATA
725 strncpy(field3
, field2
, 512);
731 //printf("fields: [%s] [%s] [%s]\n",field1,field2,field3);
733 //////////////////////////////////////////////////////////////////////
734 // Now create the whole answer section: Type, Class, TTL, RDLEN, RDATA
737 if ( (strcmp(field1
,"CNAME")==0) ||
738 (strcmp(field1
,"cname")==0) )
744 else if ( (strcmp(field1
,"A")==0) ||
745 (strcmp(field1
,"a")==0) )
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);
760 //// CLASS = IN = 0x00 01
761 gbuf
[2]= 0x00; gbuf
[3]=0x01;
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);
775 RDLEN
= num2hex(field3
, rdata
); // should be 4 if IP address
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
);
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
];
808 for (i=0; i<gbuf_s; i++)
810 printf("%02x \n",gbuf[i]);