diff options
Diffstat (limited to 'examples/ldns-dpa.c')
-rw-r--r-- | examples/ldns-dpa.c | 2837 |
1 files changed, 2837 insertions, 0 deletions
diff --git a/examples/ldns-dpa.c b/examples/ldns-dpa.c new file mode 100644 index 0000000..356b7ee --- /dev/null +++ b/examples/ldns-dpa.c @@ -0,0 +1,2837 @@ +/* + * ldns-dpa inspects the (udp) DNS packets found in a pcap file + * and provides statistics about them + * + * (C) NLnet Labs 2006 - 2008 + * + * See the file LICENSE for the license + */ +#include "config.h" + +#include <ldns/ldns.h> + +#ifdef HAVE_PCAP_H +#ifdef HAVE_LIBPCAP +#include "ldns-dpa.h" + +#ifdef HAVE_NETINET_IP6_H +#include <netinet/ip6.h> +#endif +#include <errno.h> + +#ifndef IP_OFFMASK +#define IP_OFFMASK 0x1fff +#endif + +int verbosity = 1; + +#define ETHER_HEADER_LENGTH 14 +#define UDP_HEADER_LENGTH 8 +#define IP6_HEADER_LENGTH 40 + +/* some systems don't have this? */ +#ifndef ETHERTYPE_IPV6 +#define ETHERTYPE_IPV6 0x86dd +#endif + +#define MAX_MATCHES 20 +#define MAX_OPERATORS 7 + + +/* global options */ +bool show_filter_matches = false; +size_t total_nr_of_dns_packets = 0; +size_t total_nr_of_filtered_packets = 0; +size_t not_ip_packets = 0; +size_t bad_dns_packets = 0; +size_t arp_packets = 0; +size_t udp_packets = 0; +size_t tcp_packets = 0; +size_t fragmented_packets = 0; +size_t lost_packet_fragments = 0; +FILE *hexdumpfile = NULL; +pcap_dumper_t *dumper = NULL; +pcap_dumper_t *not_ip_dump = NULL; +pcap_dumper_t *bad_dns_dump = NULL; + + +struct +fragment_part { + uint16_t ip_id; + uint8_t data[65536]; + size_t cur_len; +}; + +struct fragment_part *fragment_p; + +/* To add a match, + * - add it to the enum + * - add it to the table_matches const + * - add a handler to value_matches + * - tell in get_string_value() where in the packet the data lies + * - add to parser? + * - add to show_match_ function + */ +enum enum_match_ids { + MATCH_ID, + MATCH_OPCODE, + MATCH_RCODE, + MATCH_PACKETSIZE, + MATCH_QR, + MATCH_TC, + MATCH_AD, + MATCH_CD, + MATCH_RD, + MATCH_EDNS, + MATCH_EDNS_PACKETSIZE, + MATCH_DO, + MATCH_QUESTION_SIZE, + MATCH_ANSWER_SIZE, + MATCH_AUTHORITY_SIZE, + MATCH_ADDITIONAL_SIZE, + MATCH_SRC_ADDRESS, + MATCH_DST_ADDRESS, + MATCH_TIMESTAMP, + MATCH_QUERY, + MATCH_QTYPE, + MATCH_QNAME, + MATCH_ANSWER, + MATCH_AUTHORITY, + MATCH_ADDITIONAL, + MATCH_LAST +}; +typedef enum enum_match_ids match_id; + +enum enum_counter_types { + TYPE_INT, + TYPE_BOOL, + TYPE_OPCODE, + TYPE_RCODE, + TYPE_STRING, + TYPE_TIMESTAMP, + TYPE_ADDRESS, + TYPE_RR, + TYPE_RR_TYPE, + TYPE_LAST +}; +typedef enum enum_counter_types counter_type; + +const ldns_lookup_table lt_types[] = { + {TYPE_INT, "int" }, + {TYPE_BOOL, "bool" }, + {TYPE_OPCODE, "opcode" }, + {TYPE_RCODE, "rcode" }, + {TYPE_STRING, "string" }, + {TYPE_TIMESTAMP, "timestamp" }, + {TYPE_ADDRESS, "address" }, + {TYPE_RR, "rr" }, + { 0, NULL } +}; + +enum enum_type_operators { + OP_EQUAL, + OP_NOTEQUAL, + OP_GREATER, + OP_LESSER, + OP_GREATEREQUAL, + OP_LESSEREQUAL, + OP_CONTAINS, + OP_LAST +}; +typedef enum enum_type_operators type_operator; + +const ldns_lookup_table lt_operators[] = { + { OP_EQUAL, "=" }, + { OP_NOTEQUAL, "!=" }, + { OP_GREATER, ">" }, + { OP_LESSER, "<" }, + { OP_GREATEREQUAL, ">=" }, + { OP_LESSEREQUAL, "<=" }, + { OP_CONTAINS, "~=" }, + { 0, NULL } +}; + +static const char *get_op_str(type_operator op) { + const ldns_lookup_table *lt; + lt = ldns_lookup_by_id((ldns_lookup_table *) lt_operators, op); + if (lt) { + return lt->name; + } else { + fprintf(stderr, "Unknown operator id: %u\n", op); + exit(1); + } +} + +static type_operator +get_op_id(char *op_str) +{ + const ldns_lookup_table *lt; + lt = ldns_lookup_by_name((ldns_lookup_table *) lt_operators, op_str); + if (lt) { + return (type_operator) lt->id; + } else { + fprintf(stderr, "Unknown operator: %s\n", op_str); + exit(1); + } +} + +struct struct_type_operators { + counter_type type; + size_t operator_count; + type_operator operators[10]; +}; +typedef struct struct_type_operators type_operators; + +const type_operators const_type_operators[] = { + { TYPE_INT, 6, { OP_EQUAL, OP_NOTEQUAL, OP_GREATER, OP_LESSER, OP_GREATEREQUAL, OP_LESSEREQUAL, 0, 0, 0, 0 } }, + { TYPE_BOOL, 2, { OP_EQUAL, OP_NOTEQUAL, 0, 0, 0, 0, 0, 0, 0, 0} }, + { TYPE_OPCODE, 2, { OP_EQUAL, OP_NOTEQUAL, 0, 0, 0, 0, 0, 0, 0, 0} }, + { TYPE_RCODE, 2, { OP_EQUAL, OP_NOTEQUAL, 0, 0, 0, 0, 0, 0, 0, 0} }, + { TYPE_STRING, 3, { OP_EQUAL, OP_NOTEQUAL, OP_CONTAINS, 0, 0, 0, 0, 0, 0, 0} }, + { TYPE_TIMESTAMP, 6, { OP_EQUAL, OP_NOTEQUAL, OP_GREATER, OP_LESSER, OP_GREATEREQUAL, OP_LESSEREQUAL, 0, 0, 0, 0 } }, + { TYPE_ADDRESS, 3, { OP_EQUAL, OP_NOTEQUAL, OP_CONTAINS, 0, 0, 0, 0, 0, 0, 0} }, + { TYPE_RR, 3, { OP_EQUAL, OP_NOTEQUAL, OP_CONTAINS, 0, 0, 0, 0, 0, 0, 0} }, + { TYPE_RR_TYPE, 6, { OP_EQUAL, OP_NOTEQUAL, OP_GREATER, OP_LESSER, OP_GREATEREQUAL, OP_LESSEREQUAL, 0, 0, 0, 0 } }, + { 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } +}; + +const type_operators * +get_type_operators(counter_type type) { + const type_operators *to = const_type_operators; + while (to) { + if (to->type == type) { + return to; + } + to++; + } + return NULL; +} + +struct struct_match_table { + match_id id; + const char *name; + const char *description; + const counter_type type; +}; +typedef struct struct_match_table match_table; + +/* order of entries has been changed after gprof analysis, and reasoning + * about the uses of -u arguments + */ +const match_table matches[] = { + { MATCH_QUERY, "query", "String representation of the query RR", TYPE_RR }, + { MATCH_QTYPE, "qtype", "RR Type of the question RR, if present", TYPE_RR_TYPE }, + { MATCH_QNAME, "qname", "Owner name of the question RR, if present", TYPE_STRING }, + { MATCH_SRC_ADDRESS, "srcaddress", "address the packet was sent from", TYPE_ADDRESS }, + { MATCH_TIMESTAMP, "timestamp", "time the packet was sent", TYPE_TIMESTAMP }, + { MATCH_DST_ADDRESS, "dstaddress", "address the packet was sent to", TYPE_ADDRESS }, + { MATCH_EDNS_PACKETSIZE, "edns-packetsize", "packets size specified in edns rr", TYPE_INT }, + { MATCH_ID, "id", "id of the packet", TYPE_INT }, + { MATCH_OPCODE, "opcode", "opcode of packet (rfc1035)", TYPE_OPCODE }, + { MATCH_RCODE, "rcode", "response code of packet", TYPE_RCODE }, + { MATCH_PACKETSIZE, "packetsize", "size of packet in bytes", TYPE_INT }, + { MATCH_QR, "qr", "value of qr bit", TYPE_BOOL }, + { MATCH_TC, "tc", "value of tc bit", TYPE_BOOL }, + { MATCH_AD, "ad", "value of ad bit", TYPE_BOOL }, + { MATCH_CD, "cd", "value of cd bit", TYPE_BOOL }, + { MATCH_RD, "rd", "value of rd bit", TYPE_BOOL }, + { MATCH_EDNS, "edns", "existence of edns rr", TYPE_BOOL }, + { MATCH_DO, "do", "value of do bit", TYPE_BOOL }, + { MATCH_QUESTION_SIZE, "questionsize", "number of rrs in the question section", TYPE_INT }, + { MATCH_ANSWER_SIZE, "answersize", "number of rrs in the answer section", TYPE_INT }, + { MATCH_AUTHORITY_SIZE, "authoritysize", "number of rrs in the authority section", TYPE_INT }, + { MATCH_ADDITIONAL_SIZE, "additionalsize", "number of rrs in the additional section", TYPE_INT }, + { MATCH_ANSWER, "answer", "String representation of the answer RRs", TYPE_RR }, + { MATCH_AUTHORITY, "authority", "String representation of the authority RRs", TYPE_RR }, + { MATCH_ADDITIONAL, "additional", "String representation of the additional RRs", TYPE_RR }, + { 0, NULL , NULL, TYPE_INT} +}; + +enum enum_match_expression_operators { + MATCH_EXPR_OR, + MATCH_EXPR_AND, + MATCH_EXPR_LEAF +}; +typedef enum enum_match_expression_operators match_expression_operator; + +struct struct_match_operation { + match_id id; + type_operator operator; + char *value; +}; +typedef struct struct_match_operation match_operation; + +typedef struct struct_match_expression match_expression; +struct struct_match_expression { + /* and or or, or leaf (in which case there are no subtrees, but only a match_table */ + match_expression_operator op; + match_expression *left; + match_expression *right; + match_operation *match; + size_t count; +}; + +typedef struct struct_match_counters match_counters; +struct struct_match_counters { +/* + match_expression **counter; + size_t size; +*/ + match_expression *match; + match_counters *left; + match_counters *right; +}; + +match_table * +get_match_by_name(char *name) { + match_table *mt = (match_table *) matches; + if (name) { + while (mt->name != NULL) { + if (strcasecmp(name, mt->name) == 0) { + return mt; + } + mt++; + } + } + return NULL; +} + +static match_table * +get_match_by_id(match_id id) { + match_table *mt = (match_table *) matches; + + while (mt->name != NULL) { + if (mt->id == id) { + return mt; + } + mt++; + } + return NULL; +} + +static const char * +get_match_name_str(match_id id) { + match_table *mt = get_match_by_id(id); + if (mt) { + return mt->name; + } else { + fprintf(stderr, "Unknown match id: %u\n", id); + exit(1); + return "Unknown match id"; + } +} + +static void +print_match_operation(FILE *output, match_operation *mc) +{ + match_table *mt = NULL; + ldns_lookup_table *lt; + struct timeval time; + time_t time_tt; + int value; + size_t pos; + char *tmp, *tmp2; + + if (mc) { + mt = get_match_by_id(mc->id); + + if (mt) { + fprintf(output, "%s %s ",mt->name, get_op_str(mc->operator)); + + switch (mt->type) { + case TYPE_INT: + case TYPE_STRING: + case TYPE_ADDRESS: + case TYPE_RR: + fprintf(output, "'%s'", mc->value); + break; + case TYPE_BOOL: + if (strncmp(mc->value, "1", 2) == 0) { + fprintf(output,"'true'"); + } else { + fprintf(output,"'false'"); + } + break; + case TYPE_OPCODE: + value = atoi(mc->value); + lt = ldns_lookup_by_id(ldns_opcodes, value); + if (lt) { + fprintf(output, "%s", lt->name); + } else { + fprintf(output, "%s", mc->value); + } + break; + case TYPE_RCODE: + value = atoi(mc->value); + lt = ldns_lookup_by_id(ldns_rcodes, value); + if (lt) { + fprintf(output, "%s", lt->name); + } else { + fprintf(output, "%s", mc->value); + } + break; + case TYPE_TIMESTAMP: +#ifndef S_SPLINT_S + time.tv_sec = (long int) atol(mc->value); +#endif + time_tt = (time_t)time.tv_sec; + tmp = ctime(&time_tt); + tmp2 = malloc(strlen(tmp) + 1); + for (pos = 0; pos < strlen(tmp); pos++) { + if (tmp[pos] == '\n') { + tmp2[pos] = '\0'; + } else { + tmp2[pos] = tmp[pos]; + } + } + tmp2[pos] = '\0'; + fprintf(output, "%s", tmp2); + free(tmp2); + break; + default: + fprintf(output, "'%s'", mc->value); + } + + } else { + fprintf(output, "%u %s '%s'", mc->id, get_op_str(mc->operator), mc->value); + } + } else { + fprintf(output, "(nil)"); + } +} + +static void +print_match_expression(FILE *output, match_expression *expr) +{ + if (expr) { + switch (expr->op) { + case MATCH_EXPR_OR: + fprintf(output, "("); + print_match_expression(output, expr->left); + fprintf(output, " | "); + print_match_expression(output, expr->right); + fprintf(output, ")"); + break; + case MATCH_EXPR_AND: + fprintf(output, "("); + print_match_expression(output, expr->left); + fprintf(output, " & "); + print_match_expression(output, expr->right); + fprintf(output, ")"); + break; + case MATCH_EXPR_LEAF: + print_match_operation(output, expr->match); + break; + default: +/* + fprintf(output, "ERROR PRINTING MATCH: unknown op: %u\n", expr->op); + exit(1); +*/ + fprintf(output, "("); +if (expr->left) { + print_match_expression(output, expr->left); +} + fprintf(output, " ? "); +if (expr->right) { + print_match_expression(output, expr->right); +} + fprintf(output, ") _"); +if (expr->match) { + print_match_operation(output, expr->match); +} +fprintf(output, "_"); + } + } else { + printf("(nil)"); + } +} + +static void +print_counters(FILE *output, match_counters *counters, bool show_percentages, size_t total, int count_minimum) +{ + double percentage; + + if (!counters || !output) { + return; + } + + if (counters->left) { + print_counters(output, counters->left, show_percentages, total, count_minimum); + } + if (counters->match) { + if (count_minimum < (int) counters->match->count) { + print_match_expression(output, counters->match); + printf(": %u", (unsigned int) counters->match->count); + if (show_percentages) { + percentage = (double) counters->match->count / (double) total * 100.0; + printf(" (%.2f%%)", percentage); + } + printf("\n"); + } + } + if (counters->right) { + print_counters(output, counters->right, show_percentages, total, count_minimum); + } + + return; +} + +static void +ldns_pkt2file_hex(FILE *fp, const ldns_pkt *pkt) +{ + uint8_t *wire; + size_t size, i; + ldns_status status; + + status = ldns_pkt2wire(&wire, pkt, &size); + + if (status != LDNS_STATUS_OK) { + fprintf(stderr, "Unable to convert packet: error code %u", status); + return; + } + + fprintf(fp, "; 0"); + for (i = 1; i < 20; i++) { + fprintf(fp, " %2u", (unsigned int) i); + } + fprintf(fp, "\n"); + fprintf(fp, ";--"); + for (i = 1; i < 20; i++) { + fprintf(fp, " --"); + } + fprintf(fp, "\n"); + for (i = 0; i < size; i++) { + if (i % 20 == 0 && i > 0) { + fprintf(fp, "\t; %4u-%4u\n", (unsigned int) i-19, (unsigned int) i); + } + fprintf(fp, " %02x", (unsigned int)wire[i]); + } + fprintf(fp, "\n\n"); +} + +/* + * Calculate the total for all match operations with the same id as this one + * (if they are 'under' this one in the tree, which should be the case in + * the unique counter tree + */ +static size_t +calculate_total_value(match_counters *counters, match_operation *cur) +{ + size_t result = 0; + + if (!counters) { + return 0; + } + + if (counters->match->match->id == cur->id) { + result = (size_t) atol(counters->match->match->value) * counters->match->count; + } + + if (counters->left) { + result += calculate_total_value(counters->left, cur); + } + if (counters->right) { + result += calculate_total_value(counters->right, cur); + } + + return result; +} + +static size_t +calculate_total_count_matches(match_counters *counters, match_operation *cur) +{ + size_t result = 0; + + if (!counters) { + return 0; + } + + if (counters->match->match->id == cur->id) { + result = 1; + } + + if (counters->left) { + /* In some cases, you don't want the number of actual + counted matches, for instance when calculating the + average number of queries per second. In this case + you want the number of seconds */ + if (cur->id == MATCH_TIMESTAMP) { + result += (size_t) abs((int) (atol(counters->match->match->value) - atol(counters->left->match->match->value))) - 1; + } + result += calculate_total_count_matches(counters->left, cur); + } + if (counters->right) { + if (cur->id == MATCH_TIMESTAMP) { + result += (size_t) abs((int) (atol(counters->right->match->match->value) - atol(counters->match->match->value))) - 1; + } + result += calculate_total_count_matches(counters->right, cur); + } + + return result; +} + +/** + * Returns true if there is a previous match operation with the given type + * in the counters structure + */ +static bool +has_previous_match(match_counters *counters, match_operation *cur) +{ + if (!counters) { + return false; + } + + if (counters->left) { + if (counters->left->match->match->id == cur->id) { + return true; + } else if (has_previous_match(counters->left, cur)) { + return true; + } else if (counters->left->right) { + if (counters->left->right->match->match->id == cur->id) { + return true; + } else if (has_previous_match(counters->left->right, cur)) { + return true; + } + } + } + return false; +} + +/** + * Returns true if there is a later match operation with the given type + * in the counters structure + */ +static bool +has_next_match(match_counters *counters, match_operation *cur) +{ + if (!counters) { + return false; + } + + if (counters->right) { + if (counters->right->match->match->id == cur->id) { + return true; + } else if (has_next_match(counters->right, cur)) { + return true; + } else if (counters->right->left) { + if (counters->right->left->match->match->id == cur->id) { + return true; + } else if (has_next_match(counters->right->left, cur)) { + return true; + } + } + } + return false; +} + +/** + * Returns the first match with the same type at *cur in + * the counter list, or NULL if it is not found + */ +static match_expression * +get_first_match_expression(match_counters *counters, match_operation *cur) +{ + if (!counters) { + return NULL; + } + + if (has_previous_match(counters, cur)) { + return get_first_match_expression(counters->left, cur); + } else if (counters->match->match->id == cur->id) { + return counters->match; + } else if (counters->right) { + return get_first_match_expression(counters->right, cur); + } else { + return NULL; + } +} + +/** + * Returns the second match expression with the same type at *cur in + * the counter list, or NULL if it is not found + */ +static match_expression * +get_second_match_expression(match_counters *counters, match_operation *cur) +{ + if (!counters) { + return NULL; + } + + if (has_previous_match(counters, cur)) { + if (has_previous_match(counters->left, cur)) { + return get_second_match_expression(counters->left, cur); + } else { + return counters->left->match; + } +/* + } else if (counters->match->match->id == cur->id) { + return counters->match->match->value; +*/ } else if (counters->right) { + return get_first_match_expression(counters->right, cur); + } else { + return NULL; + } +} + +/** + * Returns the last match expression with the same type at *cur in + * the counter list, or NULL if it is not found + */ +static match_expression * +get_last_match_expression(match_counters *counters, match_operation *cur) +{ + if (!counters) { + return NULL; + } + + if (has_next_match(counters, cur)) { + return get_last_match_expression(counters->right, cur); + } else if (counters->match->match->id == cur->id) { + return counters->match; + } else if (counters->left) { + return get_last_match_expression(counters->left, cur); + } else { + return NULL; + } +} + +/** + * Returns the last but one match expression with the same type at *cur in + * the counter list, or NULL if it is not found + */ +static match_expression * +get_last_but_one_match_expression(match_counters *counters, match_operation *cur) +{ + if (!counters) { + return NULL; + } + + if (has_next_match(counters, cur)) { + if (has_next_match(counters->right, cur)) { + return get_last_but_one_match_expression(counters->right, cur); + } else { + return counters->match; + } +/* + } else if (counters->match->match->id == cur->id) { + return counters->match->match->value; +*/ } else if (counters->left) { + return get_last_match_expression(counters->right, cur); + } else { + return NULL; + } +} + +static size_t +get_first_count(match_counters *counters, match_operation *cur) +{ + match_expression *o = get_first_match_expression(counters, cur); + if (o) { + return o->count; + } else { + return 0; + } +} + +static size_t +get_last_count(match_counters *counters, match_operation *cur) +{ + match_expression *o = get_last_match_expression(counters, cur); + if (o) { + return o->count; + } else { + return 0; + } +} + + +static size_t +calculate_total_count(match_counters *counters, match_operation *cur) +{ + size_t result = 0; + + if (!counters) { + return 0; + } + + if (counters->match->match->id == cur->id) { + result = counters->match->count; + } + + if (counters->left) { + result += calculate_total_count(counters->left, cur); + } + if (counters->right) { + result += calculate_total_count(counters->right, cur); + } + + return result; +} + +static void +print_counter_averages(FILE *output, match_counters *counters, match_operation *cur) +{ + size_t total_value; + size_t total_count; + match_table *mt; + + if (!counters || !output) { + return; + } + + if (!cur) { + cur = counters->match->match; + mt = get_match_by_id(cur->id); + total_value = calculate_total_value(counters, cur); + total_count = calculate_total_count(counters, cur); + printf("Average for %s: (%u / %u) %.02f\n", mt->name, (unsigned int) total_value, (unsigned int) total_count, (float) total_value / (float) total_count); + if (counters->left) { + print_counter_averages(output, counters->left, cur); + } + if (counters->right) { + print_counter_averages(output, counters->right, cur); + } + } else { + if (counters->left) { + if (counters->left->match->match->id != cur->id) { + print_counter_averages(output, counters->left, NULL); + } + } + if (counters->right) { + if (counters->right->match->match->id != cur->id) { + print_counter_averages(output, counters->right, NULL); + } + } + } + + return; +} + +static void +print_counter_average_count(FILE *output, match_counters *counters, match_operation *cur, bool remove_first_last) +{ + size_t total_matches; + size_t total_count; + match_table *mt; + + if (!counters || !output) { + return; + } + + if (!cur) { + cur = counters->match->match; + mt = get_match_by_id(cur->id); + total_matches = calculate_total_count_matches(counters, cur); + total_count = calculate_total_count(counters, cur); + /* Remove the first and last for instance for timestamp average counts (half seconds drag down the average) */ + if (remove_first_last) { + total_count -= get_first_count(counters, cur); + total_count -= get_last_count(counters, cur); + printf("Removing first count from average: %u\n", (unsigned int) get_first_count(counters,cur)); + printf("Removing last count from average: %u\n", (unsigned int) get_last_count(counters,cur)); + /* in the case where we count the differences between match values too + * (like with timestamps) we need to subtract from the match count too + */ + if (cur->id == MATCH_TIMESTAMP) { + if (get_first_match_expression(counters, cur) && get_second_match_expression(counters, cur)) { + total_matches -= atol(get_second_match_expression(counters, cur)->match->value) - atol(get_first_match_expression(counters, cur)->match->value); + } + if (get_last_match_expression(counters, cur) && get_last_but_one_match_expression(counters, cur)) { + total_matches -= atol(get_last_match_expression(counters, cur)->match->value) - atol(get_last_but_one_match_expression(counters, cur)->match->value); + } + } else { + total_matches -= 2; + } + } + printf("Average count for %s: (%u / %u) %.02f\n", mt->name, (unsigned int) total_count, (unsigned int) total_matches, (float) total_count / (float) total_matches); + if (counters->left) { + print_counter_averages(output, counters->left, cur); + } + if (counters->right) { + print_counter_averages(output, counters->right, cur); + } + } else { + if (counters->left) { + if (counters->left->match->match->id != cur->id) { + print_counter_averages(output, counters->left, NULL); + } + } + if (counters->right) { + if (counters->right->match->match->id != cur->id) { + print_counter_averages(output, counters->right, NULL); + } + } + } + + return; +} + +static bool +match_int(type_operator operator, + char *value, + char *mvalue) +{ + int a, b; + + if (!value || !mvalue) { + return false; + } + + a = atoi(value); + b = atoi(mvalue); + + switch (operator) { + case OP_EQUAL: + return a == b; + break; + case OP_NOTEQUAL: + return a != b; + break; + case OP_GREATER: + return a > b; + break; + case OP_LESSER: + return a < b; + break; + case OP_GREATEREQUAL: + return a >= b; + break; + case OP_LESSEREQUAL: + return a <= b; + break; + default: + fprintf(stderr, "Unknown operator: %u\n", operator); + exit(2); + } +} + +static bool +match_opcode(type_operator operator, + char *value, + char *mvalue) +{ + ldns_pkt_opcode a, b; + int i; + ldns_lookup_table *lt; + + /* try parse name first, then parse as int */ + lt = ldns_lookup_by_name(ldns_opcodes, value); + if (lt) { + a = lt->id; + } else { + i = atoi(value); + if (i >= 0 && isdigit((unsigned char)value[0])) { + lt = ldns_lookup_by_id(ldns_opcodes, i); + if (lt) { + a = lt->id; + } else { + fprintf(stderr, "Unknown opcode: %s\n", value); + exit(1); + return false; + } + } else { + fprintf(stderr, "Unknown opcode: %s\n", value); + exit(1); + return false; + } + } + + lt = ldns_lookup_by_name(ldns_opcodes, mvalue); + if (lt) { + b = lt->id; + } else { + i = atoi(mvalue); + if (i >= 0 && isdigit((unsigned char)mvalue[0])) { + lt = ldns_lookup_by_id(ldns_opcodes, i); + if (lt) { + b = lt->id; + } else { + fprintf(stderr, "Unknown opcode: %s\n", mvalue); + exit(1); + return false; + } + } else { + fprintf(stderr, "Unknown opcode: %s\n", mvalue); + exit(1); + return false; + } + } + + switch(operator) { + case OP_EQUAL: + return a == b; + break; + case OP_NOTEQUAL: + return a != b; + break; + default: + fprintf(stderr, "Error bad operator for opcode: %s\n", get_op_str(operator)); + return false; + break; + } +} + +static bool +match_str(type_operator operator, + char *value, + char *mvalue) +{ + char *valuedup, *mvaluedup; + size_t i; + bool result; + + if (operator == OP_CONTAINS) { + /* strcasestr is not C89 + return strcasestr(value, mvalue) != 0; + */ + valuedup = strdup(value); + mvaluedup = strdup(mvalue); + for (i = 0; i < strlen(valuedup); i++) { + valuedup[i] = tolower(valuedup[i]); + } + for (i = 0; i < strlen(mvaluedup); i++) { + mvaluedup[i] = tolower(mvaluedup[i]); + } + result = strstr(valuedup, mvaluedup) != 0; + free(valuedup); + free(mvaluedup); + return result; + } else if (operator == OP_EQUAL) { + return strcmp(value, mvalue) == 0; + } else { + return strcmp(value, mvalue) != 0; + } +} + +static bool +match_rr_type(type_operator operator, + char *value, + char *mvalue) +{ + ldns_rr_type a,b; + + a = ldns_get_rr_type_by_name(value); + b = ldns_get_rr_type_by_name(mvalue); + + switch (operator) { + case OP_EQUAL: + return a == b; + break; + case OP_NOTEQUAL: + return a != b; + break; + case OP_GREATER: + return a > b; + break; + case OP_LESSER: + return a < b; + break; + case OP_GREATEREQUAL: + return a >= b; + break; + case OP_LESSEREQUAL: + return a <= b; + break; + default: + fprintf(stderr, "Unknown operator: %u\n", operator); + exit(2); + } +} + +static bool +match_rcode(type_operator operator, + char *value, + char *mvalue) +{ + int a, b; + int i; + ldns_lookup_table *lt; + + /* try parse name first, then parse as int */ + lt = ldns_lookup_by_name(ldns_rcodes, value); + if (lt) { + a = lt->id; + } else { + i = atoi(value); + if (i >= 0 && isdigit((unsigned char)value[0])) { + lt = ldns_lookup_by_id(ldns_rcodes, i); + if (lt) { + a = lt->id; + } else { + fprintf(stderr, "Unknown rcode: %s\n", value); + exit(1); + return false; + } + } else { + fprintf(stderr, "Unknown rcode: %s\n", value); + exit(1); + return false; + } + } + + lt = ldns_lookup_by_name(ldns_rcodes, mvalue); + if (lt) { + b = lt->id; + } else { + i = atoi(mvalue); + if (i >= 0 && isdigit((unsigned char)mvalue[0])) { + lt = ldns_lookup_by_id(ldns_rcodes, i); + if (lt) { + b = lt->id; + } else { + fprintf(stderr, "Unknown rcode: %s\n", mvalue); + exit(1); + return false; + } + } else { + fprintf(stderr, "Unknown rcode: %s\n", mvalue); + exit(1); + return false; + } + } + + switch(operator) { + case OP_EQUAL: + return a == b; + break; + case OP_NOTEQUAL: + return a != b; + break; + default: + fprintf(stderr, "Error bad operator for rcode: %s\n", get_op_str(operator)); + return false; + break; + } +} + +static bool +value_matches(match_id id, + type_operator operator, + char *value, + char *mvalue) +{ + int result; + + if (verbosity >= 5) { + printf("Match %s: %s %s %s: ", get_match_name_str(id), value, get_op_str(operator), mvalue); + } + switch(id) { + case MATCH_OPCODE: + result = match_opcode(operator, value, mvalue); + break; + case MATCH_RCODE: + result = match_rcode(operator, value, mvalue); + break; + case MATCH_ID: + case MATCH_QR: + case MATCH_TC: + case MATCH_AD: + case MATCH_CD: + case MATCH_RD: + case MATCH_DO: + case MATCH_PACKETSIZE: + case MATCH_EDNS: + case MATCH_EDNS_PACKETSIZE: + case MATCH_QUESTION_SIZE: + case MATCH_ANSWER_SIZE: + case MATCH_AUTHORITY_SIZE: + case MATCH_ADDITIONAL_SIZE: + case MATCH_TIMESTAMP: + result = match_int(operator, value, mvalue); + break; + case MATCH_QUERY: + case MATCH_QNAME: + case MATCH_ANSWER: + case MATCH_AUTHORITY: + case MATCH_ADDITIONAL: + result = match_str(operator, value, mvalue); + break; + case MATCH_SRC_ADDRESS: + case MATCH_DST_ADDRESS: + result = match_str(operator, value, mvalue); + break; + case MATCH_QTYPE: + result = match_rr_type(operator, value, mvalue); + break; + default: + fprintf(stderr, "Error: value_matches() for operator %s not implemented yet.\n", get_op_str((type_operator) id)); + exit(3); + } + if (verbosity >= 5) { + if (result) { + printf("true\n"); + } else { + printf("false\n"); + } + } + return result; +} + +static char * +get_string_value(match_id id, ldns_pkt *pkt, ldns_rdf *src_addr, ldns_rdf *dst_addr) +{ + char *val; + match_table *mt; + size_t valsize = 100; + + val = malloc(valsize); + memset(val, 0, valsize); + + switch(id) { + case MATCH_QR: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_qr(pkt)); + break; + case MATCH_ID: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_id(pkt)); + break; + case MATCH_OPCODE: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_get_opcode(pkt)); + break; + case MATCH_RCODE: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_get_rcode(pkt)); + break; + case MATCH_PACKETSIZE: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_size(pkt)); + break; + case MATCH_TC: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_tc(pkt)); + break; + case MATCH_AD: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_ad(pkt)); + break; + case MATCH_CD: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_cd(pkt)); + break; + case MATCH_RD: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_rd(pkt)); + break; + case MATCH_EDNS: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_edns(pkt)); + break; + case MATCH_EDNS_PACKETSIZE: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_edns_udp_size(pkt)); + break; + case MATCH_DO: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_edns_do(pkt)); + break; + case MATCH_QUESTION_SIZE: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_qdcount(pkt)); + break; + case MATCH_ANSWER_SIZE: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_ancount(pkt)); + break; + case MATCH_AUTHORITY_SIZE: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_nscount(pkt)); + break; + case MATCH_ADDITIONAL_SIZE: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_arcount(pkt)); + break; + case MATCH_SRC_ADDRESS: + free(val); + val = ldns_rdf2str(src_addr); + break; + case MATCH_DST_ADDRESS: + free(val); + val = ldns_rdf2str(dst_addr); + break; + case MATCH_TIMESTAMP: + snprintf(val, valsize, "%u", (unsigned int) ldns_pkt_timestamp(pkt).tv_sec); + break; + case MATCH_QUERY: + if (ldns_pkt_qdcount(pkt) > 0) { + free(val); + val = ldns_rr2str(ldns_rr_list_rr(ldns_pkt_question(pkt), 0)); + /* replace \n for nicer printing later */ + if (strchr(val, '\n')) { + *(strchr(val, '\n')) = '\0'; + } + } else { + val[0] = '\0'; + } + break; + case MATCH_QNAME: + if (ldns_pkt_qdcount(pkt) > 0) { + free(val); + val = ldns_rdf2str(ldns_rr_owner(ldns_rr_list_rr(ldns_pkt_question(pkt), 0))); + /* replace \n for nicer printing later */ + if (strchr(val, '\n')) { + *(strchr(val, '\n')) = '\0'; + } + } else { + val[0] = '\0'; + } + break; + case MATCH_QTYPE: + if (ldns_pkt_qdcount(pkt) > 0) { + free(val); + val = ldns_rr_type2str(ldns_rr_get_type(ldns_rr_list_rr(ldns_pkt_question(pkt), 0))); + } else { + val[0] = '\0'; + } + break; + case MATCH_ANSWER: + if (ldns_pkt_ancount(pkt) > 0) { + free(val); + val = ldns_rr_list2str(ldns_pkt_answer(pkt)); + } else { + val[0] = '\0'; + } + break; + case MATCH_AUTHORITY: + if (ldns_pkt_nscount(pkt) > 0) { + free(val); + val = ldns_rr_list2str(ldns_pkt_authority(pkt)); + } else { + val[0] = '\0'; + } + break; + case MATCH_ADDITIONAL: + if (ldns_pkt_arcount(pkt) > 0) { + free(val); + val = ldns_rr_list2str(ldns_pkt_additional(pkt)); + } else { + val[0] = '\0'; + } + break; + default: + mt = get_match_by_id(id); + if (!mt) { + printf("ERROR UNKNOWN MATCH_TABLE ID %u\n", id); + exit(1); + } + printf("Matcher for %s not implemented yet\n", mt->name); + exit(1); + return NULL; + } + + return val; +} + +static bool +match_packet_to_operation(ldns_pkt *pkt, ldns_rdf *src_addr, ldns_rdf *dst_addr, match_operation *operation) +{ + bool result; + char *val; + + if (!pkt || !operation) { + return false; + } else { + val = get_string_value(operation->id, pkt, src_addr, dst_addr); + if (!val) { + return false; + } + result = value_matches(operation->id, operation->operator, val, operation->value); + free(val); + return result; + } +} + +static int +match_operation_compare(const void *a, const void *b) +{ + match_operation *moa, *mob; + match_table *mt; + long ia, ib; + + if (!a) { + return 1; + } else if (!b) { + return -1; + } else { + moa = (match_operation *) a; + mob = (match_operation *) b; + + if (moa->id < mob->id) { + return -1; + } else if (moa->id > mob->id) { + return 1; + } else { + if (moa->operator < mob->operator) { + return -1; + } else if (moa->operator > mob->operator) { + return 1; + } else { + mt = get_match_by_id(moa->id); + if (mt) { + switch (mt->type) { + case TYPE_INT: + case TYPE_TIMESTAMP: + case TYPE_BOOL: + case TYPE_OPCODE: + case TYPE_RCODE: + ia = atol(moa->value); + ib = atol(mob->value); + return ia - ib; + break; + case TYPE_STRING: + case TYPE_ADDRESS: + case TYPE_RR: + default: + return strcmp(moa->value, mob->value); + break; + } + } else { + return strcmp(moa->value, mob->value); + } + } + } + } +} + +static int +match_expression_compare(const void *a, const void *b) +{ + match_expression *mea, *meb; + + if (!a) { + return 1; + } else if (!b) { + return -1; + } else { + mea = (match_expression *) a; + meb = (match_expression *) b; + + if (mea->op < meb->op) { + return -1; + } else if (mea->op > meb->op) { + return 1; + } else { + switch(mea->op) { + case MATCH_EXPR_AND: + case MATCH_EXPR_OR: + if (match_expression_compare(mea->left, meb->left) < 0) { + return -1; + } else if (match_expression_compare(mea->left, meb->left) > 0) { + return 1; + } else { + return match_expression_compare(mea->right, meb->right); + } + break; + case MATCH_EXPR_LEAF: + return match_operation_compare(mea->match, meb->match); + break; + default: + fprintf(stderr, "Unknown Match Expression logic operator: %u\n", mea->op); + exit(1); + } + } + } +} + +/** + * If count is true, and the counter is found, its count is increased by 1 + */ +static int +add_match_counter(match_counters *counters, + match_expression *expr, + bool count) +{ + int cmp; + match_counters *new; + + if (!counters || !expr) { + return -1; + } else { + if (counters->match) { + cmp = match_expression_compare(counters->match, + expr); + if (cmp > 0) { + if (counters->left) { + return add_match_counter(counters->left, + expr, + count); + } else { + new = malloc(sizeof(match_counters)); + new->left = NULL; + new->right = NULL; + new->match = expr; + counters->left = new; + return 0; + } + } else if (cmp < 0) { + if (counters->right) { + return add_match_counter(counters->right, + expr, + count); + } else { + new = malloc(sizeof(match_counters)); + new->left = NULL; + new->right = NULL; + new->match = expr; + counters->right = new; + return 0; + } + } else { + /* already there? */ + if (count) { + counters->match->count++; + } + return 1; + } + } else { + /* shouldn't happen but anyway */ + counters->match = expr; + } + } + return 0; +} + +static bool +match_dns_packet_to_expr(ldns_pkt *pkt, ldns_rdf *src_addr, ldns_rdf *dst_addr, match_expression *expr) +{ + bool result; + + if (!pkt || !expr) { + return false; + } + + switch(expr->op) { + case MATCH_EXPR_OR: + result = (match_dns_packet_to_expr(pkt, src_addr, dst_addr, expr->left) || + match_dns_packet_to_expr(pkt, src_addr, dst_addr, expr->right)); + break; + case MATCH_EXPR_AND: + result = (match_dns_packet_to_expr(pkt, src_addr, dst_addr, expr->left) && + match_dns_packet_to_expr(pkt, src_addr, dst_addr, expr->right)); + break; + case MATCH_EXPR_LEAF: + result = match_packet_to_operation(pkt, src_addr, dst_addr, expr->match); + break; + default: + fprintf(stderr, "Error, unknown expression operator %u\n", expr->op); + fprintf(stderr, "full expression:\n"); + print_match_expression(stderr, expr); + fprintf(stderr, "\n"); + exit(1); + } + + if (result) { + if (verbosity >= 5) { + printf("Found Match:\n"); + print_match_expression(stdout, expr); + printf("\nCount now %u\n", (unsigned int) expr->count); + } + expr->count++; + } + + return result; +} + +static void +free_match_operation(match_operation *operation) +{ + if (operation) { + if (operation->value) { + free(operation->value); + } + free(operation); + } +} + +static void +free_match_expression(match_expression *expr) +{ + if (expr) { + switch(expr->op) { + case MATCH_EXPR_OR: + case MATCH_EXPR_AND: + free_match_expression(expr->left); + free_match_expression(expr->right); + break; + case MATCH_EXPR_LEAF: + free_match_operation(expr->match); + break; + } + free(expr); + } +} + +static void +free_counters(match_counters *counters) +{ + if (counters) { + if (counters->left) { + free_counters(counters->left); + } + if (counters->match) { + free_match_expression(counters->match); + } + if (counters->right) { + free_counters(counters->right); + } + free(counters); + } +} + +static void +match_pkt_counters(ldns_pkt *pkt, ldns_rdf *src_addr, ldns_rdf *dst_addr, match_counters *counts) +{ + if (counts->left) { + match_pkt_counters(pkt, src_addr, dst_addr, counts->left); + } + if (counts->match) { + if (match_dns_packet_to_expr(pkt, src_addr, dst_addr, counts->match)) { +/* + counts->match->count++; +*/ + } + } + if (counts->right) { + match_pkt_counters(pkt, src_addr, dst_addr, counts->right); + } +} + +static void +match_pkt_uniques(ldns_pkt *pkt, ldns_rdf *src_addr, ldns_rdf *dst_addr, match_counters *uniques, match_id unique_ids[], size_t unique_id_count) +{ + match_expression *me; + size_t i; + match_operation *mo; + int add_result; + + for (i = 0; i < unique_id_count; i++) { + mo = malloc(sizeof(match_operation)); + mo->id = unique_ids[i]; + mo->operator = OP_EQUAL; + mo->value = get_string_value(mo->id, pkt, src_addr, dst_addr); + + me = malloc(sizeof(match_expression)); + me->op = MATCH_EXPR_LEAF; + me->left = NULL; + me->right = NULL; + me->match = mo; + me->count = 1; + + add_result = add_match_counter(uniques, me, true); + /* if result=1 it was already found, so delete new one */ + if (add_result == 1) { + free_match_expression(me); + } + } + +#if 0 + size_t i, j; + bool found; + match_expression *me; + match_operation *mo; + + /* get the value, match uniques for that, if not match, add new */ + /* all unique values should be MATCH_EXPR_LEAF */ + found = false; + for (j = 0; j < uniques->size; j++) { + if (uniques->counter[j]->match->id == unique_ids[i]) { + if (match_dns_packet_to_expr(pkt, src_addr, dst_addr, uniques->counter[j])) { + found = true; + } + } + } + if (!found) { + mo = malloc(sizeof(match_operation)); + mo->id = unique_ids[i]; + mo->operator = OP_EQUAL; + mo->value = get_string_value(mo->id, pkt, src_addr, dst_addr); + + me = malloc(sizeof(match_expression)); + me->match = mo; + me->op = MATCH_EXPR_LEAF; + me->left = NULL; + me->right = NULL; + me->count = 1; + + add_counter(uniques, me); + } + } +#endif +} + +static match_expression * +parse_match_expression(char *string) +{ + match_expression *expr; + size_t i,j; + size_t leftstart, leftend = 0; + char *left_str, *op, *val; + match_table *mt; + match_operation *mo = NULL; + const type_operators *tos; + match_expression *result; + ldns_lookup_table *lt = NULL; + + /* remove whitespace */ + char *str = malloc(strlen(string) + 1); + + j = 0; + for (i = 0; i < strlen(string); i++) { + if(!isspace((unsigned char)string[i])) { + str[j] = string[i]; + j++; + } + } + str[j] = '\0'; + + expr = malloc(sizeof(match_expression)); + expr->left = NULL; + expr->right = NULL; + expr->match = NULL; + expr->count = 0; + leftstart = 0; + for (i = 0; i < strlen(str); i++) { + if (str[i] == '&') { + expr->op = MATCH_EXPR_AND; + if (!expr->left) { + left_str = malloc(leftend - leftstart + 2); + strncpy(left_str, &str[leftstart], leftend-leftstart+1); + left_str[leftend - leftstart + 1] = '\0'; + expr->left = parse_match_expression(left_str); + free(left_str); + } + expr->right = parse_match_expression(&str[i+1]); + if (expr->left && expr->right) { + result = expr; + goto done; + } else { + result = NULL; + goto done; + } + } else if (str[i] == '|') { + expr->op = MATCH_EXPR_OR; + if (!expr->left) { + left_str = malloc(leftend - leftstart + 2); + strncpy(left_str, &str[leftstart], leftend-leftstart+1); + left_str[leftend - leftstart + 1] = '\0'; + expr->left = parse_match_expression(left_str); + free(left_str); + } + expr->right = parse_match_expression(&str[i+1]); + expr->count = 0; + if (expr->left && expr->right) { + result = expr; + goto done; + } else { + result = NULL; + goto done; + } + } else if (str[i] == '(') { + leftstart = i + 1; + j = 1; + while (j > 0) { + i++; + if (i > strlen(str)) { + printf("parse error: no closing bracket: %s\n", str); + printf(" "); + for (j = 0; j < leftstart - 1; j++) { + printf(" "); + } + printf("^\n"); + result = NULL; + goto done; + } + if (str[i] == ')') { + j--; + } else if (str[i] == '(') { + j++; + } else { + } + } + leftend = i-1; + left_str = malloc(leftend - leftstart + 1); + strncpy(left_str, &str[leftstart], leftend - leftstart + 1); + expr->left = parse_match_expression(left_str); + free(left_str); + if (i >= strlen(str)-1) { + result = expr->left; + goto done; + } + } else if (str[i] == ')') { + printf("parse error: ) without (\n"); + result = NULL; + goto done; + } else { + leftend = i; + } + } + + /* no operators or hooks left, expr should be of the form + <name><operator><value> now */ + for (i = 0; i < strlen(str); i++) { + if (str[i] == '=' || + str[i] == '>' || + str[i] == '<' || + str[i] == '!' || + str[i] == '~' + ) { + leftend = i-1; + op = malloc(3); + j = 0; + op[j] = str[i]; + i++; + j++; + + if (i > strlen(str)) { + printf("parse error no right hand side: %s\n", str); + result = NULL; + goto done; + } + if (str[i] == '=' || + str[i] == '>' || + str[i] == '<' || + str[i] == '!' || + str[i] == '~' + ) { + op[j] = str[i]; + i++; + j++; + if (i > strlen(str)) { + printf("parse error no right hand side: %s\n", str); + result = NULL; + goto done; + } + } + op[j] = '\0'; + left_str = malloc(leftend - leftstart + 2); + strncpy(left_str, &str[leftstart], leftend - leftstart + 1); + left_str[leftend - leftstart + 1] = '\0'; + mt = get_match_by_name(left_str); + if (!mt) { + printf("parse error: unknown match name: %s\n", left_str); + result = NULL; + goto done; + } else { + /* check if operator is allowed */ + tos = get_type_operators(mt->type); + for (j = 0; j < tos->operator_count; j++) { + if (get_op_id(op) == tos->operators[j]) { + mo = malloc(sizeof(match_operation)); + mo->id = mt->id; + mo->operator = get_op_id(op); + switch (mt->type) { + case TYPE_BOOL: + val = malloc(2); + if (strncmp(&str[i], "true", 5) == 0 || + strncmp(&str[i], "TRUE", 5) == 0 || + strncmp(&str[i], "True", 5) == 0 || + strncmp(&str[i], "1", 2) == 0 + ) { + val[0] = '1'; + val[1] = '\0'; + } else if (strncmp(&str[i], "false", 5) == 0 || + strncmp(&str[i], "FALSE", 5) == 0 || + strncmp(&str[i], "False", 5) == 0 || + strncmp(&str[i], "0", 2) == 0 + ) { + + val[0] = '0'; + } else { + fprintf(stderr, "Bad value for bool: %s\n", &str[i]); + exit(EXIT_FAILURE); + } + val[1] = '\0'; + break; + case TYPE_RR: + /* convert first so we have the same strings for the same rrs in match_ later */ + /* + qrr = ldns_rr_new_frm_str(&str[i], LDNS_DEFAULT_TTL, NULL); + if (!qrr) { + fprintf(stderr, "Bad value for RR: %s\n", &str[i]); + exit(EXIT_FAILURE); + } + val = ldns_rr2str(qrr); + */ + /* remove \n for readability */ + /* + if (strchr(val, '\n')) { + *(strchr(val, '\n')) = '\0'; + } + ldns_rr_free(qrr); + */ + val = strdup(&str[i]); + break; + case TYPE_OPCODE: + lt = ldns_lookup_by_name(ldns_opcodes, &str[i]); + if (lt) { + val = malloc(4); + snprintf(val, 3, "%u", (unsigned int) lt->id); + } else { + val = strdup(&str[i]); + } + break; + case TYPE_RCODE: + lt = ldns_lookup_by_name(ldns_rcodes, &str[i]); + if (lt) { + val = malloc(4); + snprintf(val, 3, "%u", (unsigned int) lt->id); + } else { + val = strdup(&str[i]); + } + break; + default: + val = strdup(&str[i]); + break; + } + mo->value = val; + } + } + if (!mo) { + printf("parse error: operator %s not allowed for match %s\n", op, left_str); + result = NULL; + goto done; + } + } + free(left_str); + free(op); + expr->match = mo; + expr->op = MATCH_EXPR_LEAF; + result = expr; + goto done; + } + } + + result = NULL; + + done: + free(str); + if (!result) { + free_match_expression(expr); + } + return result; + +} +/* end of matches and counts */ +void +usage(FILE *output) +{ + fprintf(output, "Usage: ldns-dpa [OPTIONS] <pcap file>\n"); + fprintf(output, "Options:\n"); + fprintf(output, "\t-c <exprlist>:\tCount occurrences of matching expressions\n"); + fprintf(output, "\t-f <expression>:\tFilter occurrences of matching expressions\n"); + fprintf(output, "\t-h:\t\tshow this help\n"); + fprintf(output, "\t-p:\t\tshow percentage of -u and -c values (of the total of\n\t\t\tmatching on the -f filter. if no filter is given,\n\t\t\tpercentages are on all correct dns packets)\n"); + fprintf(output, "\t-of <file>:\tWrite pcap packets that match the -f flag to file\n"); + fprintf(output, "\t-ofh <file>:\tWrite pcap packets that match the -f flag to file\n\t\tin a hexadecimal format readable by drill\n"); + fprintf(output, "\t-s:\t\tshow possible match names\n"); + fprintf(output, "\t-s <matchname>:\tshow possible match operators and values for <name>\n"); + fprintf(output, "\t-sf:\t\tPrint packet that match -f. If no -f is given, print\n\t\t\tall dns packets\n"); + fprintf(output, "\t-u <matchnamelist>:\tCount all occurrences of matchname\n"); + fprintf(output, "\t-ua:\t\tShow average value of every -u matchname\n"); + fprintf(output, "\t-uac:\t\tShow average count of every -u matchname\n"); + fprintf(output, "\t-um <number>:\tOnly show -u results that occured more than number times\n"); + fprintf(output, "\t-v <level>:\tbe more verbose\n"); + fprintf(output, "\t-notip <file>:\tDump pcap packets that were not recognized as\n\t\t\tIP packets to file\n"); + fprintf(output, "\t-baddns <file>:\tDump mangled dns packets to file\n"); + fprintf(output, "\t-version:\tShow the version and exit\n"); + fprintf(output, "\n"); + fprintf(output, "The filename '-' stands for stdin or stdout, so you can use \"-of -\" if you want to pipe the output to another process\n"); + fprintf(output, "\n"); + fprintf(output, "A <list> is a comma separated list of items\n"); + fprintf(output, "\n"); + fprintf(output, "An expression has the following form:\n"); + fprintf(output, "<expr>:\t(<expr>)\n"); + fprintf(output, "\t<expr> | <expr>\n"); + fprintf(output, "\t<expr> & <expr>\n"); + fprintf(output, "\t<match>\n"); + fprintf(output, "\n"); + fprintf(output, "<match>:\t<matchname> <operator> <value>\n"); + fprintf(output, "\n"); + fprintf(output, "See the -s option for possible matchnames, operators and values.\n"); +} + +void +show_match_names(char *name) +{ + size_t j; + match_table *mt; + ldns_lookup_table *lt; + const type_operators *tos; + char *str; + size_t i; + + if (name) { + mt = get_match_by_name(name); + if (mt) { + printf("%s:\n", mt->name); + printf("\t%s.\n", mt->description); + printf("\toperators: "); + printf("\t"); + tos = get_type_operators(mt->type); + if (tos) { + for (j = 0; j < tos->operator_count; j++) { + printf("%s ", get_op_str(tos->operators[j])); +/* + lt = ldns_lookup_by_id((ldns_lookup_table *) lt_operators, tos->operators[j]); + if (lt) { + printf("%s ", lt->name); + } else { + printf("? "); + } +*/ + } + } else { + printf("unknown type"); + } + + printf("\n"); + printf("\tValues:\n"); + switch (mt->type) { + case TYPE_INT: + printf("\t\t<Integer>\n"); + break; + case TYPE_BOOL: + printf("\t\t0\n"); + printf("\t\t1\n"); + printf("\t\ttrue\n"); + printf("\t\tfalse\n"); + break; + case TYPE_OPCODE: + printf("\t\t<Integer>\n"); + lt = ldns_opcodes; + while (lt->name != NULL) { + printf("\t\t%s\n", lt->name); + lt++; + } + break; + case TYPE_RCODE: + printf("\t\t<Integer>\n"); + lt = ldns_rcodes; + while (lt->name != NULL) { + printf("\t\t%s\n", lt->name); + lt++; + } + break; + case TYPE_STRING: + printf("\t\t<String>\n"); + break; + case TYPE_TIMESTAMP: + printf("\t\t<Integer> (seconds since epoch)\n"); + break; + case TYPE_ADDRESS: + printf("\t\t<IP address>\n"); + break; + case TYPE_RR: + printf("\t\t<Resource Record>\n"); + break; + default: + break; + } + } else { + printf("Unknown match name: %s\n", name); + } + } else { + mt = (match_table *) matches; + while (mt->name != NULL) { + str = (char *) mt->name; + printf("%s:", str); + i = strlen(str) + 1; + while (i < 24) { + printf(" "); + i++; + } + printf("%s\n", mt->description); + mt++; + } + } +} + +int +handle_ether_packet(const u_char *data, struct pcap_pkthdr cur_hdr, match_counters *count, match_expression *match_expr, match_counters *uniques, match_id unique_ids[], size_t unique_id_count) +{ + struct ether_header *eptr; + struct ip *iptr; + struct ip6_hdr *ip6_hdr; + int ip_hdr_size; + uint8_t protocol; + size_t data_offset = 0; + ldns_rdf *src_addr, *dst_addr; + uint8_t *ap; + char *astr; + bpf_u_int32 len = cur_hdr.caplen; + struct timeval timestamp; + uint16_t ip_flags; + uint16_t ip_len; + uint16_t ip_id; + uint16_t ip_f_offset; + const u_char *newdata = NULL; +/* +printf("timeval: %u ; %u\n", cur_hdr.ts.tv_sec, cur_hdr.ts.tv_usec); +*/ + + uint8_t *dnspkt; + + ldns_pkt *pkt; + ldns_status status; + + /* lets start with the ether header... */ + eptr = (struct ether_header *) data; + /* Do a couple of checks to see what packet type we have..*/ + if (ntohs (eptr->ether_type) == ETHERTYPE_IP) + { + if (verbosity >= 5) { + printf("Ethernet type hex:%x dec:%u is an IP packet\n", + (unsigned int) ntohs(eptr->ether_type), + (unsigned int) ntohs(eptr->ether_type)); + } + + data_offset = ETHER_HEADER_LENGTH; + iptr = (struct ip *) (data + data_offset); + /* + printf("IP_OFF: %u (%04x) %04x %04x (%d) (%d)\n", iptr->ip_off, iptr->ip_off, IP_MF, IP_DF, iptr->ip_off & 0x4000, iptr->ip_off & 0x2000); + */ + ip_flags = ldns_read_uint16(&(iptr->ip_off)); + ip_id = ldns_read_uint16(&(iptr->ip_id)); + ip_len = ldns_read_uint16(&(iptr->ip_len)); + ip_f_offset = (ip_flags & IP_OFFMASK)*8; + if (ip_flags & IP_MF && ip_f_offset == 0) { + /*printf("First Frag id %u len\n", ip_id, ip_len);*/ + fragment_p->ip_id = ip_id; + memset(fragment_p->data, 0, 65535); + memcpy(fragment_p->data, iptr, ip_len); + fragment_p->cur_len = ip_len + 20; +/* + for (ip_len = 0; ip_len < fragment_p->cur_len; ip_len++) { + if (ip_len > 0 && ip_len % 20 == 0) { + printf("\t; %u - %u\n", ip_len - 19, ip_len); + } + printf("%02x ", fragment_p->data[ip_len]); + } + printf("\t; ??? - %u\n", ip_len); +*/ + return 0; + } else + if (ip_flags & IP_MF && ip_f_offset != 0) { + /*printf("Next frag\n");*/ + if (ip_id == fragment_p->ip_id) { + /*printf("add fragment to current id %u len %u offset %u\n", ip_id, ip_len, ip_f_offset);*/ + memcpy(fragment_p->data + (ip_f_offset) + 20, data+data_offset+20, ip_len - (iptr->ip_hl)*4); + /*printf("COPIED %u\n", ip_len);*/ + fragment_p->cur_len = fragment_p->cur_len + ip_len - 20; + /*printf("cur len now %u\n", fragment_p->cur_len);*/ +/* + for (ip_len = 0; ip_len < fragment_p->cur_len; ip_len++) { + if (ip_len > 0 && ip_len % 20 == 0) { + printf("\t; %u - %u\n", ip_len - 19, ip_len); + } + printf("%02x ", fragment_p->data[ip_len]); + } + printf("\t; ??? - %u\n", ip_len); +*/ + return 0; + } else { + /*printf("Lost fragment %u\n", iptr->ip_id);*/ + lost_packet_fragments++; + return 1; + } + } else + if (!(ip_flags & IP_MF) && ip_f_offset != 0) { + /*printf("Last frag\n");*/ + if (ip_id == fragment_p->ip_id) { + /*printf("add fragment to current id %u len %u offset %u\n", ip_id, ip_len, ip_f_offset);*/ + memcpy(fragment_p->data + ip_f_offset + 20, data+data_offset+20, ip_len - 20); + fragment_p->cur_len = fragment_p->cur_len + ip_len - 20; + iptr = (struct ip *) fragment_p->data; + newdata = malloc(fragment_p->cur_len + data_offset); + if (!newdata) { + printf("Malloc failed, out of mem?\n"); + exit(4); + } + memcpy((char *) newdata, data, data_offset); + memcpy((char *) newdata+data_offset, fragment_p->data, fragment_p->cur_len); + iptr->ip_len = (u_short) ldns_read_uint16(&(fragment_p->cur_len)); + iptr->ip_off = 0; + len = (bpf_u_int32) fragment_p->cur_len; + cur_hdr.caplen = len; + fragment_p->ip_id = 0; + fragmented_packets++; +/* + for (ip_len = 0; ip_len < fragment_p->cur_len; ip_len++) { + if (ip_len > 0 && ip_len % 20 == 0) { + printf("\t; %u - %u\n", ip_len - 19, ip_len); + } + printf("%02x ", fragment_p->data[ip_len]); + } + printf("\t; ??? - %u\n", ip_len); +*/ + } else { + /*printf("Lost fragment %u\n", iptr->ip_id);*/ + lost_packet_fragments++; + return 1; + } + } else { + newdata = data; + } +/* + if (iptr->ip_off & 0x0040) { + printf("Don't fragment\n"); + } +*/ + + /* in_addr portability woes, going manual for now */ + /* ipv4 */ + ap = (uint8_t *) &(iptr->ip_src); + astr = malloc(INET_ADDRSTRLEN); + if (inet_ntop(AF_INET, ap, astr, INET_ADDRSTRLEN)) { + if (ldns_str2rdf_a(&src_addr, astr) == LDNS_STATUS_OK) { + + } + free(astr); + } + ap = (uint8_t *) &(iptr->ip_dst); + astr = malloc(INET_ADDRSTRLEN); + if (inet_ntop(AF_INET, ap, astr, INET_ADDRSTRLEN)) { + if (ldns_str2rdf_a(&dst_addr, astr) == LDNS_STATUS_OK) { + + } + free(astr); + } + + ip_hdr_size = (int) iptr->ip_hl * 4; + protocol = (uint8_t) iptr->ip_p; + + data_offset += ip_hdr_size; + + if (protocol == IPPROTO_UDP) { + udp_packets++; + data_offset += UDP_HEADER_LENGTH; + + dnspkt = (uint8_t *) (newdata + data_offset); + + /*printf("packet starts at byte %u\n", data_offset);*/ + + /*printf("Len: %u\n", len);*/ + + status = ldns_wire2pkt(&pkt, dnspkt, len - data_offset); + + if (status != LDNS_STATUS_OK) { + if (verbosity >= 3) { + printf("No dns packet: %s\n", ldns_get_errorstr_by_id(status)); + } + if (verbosity >= 5) { + for (ip_len = 0; ip_len < len - data_offset; ip_len++) { + if (ip_len > 0 && ip_len % 20 == 0) { + printf("\t; %u - %u\n", (unsigned int) ip_len - 19, (unsigned int) ip_len); + } + printf("%02x ", (unsigned int) dnspkt[ip_len]); + } + printf("\t; ??? - %u\n", (unsigned int) ip_len); + + } + bad_dns_packets++; + if (bad_dns_dump) { + pcap_dump((u_char *)bad_dns_dump, &cur_hdr, newdata); + } + } else { + timestamp.tv_sec = cur_hdr.ts.tv_sec; + timestamp.tv_usec = cur_hdr.ts.tv_usec; + ldns_pkt_set_timestamp(pkt, timestamp); + + if (verbosity >= 4) { + printf("DNS packet\n"); + ldns_pkt_print(stdout, pkt); + printf("\n\n"); + } + + total_nr_of_dns_packets++; + + if (match_expr) { + if (match_dns_packet_to_expr(pkt, src_addr, dst_addr, match_expr)) { + /* if outputfile write */ + if (dumper) { + pcap_dump((u_char *)dumper, &cur_hdr, data); + } + if (hexdumpfile) { + fprintf(hexdumpfile, ";; %u\n", (unsigned int) total_nr_of_dns_packets); + ldns_pkt2file_hex(hexdumpfile, pkt); + } + if (show_filter_matches) { + printf(";; From: "); + ldns_rdf_print(stdout, src_addr); + printf("\n"); + printf(";; To: "); + ldns_rdf_print(stdout, dst_addr); + printf("\n"); + ldns_pkt_print(stdout, pkt); + printf("------------------------------------------------------------\n\n"); + } + } else { + ldns_pkt_free(pkt); + ldns_rdf_deep_free(src_addr); + ldns_rdf_deep_free(dst_addr); + return 0; + } + } else { + if (dumper) { + pcap_dump((u_char *)dumper, &cur_hdr, data); + } + if (hexdumpfile) { + fprintf(hexdumpfile, ";; %u\n", (unsigned int) total_nr_of_dns_packets); + ldns_pkt2file_hex(hexdumpfile, pkt); + } + if (show_filter_matches) { + printf(";; From: "); + ldns_rdf_print(stdout, src_addr); + printf("\n"); + printf(";; To: "); + ldns_rdf_print(stdout, dst_addr); + printf("\n"); + ldns_pkt_print(stdout, pkt); + printf("------------------------------------------------------------\n\n"); + } + } + + /* General counters here */ + total_nr_of_filtered_packets++; + + match_pkt_counters(pkt, src_addr, dst_addr, count); + match_pkt_uniques(pkt, src_addr, dst_addr, uniques, unique_ids, unique_id_count); + + ldns_pkt_free(pkt); + pkt = NULL; + } + ldns_rdf_deep_free(src_addr); + ldns_rdf_deep_free(dst_addr); + + } else if (protocol == IPPROTO_TCP) { + /* tcp packets are skipped */ + tcp_packets++; + } + /* don't have a define for ethertype ipv6 */ + } else if (ntohs (eptr->ether_type) == ETHERTYPE_IPV6) { + /*printf("IPv6!\n");*/ + + + /* copied from ipv4, move this to function? */ + + data_offset = ETHER_HEADER_LENGTH; + ip6_hdr = (struct ip6_hdr *) (data + data_offset); + + newdata = data; + + /* in_addr portability woes, going manual for now */ + /* ipv6 */ + ap = (uint8_t *) &(ip6_hdr->ip6_src); + astr = malloc(INET6_ADDRSTRLEN); + if (inet_ntop(AF_INET6, ap, astr, INET6_ADDRSTRLEN)) { + if (ldns_str2rdf_aaaa(&src_addr, astr) == LDNS_STATUS_OK) { + + } + free(astr); + } + ap = (uint8_t *) &(ip6_hdr->ip6_dst); + astr = malloc(INET6_ADDRSTRLEN); + if (inet_ntop(AF_INET6, ap, astr, INET6_ADDRSTRLEN)) { + if (ldns_str2rdf_aaaa(&dst_addr, astr) == LDNS_STATUS_OK) { + + } + free(astr); + } + + ip_hdr_size = IP6_HEADER_LENGTH; + protocol = (uint8_t) ip6_hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt; + + data_offset += ip_hdr_size; + + if (protocol == IPPROTO_UDP) { + udp_packets++; + /*printf("V6 UDP!\n");*/ + data_offset += UDP_HEADER_LENGTH; + + dnspkt = (uint8_t *) (newdata + data_offset); + + /*printf("Len: %u\n", len);*/ + + status = ldns_wire2pkt(&pkt, dnspkt, len - data_offset); + + if (status != LDNS_STATUS_OK) { + if (verbosity >= 3) { + printf("No dns packet: %s\n", ldns_get_errorstr_by_id(status)); + } + bad_dns_packets++; + if (bad_dns_dump) { + pcap_dump((u_char *)bad_dns_dump, &cur_hdr, newdata); + } + } else { + timestamp.tv_sec = cur_hdr.ts.tv_sec; + timestamp.tv_usec = cur_hdr.ts.tv_usec; + ldns_pkt_set_timestamp(pkt, timestamp); + + if (verbosity >= 4) { + printf("DNS packet\n"); + ldns_pkt_print(stdout, pkt); + printf("\n\n"); + } + + total_nr_of_dns_packets++; + + if (match_expr) { + if (match_dns_packet_to_expr(pkt, src_addr, dst_addr, match_expr)) { + /* if outputfile write */ + if (dumper) { + pcap_dump((u_char *)dumper, &cur_hdr, data); + } + if (show_filter_matches) { + printf(";; From: "); + ldns_rdf_print(stdout, src_addr); + printf("\n"); + printf(";; To: "); + ldns_rdf_print(stdout, dst_addr); + printf("\n"); + ldns_pkt_print(stdout, pkt); + printf("------------------------------------------------------------\n\n"); + } + } else { + ldns_pkt_free(pkt); + ldns_rdf_deep_free(src_addr); + ldns_rdf_deep_free(dst_addr); + return 0; + } + } else { + if (show_filter_matches) { + printf(";; From: "); + ldns_rdf_print(stdout, src_addr); + printf("\n"); + printf(";; To: "); + ldns_rdf_print(stdout, dst_addr); + printf("\n"); + ldns_pkt_print(stdout, pkt); + printf("------------------------------------------------------------\n\n"); + } + } + + /* General counters here */ + total_nr_of_filtered_packets++; + + match_pkt_counters(pkt, src_addr, dst_addr, count); + match_pkt_uniques(pkt, src_addr, dst_addr, uniques, unique_ids, unique_id_count); + + ldns_pkt_free(pkt); + pkt = NULL; + } + ldns_rdf_deep_free(src_addr); + ldns_rdf_deep_free(dst_addr); + + } else if (protocol == IPPROTO_TCP) { + /* tcp packets are skipped */ + tcp_packets++; + } else { + printf("ipv6 unknown next header type: %u\n", (unsigned int) protocol); + } + + + + } else if (ntohs (eptr->ether_type) == ETHERTYPE_ARP) { + if (verbosity >= 5) { + printf("Ethernet type hex:%x dec:%u is an ARP packet\n", + (unsigned int) ntohs(eptr->ether_type), + (unsigned int) ntohs(eptr->ether_type)); + } + arp_packets++; + } else { + printf("Ethernet type %x not IP\n", (unsigned int) ntohs(eptr->ether_type)); + if (verbosity >= 5) { + printf("Ethernet type %x not IP\n", (unsigned int) ntohs(eptr->ether_type)); + } + not_ip_packets++; + if (not_ip_dump) { + pcap_dump((u_char *)not_ip_dump, &cur_hdr, data); + } + } + + return 0; +} + +bool +parse_match_list(match_counters *counters, char *string) +{ + size_t i; + match_expression *expr; +/* match_counter *mc;*/ + size_t lastpos = 0; + char *substring; + + /*printf("Parsing match list: '%s'\n", string);*/ + + for (i = 0; i < strlen(string); i++) { + if (string[i] == ',') { + if (i<2) { + fprintf(stderr, "Matchlist cannot start with ,\n"); + return false; + } else { + substring = malloc(strlen(string)+1); + strncpy(substring, &string[lastpos], i - lastpos + 1); + substring[i - lastpos] = '\0'; + expr = parse_match_expression(substring); + if (!expr) { + return false; + } + free(substring); + /* + if (expr->op != MATCH_EXPR_LEAF) { + fprintf(stderr, "Matchlist can only contain <match>, not a logic expression\n"); + return false; + } + */ + add_match_counter(counters, expr, false); + lastpos = i+1; + } + } + } + substring = malloc(strlen(string) + 1); + strncpy(substring, &string[lastpos], i - lastpos + 1); + substring[i - lastpos] = '\0'; + expr = parse_match_expression(substring); + + if (!expr) { + fprintf(stderr, "Bad match: %s\n", substring); + return false; + } + free(substring); + /* + if (expr->op != MATCH_EXPR_LEAF) { + fprintf(stderr, "Matchlist can only contain <match>, not a logic expression\n"); + return false; + } + */ + add_match_counter(counters, expr, false); + return true; +} + +bool +parse_uniques(match_id ids[], size_t *count, char *string) +{ + size_t i, j, lastpos; + char *str, *strpart; + match_table *mt; + + /*printf("Parsing unique counts: '%s'\n", string);*/ + str = malloc(strlen(string) + 1); + j = 0; + for (i = 0; i < strlen(string); i++) { + if (!isspace((unsigned char)string[i])) { + str[j] = string[i]; + j++; + } + } + str[j] = '\0'; + + lastpos = 0; + for (i = 0; i <= strlen(str); i++) { + if (str[i] == ',' || i >= strlen(str)) { + strpart = malloc(i - lastpos + 1); + strncpy(strpart, &str[lastpos], i - lastpos); + strpart[i - lastpos] = '\0'; + if ((mt = get_match_by_name(strpart))) { + ids[*count] = mt->id; + *count = *count + 1; + } else { + printf("Error parsing match list; unknown match name: %s\n", strpart); + return false; + } + free(strpart); + lastpos = i + 1; + } + } + if (i > lastpos) { + strpart = malloc(i - lastpos + 1); + strncpy(strpart, &str[lastpos], i - lastpos); + strpart[i - lastpos] = '\0'; + if ((mt = get_match_by_name(strpart))) { + ids[*count] = mt->id; + *count = *count + 1; + } else { + printf("Error parsing match list; unknown match name: %s\n", strpart); + return false; + } + free(strpart); + lastpos = i + 1; + } + free(str); + return true; +} + +int main(int argc, char *argv[]) { + + int i; + int status = EXIT_SUCCESS; + match_counters *count = malloc(sizeof(match_counters)); + const char *inputfile = NULL; + char errbuf[PCAP_ERRBUF_SIZE]; + pcap_t *pc = NULL; + const u_char *cur; + struct pcap_pkthdr cur_hdr; + match_expression *expr = NULL; + match_id unique_ids[MAX_MATCHES]; + size_t unique_id_count = 0; /* number of unique counters */ + match_counters *uniques = malloc(sizeof(match_counters)); + char *dumpfile = NULL; + char *hexdumpfilename = NULL; + char *not_ip_dumpfile = NULL; + char *bad_dns_dumpfile = NULL; + + bool show_percentages = false; + bool show_averages = false; + bool show_average_count = false; + int unique_minimum = 0; + + count->left = NULL; + count->match = NULL; + count->right = NULL; + uniques->left = NULL; + uniques->match = NULL; + uniques->right = NULL; + + fragment_p = malloc(sizeof(struct fragment_part)); + fragment_p->ip_id = 0; + fragment_p->cur_len = 0; + + for (i = 1; i < argc; i++) { + + if (strncmp(argv[i], "-baddns", 8) == 0) { + if (i + 1 < argc) { + bad_dns_dumpfile = argv[i + 1]; + i++; + } else { + usage(stderr); + status = EXIT_FAILURE; + goto exit; + } + } else if (strncmp(argv[i], "-notip", 7) == 0) { + if (i + 1 < argc) { + not_ip_dumpfile = argv[i + 1]; + i++; + } else { + usage(stderr); + status = EXIT_FAILURE; + goto exit; + } + } else if (strncmp(argv[i], "-c", 3) == 0) { + if (i + 1 < argc) { + if (!parse_match_list(count, argv[i + 1])) { + status = EXIT_FAILURE; + goto exit; + } + i++; + } else { + usage(stderr); + status = EXIT_FAILURE; + goto exit; + } + } else if (strncmp(argv[i], "-f", 3) == 0) { + if (i + 1 < argc) { + if (expr || strchr(argv[i+1], ',')) { + fprintf(stderr, "You can only specify 1 filter expression.\n"); + status = EXIT_FAILURE; + goto exit; + } + expr = parse_match_expression(argv[i + 1]); + i++; + } else { + usage(stderr); + status = EXIT_FAILURE; + goto exit; + } + } else if (strncmp(argv[i], "-h", 3) == 0) { + usage(stdout); + status = EXIT_SUCCESS; + goto exit; + } else if (strncmp(argv[i], "-p", 3) == 0) { + show_percentages = true; + } else if (strncmp(argv[i], "-of", 4) == 0) { + if (i + 1 < argc) { + dumpfile = argv[i + 1]; + i++; + } else { + usage(stderr); + status = EXIT_FAILURE; + goto exit; + } + } else if (strncmp(argv[i], "-ofh", 5) == 0) { + if (i + 1 < argc) { + hexdumpfilename = argv[i + 1]; + i++; + } else { + usage(stderr); + status = EXIT_FAILURE; + goto exit; + } + } else if (strncmp(argv[i], "-s", 3) == 0) { + if (i + 1 < argc) { + show_match_names(argv[i + 1]); + } else { + show_match_names(NULL); + } + status = EXIT_SUCCESS; + goto exit; + } else if (strncmp(argv[i], "-sf", 4) == 0) { + show_filter_matches = true; + } else if (strncmp(argv[i], "-u", 3) == 0) { + if (i + 1 < argc) { + if (!parse_uniques(unique_ids, &unique_id_count, argv[i + 1])) { + status = EXIT_FAILURE; + goto exit; + } + i++; + } else { + usage(stderr); + status = EXIT_FAILURE; + goto exit; + } + } else if (strcmp("-ua", argv[i]) == 0) { + show_averages = true; + } else if (strcmp("-uac", argv[i]) == 0) { + show_average_count = true; + } else if (strcmp("-um", argv[i]) == 0) { + if (i + 1 < argc) { + unique_minimum = atoi(argv[i+1]); + i++; + } else { + fprintf(stderr, "-um requires an argument"); + usage(stderr); + status = EXIT_FAILURE; + goto exit; + } + } else if (strcmp("-v", argv[i]) == 0) { + i++; + if (i < argc) { + verbosity = atoi(argv[i]); + } + } else if (strcmp("-version", argv[i]) == 0) { + printf("dns packet analyzer, version %s (ldns version %s)\n", LDNS_VERSION, ldns_version()); + goto exit; + } else { + if (inputfile) { + fprintf(stderr, "You can only specify 1 input file\n"); + exit(1); + } + inputfile = argv[i]; + } + } + + if (!inputfile) { + inputfile = "-"; + } + + if (verbosity >= 5) { + printf("Filter:\n"); + print_match_expression(stdout, expr); + printf("\n\n"); + } + + pc = pcap_open_offline(inputfile, errbuf); + + if (!pc) { + if (errno != 0) { + printf("Error opening pcap file %s: %s\n", inputfile, errbuf); + exit(1); + } else { + goto showresult; + } + } + + if (dumpfile) { + dumper = pcap_dump_open(pc, dumpfile); + + if (!dumper) { + printf("Error opening pcap dump file %s: %s\n", dumpfile, errbuf); + exit(1); + } + } + + if (hexdumpfilename) { + if (strncmp(hexdumpfilename, "-", 2) != 0) { + printf("hexdump is file\n"); + hexdumpfile = fopen(hexdumpfilename, "w"); + } else { + printf("hexdump is stdout\n"); + hexdumpfile = stdout; + } + + if (!hexdumpfile) { + printf("Error opening hex dump file %s: %s\n", hexdumpfilename, strerror(errno)); + exit(1); + } + } + + if (not_ip_dumpfile) { + not_ip_dump = pcap_dump_open(pc, not_ip_dumpfile); + if (!not_ip_dump) { + printf("Error opening pcap dump file NOT_IP: %s\n", errbuf); + } + } + if (bad_dns_dumpfile) { + bad_dns_dump = pcap_dump_open(pc, bad_dns_dumpfile); + if (!bad_dns_dump) { + printf("Error opening pcap dump file NOT_IP: %s\n", errbuf); + } + } + + while ((cur = pcap_next(pc, &cur_hdr))) { + if (verbosity >= 5) { + printf("\n\n\n[PKT_HDR] caplen: %u \tlen: %u\n", (unsigned int)cur_hdr.caplen, (unsigned int)cur_hdr.len); + } + handle_ether_packet(cur, cur_hdr, count, expr, uniques, unique_ids, unique_id_count); + } + + if (not_ip_dump) { + pcap_dump_close(not_ip_dump); + } + + if (bad_dns_dump) { + pcap_dump_close(bad_dns_dump); + } + + if (dumper) { + pcap_dump_close(dumper); + } + + if (hexdumpfile && hexdumpfile != stdout) { + fclose(hexdumpfile); + } + + pcap_close(pc); + + showresult: + if (show_percentages) { + fprintf(stdout, "Packets that are not IP: %u\n", (unsigned int) not_ip_packets); + fprintf(stdout, "bad dns packets: %u\n", (unsigned int) bad_dns_packets); + fprintf(stdout, "arp packets: %u\n", (unsigned int) arp_packets); + fprintf(stdout, "udp packets: %u\n", (unsigned int) udp_packets); + fprintf(stdout, "tcp packets (skipped): %u\n", (unsigned int) tcp_packets); + fprintf(stdout, "reassembled fragmented packets: %u\n", (unsigned int) fragmented_packets); + fprintf(stdout, "packet fragments lost: %u\n", (unsigned int) lost_packet_fragments); + fprintf(stdout, "Total number of DNS packets: %u\n", (unsigned int) total_nr_of_dns_packets); + fprintf(stdout, "Total number of DNS packets after filter: %u\n", (unsigned int) total_nr_of_filtered_packets); + } + if (count->match) { + print_counters(stdout, count, show_percentages, total_nr_of_filtered_packets, 0); + } + if (uniques->match) { + print_counters(stdout, uniques, show_percentages, total_nr_of_filtered_packets, unique_minimum); + if (show_averages) { + print_counter_averages(stdout, uniques, NULL); + } + if (show_average_count) { + print_counter_average_count(stdout, uniques, NULL, true); + } + } + + exit: + + free_match_expression(expr); + free_counters(count); + free_counters(uniques); + + return status; +} + +#else +int main(void) { + fprintf(stderr, "ldns-dpa was not built because there is no pcap library on this system, or there was no pcap header file at compilation time. Please install pcap and rebuild.\n"); + return 1; +} +#endif +#else +int main(void) { + fprintf(stderr, "ldns-dpa was not built because there is no pcap library on this system, or there was no pcap header file at compilation time. Please install pcap and rebuild.\n"); + return 1; +} +#endif + + |