/* * Mausezahn - A fast versatile traffic generator * Copyright (C) 2008 Herbert Haas * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html * */ //////////////////////////////////////////////////////////////////// // // DNS: Only UDP-based here // //////////////////////////////////////////////////////////////////// #include "mz.h" #include "cli.h" #define MZ_DNS_HELP \ "| DNS type: Send Domain Name System Messages.\n" \ "|\n" \ "| Generally there are two interesting general DNS messages: queries and answers. The easiest\n" \ "| way is to use the following syntax:\n" \ "|\n" \ "| query|q = [:] ............. where type is per default \"A\"\n" \ "| (and class is always \"IN\")\n" \ "|\n" \ "| answer|a = [::] ...... ttl is per default 0.\n" \ "| = [::]/[::]/...\n" \ "|\n" \ "| Note: If you only use the 'query' option then a query is sent. If you additonally add\n" \ "| an 'answer' option then an answer is sent.\n" \ "|\n" \ "| Examples: \n" \ "|\n" \ "| q = www.xyz.com\n" \ "| q = www.xyz.com, a=192.168.1.10\n" \ "| q = www.xyz.com, a=A:3600:192.168.1.10\n" \ "| q = www.xyz.com, a=CNAME:3600:abc.com/A:3600:192.168.1.10\n" \ "|\n" \ "| Note: can be: A, CNAME, or any integer\n" \ "|\n" \ "|\n" \ "| OPTIONAL parameter hacks: (if you don't know what you do this might cause invalid packets)\n" \ "|\n" \ "| Parameter Description query / reply)\n" \ "| -------------------------------------------------------------------------------------\n" \ "|\n" \ "| request/response|reply ..... flag only request / n.a. \n" \ "| id ......................... packet id (0-65535) random / random\n" \ "| opcode (or op) ............. accepts values 0..15 or one of std / 0 \n" \ "| these keywords: \n" \ "| = std ................... Standard Query\n" \ "| = inv ................... Inverse Query\n" \ "| = sts ................... Server Status Request\n" \ "| aa or !aa .................. Authoritative Answer UNSET / SET\n" \ "| tc or !tc .................. Truncation UNSET / UNSET\n" \ "| rd or !rd .................. Recursion Desired SET / SET\n" \ "| ra or !ra .................. Recursion Available UNSET / SET\n" \ "| z .......................... Reserved (takes values 0..7) 0 / 0 \n" \ "| (z=2...authenticated)\n" \ "| rcode ...................... Response Code (0..15); interesting 0 / 0 \n" \ "| values are:\n" \ "| = 0 ...................... No Error Condition\n" \ "| = 1 ...................... Unable to interprete query due to format error\n" \ "| = 2 ...................... Unable to process due to server failure\n" \ "| = 3 ...................... Name in query does not exist\n" \ "| = 4 ...................... Type of query not supported\n" \ "| = 5 ...................... Query refused\n" \ "|\n" \ "| Count values (values 0..65535) will be set automatically! You should not set these\n" \ "| values manually except you are interested in invalid packets.\n" \ "| qdcount (or qdc) ........... Number of entries in question section 1 / 1\n" \ "| ancount (or anc) ........... Number of RRs in answer records section 0 / 1\n" \ "| nscount (or nsc) ........... Number of name server RRs in authority 0 / 0\n" \ "| records section\n" \ "| arcount (or arc) ........... Number of RRs in additional records section 0 / 0\n" \ "\n" static u_int8_t gbuf[MAX_PAYLOAD_SIZE]; // This is only a generic global buffer to handover data more easily static u_int32_t gbuf_s; int dns_get_query (char* argval); int dns_get_answer (char* argval); // Note: I do NOT use libnet here (had problems with bugs there...) int create_dns_packet(void) { char *token, *tokenptr, argval[MAX_PAYLOAD_SIZE]; int i=0,j=0; unsigned char *x; u_int16_t tmp; // 16 bit values: u_int8_t dns_id0 =0, // DNS packet ID dns_id1 =0, dns_flags0 =0, // consists of the flags below dns_flags1 =0, dns_num_q0 =0, // number of questions dns_num_q1 =0, dns_num_ans0 =0, // number of answer resource records dns_num_ans1 =0, dns_num_aut0 =0, // number of authority resource records dns_num_aut1 =0, dns_num_add0 =0, // number of additional resource records dns_num_add1 =0, dns_type0 =0, dns_type1 =0; // bit fields for dns_flags1: Q/R(1), OPCODE(4), AA(1), TC(1), RD(1) // bit fields for dns_flags0: RA(1), Z(3), RCODE(4) u_int8_t dns_flags_qr, // 1 bit dns_flags_opcode, // 4 bits dns_flags_aa, // 1 bit dns_flags_tc, // 1 bit dns_flags_rd, // 1 bit // ---- next byte ----- dns_flags_ra, // 1 bit dns_flags_z, // 3 bits dns_flags_rcode; // 4 bits u_int8_t dns_packet[MAX_PAYLOAD_SIZE], // finally the whole packet with all sections section[MAX_PAYLOAD_SIZE]; // contains only a section (intermediately) u_int32_t dns_packet_s; if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==DNS) ) { if (mz_port) { cli_print(gcli, "%s", MZ_DNS_HELP); return -1; } else { fprintf(stderr,"\n" MAUSEZAHN_VERSION "\n%s", MZ_DNS_HELP); exit(0); } } // general defaults: // TODO: define globals in case dns is called by external functions!) // MOST SAFEST AND EASIEST METHOD: define tx.dns_xxxx for default-initialization // dns_id0 = 0x42; // dns_id0 = (u_int8_t) ( ((float) rand()/RAND_MAX)*255); dns_id1 = 0x42; dns_flags_qr = 0; // request dns_flags_opcode = 0; // 'standard query' (also for response!) dns_type0 = 1; // A record dns_type1 = 0; i=0; ///////////////////////////////////////////////////////////////////////////////// // Evaluate CLI parameters: // Handle the query // if ( (getarg(tx.arg_string,"query", argval)==1) || (getarg(tx.arg_string,"q", argval)==1) ) { (void) dns_get_query (argval); // returns the length in byte dns_num_q0=1; // copy the result from gbuf to our local buffer 'section': for (j=0;j[:[:]]/[:]\n" if ( (getarg(tx.arg_string,"answer", argval)==1) || (getarg(tx.arg_string,"a", argval)==1) ) { // In case there are multiple answer sections seperate them with / or | token = strtok_r(argval,"/|",&tokenptr); do { //then the corresponding answer section: //first create a pointer to the : section[i]=0xc0; // a pointer always starts with MSB=11xxxxxx xxxxxxx = 0xc0 i++; section[i]=0x0c; // this number always points to the first query entry i++; //then add rdata dns_num_ans0 += dns_get_answer (token); //NOTE: 'i' points to the next free byte in section[] (see the query handling above) for (j=0;j 15) { if (!quiet) { fprintf(stderr, "mz/dns: [Warning] Opcode cannot exceed 15 => will reduce to 15!\n"); } dns_flags_opcode = 15; } } } if (getarg(tx.arg_string,"aa",NULL)==1) { dns_flags_aa = 1; } if (getarg(tx.arg_string,"!aa",NULL)==1) { dns_flags_aa = 0; } if (getarg(tx.arg_string,"tc",NULL)==1) { dns_flags_tc = 1; } if (getarg(tx.arg_string,"!tc",NULL)==1) { dns_flags_tc = 0; } if (getarg(tx.arg_string,"rd",NULL)==1) { dns_flags_rd = 1; } if (getarg(tx.arg_string,"!rd",NULL)==1) { dns_flags_rd = 0; } if (getarg(tx.arg_string,"ra",NULL)==1) { dns_flags_ra = 1; } if (getarg(tx.arg_string,"!ra",NULL)==1) { dns_flags_ra = 0; } if (getarg(tx.arg_string,"z", argval)==1) { dns_flags_z = (u_int8_t) str2int (argval); if (dns_flags_z > 7) { if (!quiet) { fprintf(stderr, "mz/dns: [Warning] z cannot exceed 7 => will reduce to 7!\n"); } dns_flags_z = 7; } } if (getarg(tx.arg_string,"rcode", argval)==1) { dns_flags_rcode = (u_int8_t) str2int (argval); if (dns_flags_rcode > 15) { if (!quiet) { fprintf(stderr, "mz/dns: [Warning] rcode cannot exceed 15 => will reduce to 15!\n"); } dns_flags_rcode = 7; } } if ( (getarg(tx.arg_string,"qdcount", argval)==1) || (getarg(tx.arg_string,"qdc", argval)==1) || (getarg(tx.arg_string,"qc", argval)==1) ) { tmp = (u_int16_t) str2int (argval); x = (unsigned char*) &tmp; dns_num_q1 = *x; x++; dns_num_q0 = *x; } if ( (getarg(tx.arg_string,"ancount", argval)==1) || (getarg(tx.arg_string,"anc", argval)==1) ) { tmp = (u_int16_t) str2int (argval); x = (unsigned char*) &tmp; dns_num_ans1 = *x; x++; dns_num_ans0 = *x; } if ( (getarg(tx.arg_string,"nscount", argval)==1) || (getarg(tx.arg_string,"nsc", argval)==1) ) { tmp = (u_int16_t) str2int (argval); x = (unsigned char*) &tmp; dns_num_aut1 = *x; x++; dns_num_aut0 = *x; } if ( (getarg(tx.arg_string,"arcount", argval)==1) || (getarg(tx.arg_string,"arc", argval)==1) ) { tmp = (u_int16_t) str2int (argval); x = (unsigned char*) &tmp; dns_num_add1 = *x; x++; dns_num_add0 = *x; } // // End of optional parameter handling // /////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // Now put all together i. e. create the UDP payload // // bit fields for dns_flags1: Q/R(1), OPCODE(4), AA(1), TC(1), RD(1) // bit fields for dns_flags0: RA(1), Z(3), RCODE(4) // // 7 6 5 4 3 2 1 0 // +--+--+--+--+--+--+--+--+ // |QR| OPCODE |AA|TC|RD| // +--+--+--+--+--+--+--+--+ // // // 7 6 5 4 3 2 1 0 // +--+--+--+--+--+--+--+--+ // |RA| Z | RCODE | // +--+--+--+--+--+--+--+--+ // //// Flags: MSB dns_flags_qr <<= 7; dns_flags1 |= dns_flags_qr; dns_flags_opcode <<= 3; dns_flags1 |= dns_flags_opcode; dns_flags_aa <<= 2; dns_flags1 |= dns_flags_aa; dns_flags_tc <<= 1; dns_flags1 |= dns_flags_tc; dns_flags1 |= dns_flags_rd; //// Flags: LSB dns_flags_ra <<= 7; dns_flags0 |= dns_flags_ra; dns_flags_z <<= 4; dns_flags0 |= dns_flags_z; dns_flags0 |= dns_flags_rcode; //// Add header bytes to dns_packet: dns_packet[0]=dns_id1; dns_packet[1]=dns_id0; dns_packet[2]=dns_flags1; dns_packet[3]=dns_flags0; dns_packet[4]=dns_num_q1; dns_packet[5]=dns_num_q0; dns_packet[6]=dns_num_ans1; dns_packet[7]=dns_num_ans0; dns_packet[8]=dns_num_aut1; dns_packet[9]=dns_num_aut0; dns_packet[10]=dns_num_add1; dns_packet[11]=dns_num_add0; //// Add sections to dns_packet: for (j=0; j[:]\n" // Return value: // number of queries (currently only 1 query accepted, // hence return value is 1 on success or 0 upon failure // int dns_get_query(char* argval) { char *token, *field, *saveptr1=NULL, *saveptr2=NULL; int i,j, cnt; u_int16_t tmp; unsigned char *x; i=0; // now get first field: field = strtok_r(argval, ":", &saveptr1); // decompose into labels: token = strtok_r(field, ".", &saveptr2); do // loop through all labels { cnt = strlen(token); gbuf[i] = cnt; i++; for (j=i; j<(i+cnt);j++) { gbuf[j] = *token; token++; } i+=cnt; } while ( (token = strtok_r(NULL, ".", &saveptr2)) != NULL); gbuf[i]=0x00; i++; // (always point to next empty byte) // lets see if has also been specified: if ( (field = strtok_r(NULL, ":", &saveptr1)) !=NULL) { if ( (strncmp(field, "A",1)==0) || (strncmp(field, "a",1)==0) ) { tmp = 1; } else { tmp = (u_int16_t) str2int (field); } x = (unsigned char*) &tmp; gbuf[i] = *(x+1); i++; gbuf[i] = *x; i++; } else // use default type=A { gbuf[i] = 0x00; i++; gbuf[i] = 0x01; i++; } // finally add the class=IN: gbuf[i] = 0x00; i++; gbuf[i] = 0x01; i++; // this is the number of used bytes: gbuf_s = i; //////// TEST /* for (j=0; j Depending on type the rdata must be handled differently // A:86400:192.168.1.33 => Up to 3 parameters // A:192.168.1.33 => TTL may be omitted, then TTL=0 // 192.168.1.44 => Single parameter can only be an A record // // Other TYPES than A and CNAME are currently not supported and therefore the user must // specify RDATA in hex. // int dns_get_answer(char* argval) { char *field, *saveptr1=NULL; char field1[512], field2[512], field3[512]; int i, len, num_params; u_int16_t TYPE=1; // A u_int8_t *ptrTYPE; u_int32_t TTL=0; u_int8_t *ptrTTL; u_int16_t RDLEN; u_int8_t *ptrRDLEN; u_int8_t rdata[512]; field1[0]='\0'; field2[0]='\0'; field3[0]='\0'; len = strlen (argval); // determine number of occurences of ':' num_params=1; for (i=0; i3) return 0; // Error! // now get the fields (type, ttl, rdata) field = strtok_r(argval, ":", &saveptr1); strncpy(field1, field, 512); if (num_params>1) // 2 or 3 { field = strtok_r(NULL, ":", &saveptr1); strncpy(field2, field, 512); if (num_params==3) { field = strtok_r(NULL, ":", &saveptr1); strncpy(field3, field, 512); } } // Now we have all parameters in field1, field2, and field3. // But field2 and/or field3 might be empty. switch (num_params) { case 1: // only RDATA specified strncpy(field3, field1, 512); strcpy(field1, "A"); strcpy(field2, "0"); break; case 2: // TYPE and RDATA strncpy(field3, field2, 512); strcpy(field2, "0"); break; } //CHECK: //printf("fields: [%s] [%s] [%s]\n",field1,field2,field3); ////////////////////////////////////////////////////////////////////// // Now create the whole answer section: Type, Class, TTL, RDLEN, RDATA //// TYPE if ( (strcmp(field1,"CNAME")==0) || (strcmp(field1,"cname")==0) ) { TYPE=5; gbuf[0]=0x00; gbuf[1]=0x05; } else if ( (strcmp(field1,"A")==0) || (strcmp(field1,"a")==0) ) { TYPE=1; gbuf[0]=0x00; gbuf[1]=0x01; } else // type must be given as number { TYPE = (u_int16_t) str2int(field1); ptrTYPE = (u_int8_t*) &TYPE; gbuf[0]=*(ptrTYPE+1); gbuf[1]=*(ptrTYPE); } //// CLASS = IN = 0x00 01 gbuf[2]= 0x00; gbuf[3]=0x01; //// TTL TTL = (u_int32_t) str2int(field2); ptrTTL = (u_int8_t*) &TTL; gbuf[4]= *(ptrTTL+3); gbuf[5]= *(ptrTTL+2); gbuf[6]= *(ptrTTL+1); gbuf[7]= *(ptrTTL+0); //// RDLEN and RDATA if (TYPE==1) // A { RDLEN = num2hex(field3, rdata); // should be 4 if IP address if (RDLEN!=4) { fprintf(stderr," mz/dns_get_answer: [WARNING] RDATA of A record should contain an IPv4 address (4 bytes).\n"); } } else if (TYPE==5) // CNAME { RDLEN = dns_process_label (field3, rdata); if (RDLEN==0) { fprintf(stderr," mz/dns_get_answer: [WARNING] RDATA must contain a domain name.\n"); } } else // Any other type { RDLEN = str2hex(field3, rdata, 512); // should be 4 if IP address } ptrRDLEN = (u_int8_t*) &RDLEN; gbuf[8] = *(ptrRDLEN+1); gbuf[9] = *(ptrRDLEN+0); // finally write rdata for (i=0; i