From 97c7ff17881c7f75436b4e26567440452f361bc1 Mon Sep 17 00:00:00 2001 From: Stephane Bortzmeyer Date: Sun, 16 Jun 2013 21:39:41 +0100 Subject: [PATCH] Parse and stores EDNS options --- DNSmezzo/packet-defs.h | 5 ++ DNSmezzo/packets2postgresql.c | 130 +++++++++++++++++++++------------- DNSmezzo/pcap-parse.c | 36 +++++++++- 3 files changed, 118 insertions(+), 53 deletions(-) diff --git a/DNSmezzo/packet-defs.h b/DNSmezzo/packet-defs.h index 10464b9..0529782 100644 --- a/DNSmezzo/packet-defs.h +++ b/DNSmezzo/packet-defs.h @@ -11,6 +11,9 @@ void fatal(char *, ...); +/* Not in RFC 6891, it is an arbitrary limit we define */ +#define MAX_EDNS_OPTIONS 10 + struct dns_packet { unsigned int rank; struct timeval date; @@ -30,6 +33,8 @@ struct dns_packet { bool edns0; unsigned int edns0_size; /* Undefined if edns0 is false */ bool do_dnssec; /* Undefined if edns0 is false */ + unsigned int edns_options[MAX_EDNS_OPTIONS]; + unsigned int num_edns_options; unsigned int ancount, nscount, arcount; }; diff --git a/DNSmezzo/packets2postgresql.c b/DNSmezzo/packets2postgresql.c index 899f189..eee641f 100644 --- a/DNSmezzo/packets2postgresql.c +++ b/DNSmezzo/packets2postgresql.c @@ -95,7 +95,7 @@ escape(char *to, const char *from) to[j++] = 't'; break; case 92: - if (from[i+1] == '.') { + if (from[i + 1] == '.') { to[j++] = '.'; i++; } @@ -116,34 +116,38 @@ escape(char *to, const char *from) * http://coding.debuntu.org/c-implementing-str_replace-replace-all-occurrences-substring * Licence: GPL v2+ */ -char * -str_replace ( const char *string, const char *substr, const char *replacement ){ - char *tok = NULL; - char *newstr = NULL; - char *oldstr = NULL; - char *head = NULL; - - /* if either substr or replacement is NULL, duplicate string a let caller handle it */ - if ( substr == NULL || replacement == NULL ) return strdup (string); - newstr = strdup (string); - head = newstr; - while ( (tok = strstr ( head, substr ))){ - oldstr = newstr; - newstr = malloc ( strlen ( oldstr ) - strlen ( substr ) + strlen ( replacement ) + 1 ); - /*failed to alloc mem, free old string and return NULL */ - if ( newstr == NULL ){ - free (oldstr); - return NULL; +char * +str_replace(const char *string, const char *substr, const char *replacement) +{ + char *tok = NULL; + char *newstr = NULL; + char *oldstr = NULL; + char *head = NULL; + + /* if either substr or replacement is NULL, duplicate string a let caller handle + * it */ + if (substr == NULL || replacement == NULL) + return strdup(string); + newstr = strdup(string); + head = newstr; + while ((tok = strstr(head, substr))) { + oldstr = newstr; + newstr = malloc(strlen(oldstr) - strlen(substr) + strlen(replacement) + 1); + /* failed to alloc mem, free old string and return NULL */ + if (newstr == NULL) { + free(oldstr); + return NULL; + } + memcpy(newstr, oldstr, tok - oldstr); + memcpy(newstr + (tok - oldstr), replacement, strlen(replacement)); + memcpy(newstr + (tok - oldstr) + strlen(replacement), tok + strlen(substr), + strlen(oldstr) - strlen(substr) - (tok - oldstr)); + memset(newstr + strlen(oldstr) - strlen(substr) + strlen(replacement), 0, 1); + /* move back head right after the last replacement */ + head = newstr + (tok - oldstr) + strlen(replacement); + free(oldstr); } - memcpy ( newstr, oldstr, tok - oldstr ); - memcpy ( newstr + (tok - oldstr), replacement, strlen ( replacement ) ); - memcpy ( newstr + (tok - oldstr) + strlen( replacement ), tok + strlen ( substr ), strlen ( oldstr ) - strlen ( substr ) - ( tok - oldstr ) ); - memset ( newstr + strlen ( oldstr ) - strlen ( substr ) + strlen ( replacement ) , 0, 1 ); - /* move back head right after the last replacement */ - head = newstr + (tok - oldstr) + strlen( replacement ); - free (oldstr); - } - return newstr; + return newstr; } #define METADATA_INT 1 @@ -192,7 +196,7 @@ get_metadata(name, key, type) #define SQL_PACKET_COMMAND "COPY DNS_Packets \ (file, rank, date, length, src_address, dst_address, protocol, src_port, dst_port, \ - query, query_id, opcode, rcode, aa, tc, rd, ra, qname, qtype, qclass, edns0_size, do_dnssec, \ + query, query_id, opcode, rcode, aa, tc, rd, ra, qname, qtype, qclass, edns0_size, do_dnssec, edns_options, \ ancount, nscount, arcount, registered_domain, lowercase_qname) FROM STDIN;" #define PREPARED_PACKET_STMT "copy-data" #define SQL_FILE_COMMAND "INSERT INTO Pcap_Files (hostname, filename, datalinktype, snaplength, filesize, filedate, stoppedat, samplingrate) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id;" @@ -243,11 +247,13 @@ main(int argc, char *argv[]) PGresult *result; struct tm file_creation, date_firstpacket, date_lastpacket; int *sampling; - char *buffer, *bufptr, *tmp, *tmp2, *tmp3; /* SQL COPY input buffer */ + char *buffer, *bufptr, *tmp, *tmp2, *tmp3; /* SQL COPY input + * buffer */ unsigned long copied; const char *file_params[NUM_FILE_PARAMS]; const char *fileend_params[NUM_FILEEND_PARAMS]; unsigned int file_id = 0; + unsigned int i; progname = argv[0]; while ((ch = getopt(argc, argv, "nvm:c:p")) != -1) { @@ -301,9 +307,9 @@ main(int argc, char *argv[]) fatal(PQerrorMessage(conn)); } /* We find lot of funny characters in domain names, not always UTF-8. - * Setting the client encoding to Latin-1 is arbitrary, but it is to - * be sure the program won't crash (because any string is valid - * Latin-1, unlike UTF-8). */ + * Setting the client encoding to Latin-1 is arbitrary, but it is to be sure + * * the program won't crash (because any string is valid Latin-1, unlike + * UTF-8). */ result = PQexec(conn, "SET CLIENT_ENCODING TO 'LATIN-1';"); if (PQresultStatus(result) != PGRES_COMMAND_OK) { fatal("Cannot set encoding"); @@ -399,18 +405,18 @@ main(int argc, char *argv[]) fatal("Cannot malloc %i bytes for the COPY input buffer", BUFFER_SIZE); } bufptr = buffer; - if (! dry_run) { - PQclear(result); - result = PQexecPrepared(conn, PREPARED_PACKET_STMT, 0, NULL, NULL, NULL, 0); - if (PQresultStatus(result) == PGRES_COPY_IN) { - /* OK */ - } else { - fatal("Result for '%s' is %s", SQL_PACKET_COMMAND, - PQresultErrorMessage(result)); + if (!dry_run) { + PQclear(result); + result = PQexecPrepared(conn, PREPARED_PACKET_STMT, 0, NULL, NULL, NULL, 0); + if (PQresultStatus(result) == PGRES_COPY_IN) { + /* OK */ + } else { + fatal("Result for '%s' is %s", SQL_PACKET_COMMAND, + PQresultErrorMessage(result)); + } } - } // read TLDs only once at daemon startup - tldnode* tree = readTldTree(tldString); + tldnode *tree = readTldTree(tldString); tmp = malloc(MAX_STRING); tmp2 = malloc(MAX_STRING); @@ -499,9 +505,27 @@ main(int argc, char *argv[]) bufptr += strlen(packet->do_dnssec ? "true" : "false"); *bufptr = '\t'; bufptr++; + if (packet->num_edns_options > 0) { + strcpy(bufptr, "{"); + bufptr += strlen("{"); + for (i = 0; i < packet->num_edns_options; i++) { + sprintf(tmp, "%i", packet->edns_options[i]); + strcpy(bufptr, tmp); + bufptr += strlen(tmp); + if (i < (packet->num_edns_options - 1)) { + strcpy(bufptr, ","); + bufptr++; + } + } + strcpy(bufptr, "}\t"); + bufptr += strlen("}\t"); + } else { + strcpy(bufptr, "\\N\t"); + bufptr += strlen("\\N\t"); + } } else { - strcpy(bufptr, "\\N\t\\N\t"); - bufptr += strlen("\\N\t\\N\t"); + strcpy(bufptr, "\\N\t\\N\t\\N\t"); + bufptr += strlen("\\N\t\\N\t\\N\t"); } sprintf(tmp, "%i\t", packet->ancount); strcpy(bufptr, tmp); @@ -518,7 +542,7 @@ main(int argc, char *argv[]) } else { tmp3 = NULL; } - if (tmp3 == NULL) { /* this is already a TLD */ + if (tmp3 == NULL) { /* this is already a TLD */ escape(tmp, tmp2); } else { escape(tmp, tmp3); @@ -564,13 +588,17 @@ main(int argc, char *argv[]) if (copied != 1) { fatal("Cannot end the data stream: %s", PQerrorMessage(conn)); } + result = PQgetResult(conn); + if (result != NULL && strcmp(PQerrorMessage(conn), "") != 0) { + fatal("COPY of data failed: %s.", PQerrorMessage(conn)); + } } if (verbose) { ct = current_time(); fprintf(stdout, "%s Done, %lu DNS packets stored%s\n", ct, packetnum, (maxpackets > 0 - && packetnum >= - maxpackets) ? + && packetnum >= + maxpackets) ? " - interrupted before the end because max packets read" : ""); free(ct); } @@ -619,7 +647,8 @@ main(int argc, char *argv[]) result = PQexec(conn, tmp); free(tmp); if (PQresultStatus(result) != PGRES_COMMAND_OK) { - fatal("Error while creating the UNIQUE(id) constraint: %s", PQresultErrorMessage(result)); + fatal("Error while creating the UNIQUE(id) constraint: %s", + PQresultErrorMessage(result)); } PQclear(result); @@ -627,7 +656,8 @@ main(int argc, char *argv[]) result = PQexec(conn, tmp); free(tmp); if (PQresultStatus(result) != PGRES_COMMAND_OK) { - fatal("Error while creating the foreign key (file) constraint: %s", PQresultErrorMessage(result)); + fatal("Error while creating the foreign key (file) constraint: %s", + PQresultErrorMessage(result)); } PQclear(result); diff --git a/DNSmezzo/pcap-parse.c b/DNSmezzo/pcap-parse.c index 41a92fe..7a8f1aa 100644 --- a/DNSmezzo/pcap-parse.c +++ b/DNSmezzo/pcap-parse.c @@ -108,6 +108,9 @@ get_next_packet(struct dns_packet *decoded, pcap_parser_file * input) uint16_t edns_size; uint16_t extended_rcode_and_version; uint16_t zpart; + uint num_edns_options = 0; + uint16_t opt_len, option_code, option_length; + uint16_t option_codes[MAX_EDNS_OPTIONS]; const uint8_t *sectionptr; const uint8_t *where_am_i; /* Cursor in packet */ bool end_of_name; @@ -382,9 +385,36 @@ get_next_packet(struct dns_packet *decoded, pcap_parser_file * input) DNS_DO_DNSSEC(zpart) ? true : false; } sectionptr += 2; - /* TODO: dissect the RDATA to find things like the - * option code (such as 3 for NSID) - * http://www.iana.org/assignments/dns-parameters */ + CHECK_SECTIONPTR(2); +#ifdef PICKY_WITH_ALIGNMENT + opt_len = unaligned_uint16(sectionptr); +#else + opt_len = ntohs(*((uint16_t *) sectionptr)); +#endif + sectionptr += 2; + while (opt_len > 0 + && num_edns_options < MAX_EDNS_OPTIONS) { + CHECK_SECTIONPTR(2); +#ifdef PICKY_WITH_ALIGNMENT + option_code = unaligned_uint16(sectionptr); +#else + option_code = ntohs(*((uint16_t *) sectionptr)); +#endif + option_codes[num_edns_options] = option_code; + sectionptr += 2; + CHECK_SECTIONPTR(2); +#ifdef PICKY_WITH_ALIGNMENT + option_length = unaligned_uint16(sectionptr); +#else + option_length = ntohs(*((uint16_t *) sectionptr)); +#endif + opt_len -= (option_length + 4); + num_edns_options++; + sectionptr += option_length; + } + memcpy(decoded->edns_options, option_codes, + num_edns_options * sizeof(option_code)); + decoded->num_edns_options = num_edns_options; } } }