/* dnscap - DNS capture utility
 *
 * By Paul Vixie (ISC) and Duane Wessels (Measurement Factory), 2007.
 */

/*
 * Copyright (c) 2016-2022, OARC, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"

#include "dnscap.h"
#include "args.h"
#include "bpft.h"
#include "pcaps.h"
#include "dumper.h"
#include "daemon.h"
#include "log.h"
#include "sig.h"

#if defined(HAVE_LIBCRYPTO) && defined(HAVE_OPENSSL_CONF_H) && defined(HAVE_OPENSSL_ERR_H) && defined(HAVE_OPENSSL_EVP_H)
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#define INIT_OPENSSL 1
#endif

plugin_list     plugins;
const char*     ProgramName = "amnesia";
int             dumptrace   = 0;
int             flush       = FALSE;
vlan_list       vlans_excl;
vlan_list       vlans_incl;
unsigned        msg_wanted = MSG_QUERY;
unsigned        dir_wanted = DIR_INITIATE | DIR_RESPONSE;
unsigned        end_hide   = 0U;
unsigned        err_wanted = ERR_NO | ERR_YES; /* accept all by default */
tcpstate_list   tcpstates;
int             tcpstate_count = 0;
endpoint_list   initiators, not_initiators;
endpoint_list   responders, not_responders;
endpoint_list   drop_responders; /* drops only responses from these hosts */
myregex_list    myregexes;
mypcap_list     mypcaps;
mypcap_ptr      pcap_offline       = NULL;
const char*     dump_base          = NULL;
char*           dump_suffix        = 0;
char*           extra_bpf          = NULL;
enum dump_type  dump_type          = nowhere;
enum dump_state dump_state         = dumper_closed;
const char*     kick_cmd           = NULL;
unsigned        limit_seconds      = 0U;
time_t          next_interval      = 0;
unsigned        limit_packets      = 0U;
size_t          limit_pcapfilesize = 0U;
pcap_t*         pcap_dead;
pcap_dumper_t*  dumper;
time_t          dumpstart;
unsigned        msgcount;
size_t          capturedbytes = 0;
char *          dumpname, *dumpnamepart;
char*           bpft;
unsigned        dns_port       = DNS_PORT;
int             promisc        = TRUE;
int             monitor_mode   = FALSE;
int             immediate_mode = FALSE;
int             background     = FALSE;
char            errbuf[PCAP_ERRBUF_SIZE];
int             wantgzip  = 0;
int             wantfrags = FALSE;
int             wanticmp  = FALSE;
int             wanttcp   = FALSE;
int             preso     = FALSE;
#ifdef USE_SECCOMP
int use_seccomp = FALSE;
#endif
int                main_exit            = FALSE;
int                alarm_set            = FALSE;
time_t             start_time           = 0;
time_t             stop_time            = 0;
int                print_pcap_stats     = FALSE;
uint64_t           pcap_drops           = 0;
my_bpftimeval      last_ts              = { 0, 0 };
unsigned long long mem_limit            = (unsigned)MEM_MAX; /* process memory limit */
int                mem_limit_set        = 1; /* TODO: Should be configurable */
const char         DROPTOUSER[]         = "nobody";
pcap_thread_t      pcap_thread          = PCAP_THREAD_T_INIT;
int                only_offline_pcaps   = FALSE;
int                dont_drop_privileges = FALSE;
options_t          options              = OPTIONS_T_DEFAULTS;

ldns_rr_type match_qtype = 0, nmatch_qtype = 0;

int main(int argc, char* argv[])
{
    struct plugin* p;
    struct timeval now;

#ifdef INIT_OPENSSL
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
#if OPENSSL_VERSION_NUMBER < 0x10100000L
    OPENSSL_config(0);
#endif
#endif

    parse_args(argc, argv);
    gettimeofday(&now, 0);
    if (!only_offline_pcaps && start_time) {
        if (now.tv_sec < start_time) {
            char      when[100];
            struct tm tm;
            gmtime_r(&start_time, &tm);
            strftime(when, sizeof when, "%F %T", &tm);
            fprintf(stderr, "Sleeping for %d seconds until %s UTC\n",
                (int)(start_time - now.tv_sec), when);
            sleep(start_time - now.tv_sec);
            fprintf(stderr, "Awake.\n");
        }
    }
    prepare_bpft();
    open_pcaps();
    if (dump_type == to_stdout) {
        if (dumper_open(now)) {
            fprintf(stderr, "%s: dumper_open() to stdout failed\n", ProgramName);
            exit(1);
        }
    }
    INIT_LIST(tcpstates);

    if (!dont_drop_privileges && !only_offline_pcaps) {
        drop_privileges();
    }

    for (p = HEAD(plugins); p != NULL; p = NEXT(p, link)) {
        if (p->start)
            if (0 != (*p->start)(logerr)) {
                logerr("%s_start returned non-zero", p->name);
                exit(1);
            }
    }
    if (dump_type == nowhere)
        dumpstart = time(NULL);
    if (background)
        daemonize();

#if HAVE_PTHREAD
    /*
     * Defer signal setup until we have dropped privileges and daemonized,
     * otherwise signals might not reach us because different threads
     * are running under different users/access
     */
    {
        sigset_t  set;
        int       err;
        pthread_t thread;

        sigfillset(&set);
        if ((err = pthread_sigmask(SIG_BLOCK, &set, 0))) {
            logerr("pthread_sigmask: %s", strerror(err));
            exit(1);
        }

        sigemptyset(&set);
        sigaddset(&set, SIGHUP);
        sigaddset(&set, SIGINT);
        sigaddset(&set, SIGALRM);
        sigaddset(&set, SIGTERM);
        sigaddset(&set, SIGQUIT);

        if ((err = pthread_create(&thread, 0, &sigthread, (void*)&set))) {
            logerr("pthread_create: %s", strerror(err));
            exit(1);
        }
    }
#else
    {
        sigset_t set;

        sigfillset(&set);
        sigdelset(&set, SIGHUP);
        sigdelset(&set, SIGINT);
        sigdelset(&set, SIGALRM);
        sigdelset(&set, SIGTERM);
        sigdelset(&set, SIGQUIT);

        if (sigprocmask(SIG_BLOCK, &set, 0)) {
            logerr("sigprocmask: %s", strerror(errno));
            exit(1);
        }
    }

    setsig(SIGHUP, TRUE);
    setsig(SIGINT, TRUE);
    setsig(SIGALRM, FALSE);
    setsig(SIGTERM, TRUE);
    setsig(SIGQUIT, TRUE);
#endif

    while (!main_exit)
        poll_pcaps();
    /* close PCAPs after dumper_close() to have statistics still available during dumper_close() */
    if (dumper_opened == dump_state)
        (void)dumper_close(last_ts);
    close_pcaps();
    for (p = HEAD(plugins); p != NULL; p = NEXT(p, link)) {
        if (p->stop)
            (*p->stop)();
    }
    options_free(&options);

#ifdef INIT_OPENSSL
    EVP_cleanup();
    CRYPTO_cleanup_all_ex_data();
    ERR_free_strings();
#endif

    return 0;
}
