/*
* 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;
}