/* * Copyright (c) 2017-2024 OARC, Inc. * Copyright (c) 2011-2017, IIS - The Internet Foundation in Sweden * All rights reserved. * * This file is part of PacketQ. * * PacketQ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PacketQ 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 PacketQ. If not, see . */ #include "config.h" #include "packet_handler.h" #include "packetq.h" #include "pcap.h" #include "reader.h" #include "server.h" #include "sql.h" #include #include #include #include #include #include #include #ifndef WIN32 #include #include #endif #define NUM_QUERIES 32 namespace packetq { static void usage(char* argv0, bool longversion) { if (!longversion) { fprintf(stdout, "usage: %s [-vhjctxd] [-s stmt] [-l pkts] [-p port] [-w dir] [-r dir] [-m num] \n", argv0); return; } fprintf(stdout, "usage: %s [options] pcapfile(s)...\n" /* -o description .*/ " --select statements |\n" " -s statement Set the SQL statement, can be given multiple times.\n" " --limit packets |\n" " -l packets Set maximum number of packets to process, from all\n" " files and not per file.\n" " --version | -v Display version and exit.\n" " --help | -h Display this help.\n" "\n" "Output:\n" " --json | -j JSON (default)\n" " --csv | -c CSV\n" " --table | -t Text table\n" " --xml | -x XML\n" " --rfc1035 Output DNS names escaped using RFC1035 format:\n" " All characters outsize [a-zA-Z0-9_-] are escaped\n" " like \\012. (Octet value in decimal.)\n" "\n" "Web Server:\n" " --daemon | -d Run web server in daemon mode.\n" " --port number |\n" " -p number Set the port number to listen on.\n" " --webroot dir |\n" " -w dir Set the root directory for the web content.\n" " --pcaproot dir |\n" " -r dir Set the root for the PCAP files to make available.\n" " --maxconn number |\n" " -m number Set the maximum number of concurrent connections.\n" "\n" "example> packetq --csv -s \"select count(*) as mycount, protocol from dns group by protocol;\" myfile.pcap\n" "\n" "Packet fields (available in all tables):\n" " id, s, us, ether_type, src_addr, src_port, dst_addr, dst_port, protocol,\n" " ip_ttl, ip_version, fragments\n" "\"dns\" table fields:\n" " qname, aname, msg_id, msg_size, opcode, rcode, extended_rcode,\n" " edns_version, z, udp_size, qd_count, an_count, ns_count, ar_count,\n" " qtype, qclass, atype, aclass, attl, aa, tc, rd, cd, ra, ad, do, edns0, qr,\n" " edns0_ecs, edns0_ecs_family, edns0_ecs_source, edns0_ecs_scope,\n" " edns0_ecs_address\n" "\"icmp\" table fields:\n" " type, code, echo_identifier, echo_sequence, du_protocol, du_src_addr,\n" " du_dst_addr, desc\n", argv0); } #ifdef WIN32 // windows support is merely for development purposes atm #define PACKAGE_STRING "packetq" struct option { char* s; int args; int b; char c; }; char* optarg = 0; int optind = 1; int getopt_long(int argc, char* argv[], const char* str, option* opt, int* option_index) { while (optind < argc) { if (argv[optind][0] != '-') return -1; if (argv[optind][1] != '-') { int i = 0; while (opt[i].s != NULL) { if (opt[i].c == argv[optind][1]) { optarg = argv[optind + opt[i].args]; optind += 1 + opt[i].args; return opt[i].c; } i++; } } optind++; } return -1; } #endif void sigproc(int sig) { // ignore sig pipe signal(SIGPIPE, sigproc); } PacketQ* g_app = new PacketQ(); } // namespace packetq using namespace packetq; // The main funtion int main(int argc, char* argv[]) { signal(SIGPIPE, sigproc); int port = 0; int limit = 0; int max_conn = 7; bool daemon = false; std::string webroot = "", pcaproot = ""; std::string queries[NUM_QUERIES] = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", }; int qcount = 0; while (1) { int option_index; struct option long_options[] = { { "select", 1, 0, 's' }, { "limit", 1, 0, 'l' }, { "maxconn", 1, 0, 'm' }, { "webroot", 1, 0, 'w' }, { "pcaproot", 1, 0, 'r' }, { "port", 1, 0, 'p' }, { "daemon", 0, 0, 'd' }, { "csv", 0, 0, 'c' }, { "json", 0, 0, 'j' }, { "table", 0, 0, 't' }, { "xml", 0, 0, 'x' }, { "rfc1035", 0, 0, 10000 }, { "help", 0, 0, 'h' }, { "version", 0, 0, 'v' }, { NULL, 0, 0, 0 } }; int c = getopt_long(argc, argv, "w:r:s:l:p:hHdvcxtjm:", long_options, &option_index); if (c == -1) break; switch (c) { case 'v': fprintf(stdout, "%s\n", PACKAGE_STRING); exit(0); case 's': if (qcount < NUM_QUERIES) { queries[qcount++] = optarg; } else { fprintf(stderr, "Warning: can't handle more than %d separate query strings; discarding '%s'\n", NUM_QUERIES, optarg); } break; case 'c': g_app->set_output(PacketQ::csv); break; case 't': g_app->set_output(PacketQ::csv_format); break; case 'x': g_app->set_output(PacketQ::xml); break; case 'j': g_app->set_output(PacketQ::json); break; case 'd': daemon = true; break; case 'w': webroot = optarg; break; case 'r': pcaproot = optarg; break; case 'm': max_conn = atoi(optarg) + 1; if (max_conn < 2) max_conn = 2; break; case 'l': limit = atoi(optarg); break; case 'p': port = atoi(optarg); break; case 10000: // rfc1035 g_app->set_escape(true); break; default: fprintf(stderr, "Unknown option: %c\n", c); usage(argv[0], false); return 1; case 'h': usage(argv[0], true); return 1; } } init_packet_handlers(g_app->get_escape()); // set up tables g_app->set_limit(limit); if (port > 0) { start_server(port, daemon, pcaproot, webroot, max_conn); } if (optind >= argc) { fprintf(stderr, "Missing input uri\n"); usage(argv[0], false); return 1; } std::vector in_files; while (optind < argc) { in_files.push_back(argv[optind]); optind++; } Reader reader(in_files, g_app->get_limit()); if (g_app->get_output() == PacketQ::json) { printf("[\n"); } for (int i = 0; i < qcount; i++) { char tablename[32]; snprintf(tablename, 32, "result-%d", i); try { Query query(tablename, queries[i].c_str()); query.parse(); query.execute(reader); Table* result = query.m_result; switch (g_app->get_output()) { case (PacketQ::csv_format): if (result) result->csv(true); break; case (PacketQ::csv): if (result) result->csv(); break; case (PacketQ::xml): if (result) result->xml(); break; case (PacketQ::json): if (result) result->json(i < (qcount - 1)); break; } } catch (Error& e) { printf("Error: %s\n", e.m_err.c_str()); fflush(stdout); exit(1); } catch (...) { printf("Error: an unknown error has occured !\n"); fflush(stdout); } } if (g_app->get_output() == PacketQ::json) { printf("]\n"); } delete g_app; destroy_packet_handlers(); return 0; }