summaryrefslogtreecommitdiffstats
path: root/src/packetq.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/packetq.cpp')
-rw-r--r--src/packetq.cpp346
1 files changed, 346 insertions, 0 deletions
diff --git a/src/packetq.cpp b/src/packetq.cpp
new file mode 100644
index 0000000..3a56fd5
--- /dev/null
+++ b/src/packetq.cpp
@@ -0,0 +1,346 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "packet_handler.h"
+#include "packetq.h"
+#include "pcap.h"
+#include "reader.h"
+#include "server.h"
+#include "sql.h"
+
+#include <algorithm>
+#include <list>
+#include <stack>
+#include <stdexcept>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#ifndef WIN32
+#include <getopt.h>
+#include <signal.h>
+#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] <pcapfile ...>\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<std::string> 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;
+}