struct lval* lval_read(mpc_ast_t* node) { // Upwrap top-level form as a single expression if (strcmp(node->tag, ">") == 0) { return lval_read(node->children[1]); } if (strstr(node->tag, "number")) { return lval_read_num(node->contents); } if (strstr(node->tag, "symbol")) { return lval_sym(node->contents); } if (strstr(node->tag, "bool")) { return lval_read_bool(node->contents); } struct lval* x; if (strstr(node->tag, "sexp")) { x = lval_sexp(); } else if (strstr(node->tag, "qexp")) { x = lval_qexp(); } else { return lval_err("Unexpected node: %s", node->tag); } for (int i = 0; i < node->children_num; i++) { if (read_ignore(node->children[i])) continue; x = lval_add(x, lval_read(node->children[i])); } return x; }
void drain_excessive_data() { // The pipe seems to have data on it, that won't go away // Read a big chunk out of it. // We don't read until it's exhausted, because if someone were to pipe say /dev/null, that would cause us to hang! size_t read_amt = 64 * 1024; void *buff = malloc(read_amt); read_ignore(this->pipe_fd, buff, read_amt); free(buff); }
bool poll() { bool result = false; // Check if we are past the readback time if (this->readback_time_usec > 0 && get_time() >= this->readback_time_usec) { // Read back what we wrote. We do nothing with the value. while (this->readback_amount > 0) { char buff[64]; size_t amt_to_read = mini(this->readback_amount, sizeof buff); read_ignore(this->pipe_fd, buff, amt_to_read); this->readback_amount -= amt_to_read; } assert(this->readback_amount == 0); this->readback_time_usec = 0; } // Check to see if we are doing readability polling if (polling_due_to_readable_fd && pipe_fd >= 0) { // We are polling, so we are definitely going to sync result = true; // See if this is still readable fd_set fds; FD_ZERO(&fds); FD_SET(this->pipe_fd, &fds); struct timeval timeout = {}; select(this->pipe_fd + 1, &fds, NULL, NULL, &timeout); if (! FD_ISSET(this->pipe_fd, &fds)) { // No longer readable, no longer polling polling_due_to_readable_fd = false; drain_if_still_readable_time_usec = 0; } else { // Still readable. If it's been readable for a long time, there is probably lingering data on the pipe if (get_time() >= drain_if_still_readable_time_usec) { drain_excessive_data(); } } } return result; }
int main(int argc, char **argv) { pcap_t * pcap_file; char errbuf[PCAP_ERRBUF_SIZE]; int read; config conf; int c; int arg_failure = 0; const char * OPTIONS = "cdfhi:m:MnrtD:x:s:S"; // Setting configuration defaults. uint8_t TCP_SAVE_STATE = 1; conf.COUNTS = 0; conf.EXCLUDES = 0; conf.RECORD_SEP = ""; conf.SEP = '\t'; conf.AD_ENABLED = 0; conf.NS_ENABLED = 0; conf.PRETTY_DATE = 0; conf.PRINT_RR_NAME = 0; conf.MISSING_TYPE_WARNINGS = 0; conf.TCP_STATE_PATH = NULL; conf.DEDUPS = 10; conf.dedup_pos = 0; conf.IGNORE_DOMAINS = NULL; c = getopt(argc, argv, OPTIONS); while (c != -1) { switch (c) { case 'c': conf.COUNTS = 1; break; case 'd': conf.AD_ENABLED = 1; break; case 'f': print_parsers(); return 0; case 'm': conf.RECORD_SEP = optarg; conf.SEP = '\n'; break; case 'M': conf.MISSING_TYPE_WARNINGS = 1; break; case 'n': conf.NS_ENABLED = 1; break; case 'r': conf.PRINT_RR_NAME = 1; break; case 's': conf.TCP_STATE_PATH = optarg; break; case 'S': TCP_SAVE_STATE = 0; break; case 't': conf.PRETTY_DATE = 1; break; case 'D': conf.DEDUPS = strtoul(optarg, NULL, 10); if (conf.DEDUPS > 10000) { conf.DEDUPS = 10000; } break; case 'i': if (optarg) conf.IGNORE_DOMAINS = read_ignore(optarg); else arg_failure = 1; break; case 'x': if (conf.EXCLUDES < MAX_EXCLUDES) { int ival = atoi(optarg); if (ival == 0 || ival >= 65536) { fprintf(stderr, "Invalid excluded rtype value. " "Value must be a short int.\n"); arg_failure = 1; } else { conf.EXCLUDED[conf.EXCLUDES] = ival; conf.EXCLUDES++; } } else { fprintf(stderr, "Too many excluded rtypes. " "If this limit is an issue, then recompile with " "the MAX_EXCLUDES define set higher.\n"); arg_failure = 1; } break; case '?': if (optopt == 'x') fprintf(stderr, "Option -x requires an rtype number.\n"); else if (optopt == 'm') fprintf(stderr, "Option -m needs a delimiter string.\n"); else if (isprint(optopt)) fprintf(stderr, "Unknown option -%c.\n",optopt); else fprintf(stderr, "Invalid option char: 0x%x.\n", optopt); case 'h': default: arg_failure = 1; } c = getopt(argc, argv, OPTIONS); } if (conf.TCP_STATE_PATH == NULL) { conf.TCP_STATE_PATH = DEFAULT_TCP_STATE_PATH; } if (optind == argc - 1) { pcap_file = pcap_open_offline(argv[optind], errbuf); if (pcap_file == NULL) { fprintf(stderr, "Could not open pcapfile.\n%s\n", errbuf); return -1; } conf.datalink = pcap_datalink(pcap_file); if (conf.datalink != DLT_EN10MB && conf.datalink != DLT_LINUX_SLL) { fprintf(stderr, "Unsupported data link layer: %d\n", conf.datalink); arg_failure = 1; pcap_close(pcap_file); } } else if (optind >= argc) { fprintf(stderr, "No input file specified.\n"); arg_failure = 1; } else { fprintf(stderr, "Multiple input files or bad arguments."); arg_failure = 1; } if (arg_failure) { fprintf(stderr, "Usage: dns_parse [-dnthf] [-m<query sep.>] [-x<rtype>] [-s<path>]\n" " <pcap file>\n" "dns_parse parses a pcap file and gives a nicely " "formatted ascii string for each dns request.\n" "By default the reservation records are tab separated " "and the entire record is ended with a newline.\n\n" "The comma separated fields printed for each request are:\n" " time - The time of the request relative to the \n" " capture source clock.\n" " srcip, dstip - the source and dest ipv4 addresses.\n" " ipv6 support is not present.\n" " size - the size of the dns portion of the message.\n" " proto - udp (u) or tcp(t)\n" " query/response - is it a query(q) or response(r)\n" " authoritative - marked with AA if authoritative\n\n" "The resource records are printed after these fields, separated by\n" "a tab (a newline in multiline mode). \n" "By default the resource record format is:\n" "<section> <name> <type> <class> <rdata>\n\n" "<section> is a symbol denoting record type.\n" " ? - Questions (No rdata is included or printed).\n" " ! - Answers\n" " $ - Name Servers\n" " + - Additional\n" "The rdata is parsed by a custom parser that depends on the\n" "record type and class. Use the -f option to get a list of\n" "the supported record types and documentation on the parsers.\n\n" "Args:\n" "<pcapfile> - The pcapfile to parse. Use a '-' for stdin\n" "-c\n" " Append a list of counts for each record type (Questions, \n" " Answers, etc) to record fields. Each type is followed by it's\n" " record type symbol.\n" "-d\n" " Enable the parsing and output of the Additional\n" " Records section. Disabled by default.\n" "-D <count>\n" " Keep hashes of the last <count> packets for de-duplication\n" " purposes (max 10,000). A <count> of zero turns off \n" " de-duplication. \n" " Default 10.\n" "-f\n" " Print out documentation on the various resource \n" " record parsers.\n" "-n\n" " Enable the parsing and output of the Name Server\n" " Records section. Disabled by default.\n" "-i <-|ignorefile>\n" " Ignore requests for domains listed in the file specified.\n" " Two modes are supported. Strict matching with a caret prefix,\n" " e.g. ^sub1.example.com. (Sub)domain matching with e.g. example.com\n" " for example.com, sub.example.com, ..\n" "-m<sep> \n" " Multiline mode. Reservation records are newline\n" " separated, and the whole record ends with the\n" " separator given.\n" "-M \n" " Print a message for each occurance of a missing class,type\n" " parser.\n" "-r \n" " Changes the resource record format to: \n" " <section> <name> <rr_type_name> <rdata>\n" " If the record type isn't known, 'UNKNOWN(<cls>,<type>)' is given\n" " The query record format is the similar, but missing the rdata.\n" "-s<path> \n" " Path to the tcp state save file. \n" " This will be loaded (and overwritten) every time dns_parse \n" " is run. \n" " Default is: %s \n" "-S \n" " Disable TCP state saving/loading.\n" "-t \n" " Print the time/date as in Y-m-d H:M:S (ISO 8601) format.\n" " The time will be in the local timezone.\n" "-x\n" " Exclude the given reservation record types by \n" " number. This option can be given multiple times.\n" "\n" "Supported protocols:\n" "DNS can ride on a number of protocols, and dns_parse supports\n" "a fair number of them, including:\n" "Ethernet, MPLS, IPv4, IPv6, UDP and TCP.\n" "IPv4 and IPv6 fragments - fragments are reassembled, but data\n" " may be lost if the fragments are split across multiple pcaps.\n" "TCP reassembly - TCP packets are reassembled, but the resulting\n" " data may be offset from their time of occurance. Partial flow\n" " reassembly is supported; long flows are printed whenever a \n" " a lull in that flow occurs (500 ms since the last packet, \n" " this can only be changed at compile time).\n" " TCP flow state is saved at the end of execution, and loaded\n" " at the beginning. See the -S option to disable.\n", DEFAULT_TCP_STATE_PATH); return -1; } conf.ip_fragment_head = NULL; // Load and prior TCP session info conf.tcp_sessions_head = NULL; if (TCP_SAVE_STATE == 1) { tcp_load_state(&conf); } conf.dedup_hashes = calloc(conf.DEDUPS, sizeof(uint64_t)); // need to check this for overflow. read = pcap_dispatch(pcap_file, -1, (pcap_handler)handler, (uint8_t *) &conf); if (TCP_SAVE_STATE == 1) { tcp_save_state(&conf); } else { tcp_expire(&conf, NULL); } free(conf.dedup_hashes); ip_frag_free(&conf); if (read == -1) { fprintf(stderr, "pcap_dispatch: %s\n", pcap_geterr(pcap_file)); } else if (read == -2) { fprintf(stderr, "pcap_dispatch: break!\n"); } VERBOSE( else fprintf(stderr, "pcap_dispatch: %d packets processed\n", read); )