/* Chrysalide - Outil d'analyse de fichiers binaires
 * fast-rost.c - fichier d'entrée du centre de collecte, adapté pour un fuzzing optimal
 *
 * Copyright (C) 2023 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide 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.
 *
 *  Chrysalide 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include <assert.h>
#include <getopt.h>
#include <libgen.h>
#include <locale.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>


#include <i18n.h>


#include <analysis/contents/file.h>
#include <analysis/scan/options.h>
#include <analysis/scan/scanner.h>
#include <analysis/scan/patterns/backends/bitap.h>
#include <analysis/scan/patterns/backends/acism.h>
#include <core/core.h>
#include <core/global.h>
#include <core/logs.h>
#include <core/paths.h>
#include <plugins/pglist.h>



#ifndef __AFL_FUZZ_TESTCASE_LEN

ssize_t fuzz_len;
unsigned char fuzz_buf[1024000];

#   define __AFL_FUZZ_TESTCASE_LEN fuzz_len
#   define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
#   define __AFL_FUZZ_INIT() void sync(void);
#   define __AFL_LOOP(x) \
        ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0)
#   define __AFL_INIT() sync()

#endif


__AFL_FUZZ_INIT();


/******************************************************************************
*                                                                             *
*  Paramètres  : argc = nombre d'arguments dans la ligne de commande.         *
*                argv = arguments de la ligne de commande.                    *
*                                                                             *
*  Description : Point d'entrée du programme.                                 *
*                                                                             *
*  Retour      : EXIT_SUCCESS si le prgm s'est déroulé sans encombres.        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

int main(int argc, char **argv)
{
    int result;                             /* Bilan de l'exécution        */
    bool check_only;                        /* Validation uniquement       */
    LogMessageType verbosity;               /* Niveau de filtre de message */
    GScanOptions *options;                  /* Options d'analyses          */
    int index;                              /* Indice d'argument           */
    int ret;                                /* Bilan d'un appel            */
    char *edir;                             /* Répertoire de base effectif */
    char *target;                           /* Cible communiquée           */
    unsigned char *afl_buf;                 /* Tampon de travail d'AFL     */
    int afl_len;                            /* Taille de ce tampon         */
    GContentScanner *scanner;               /* Encadrement d'une recherche */
    GBinContent *content;                   /* Contenu à analyser          */
    GScanContext *context;                  /* Contexte des trouvailles    */
    sized_string_t padding;                 /* Bourrage pour le JSON       */
    bool full;                              /* Détailler l'affichage ?     */

    static struct option long_options[] = {
        { "algorithm",      required_argument,  NULL,   'A' },
        { "check-only",     no_argument,        NULL,   'C' },
        { "print-json",     no_argument,        NULL,   'j' },
        { "print-strings",  no_argument,        NULL,   's' },
        { "print-stats",    no_argument,        NULL,   'S' },
        { "print-tags",     no_argument,        NULL,   'g' },
        { "tag",            required_argument,  NULL,   't' },
        { "verbosity",      required_argument,  NULL,   'V' },
        { NULL,             0,                  NULL,   0 }
    };

    result = EXIT_FAILURE;

    /* Décodage des options */

    check_only = false;
    verbosity = LMT_COUNT;

    options = g_scan_options_new();

    g_scan_options_set_backend_for_data(options, G_TYPE_ACISM_BACKEND);

    while (true)
    {
        ret = getopt_long(argc, argv, "A:CjsSgt:V:", long_options, &index);
        if (ret == -1) break;

        switch (ret)
        {
            case 'A':
                if (strcmp(optarg, "bitmap") == 0)
                    g_scan_options_set_backend_for_data(options, G_TYPE_BITAP_BACKEND);
                else if (strcmp(optarg, "acism") == 0)
                    g_scan_options_set_backend_for_data(options, G_TYPE_ACISM_BACKEND);
                else
                    g_scan_options_set_backend_for_data(options, G_TYPE_INVALID);
                break;

            case 'C':
                check_only = true;
                g_scan_options_set_check_only(options, true);
                break;

            case 'j':
                g_scan_options_set_print_json(options, true);
                break;

            case 's':
                g_scan_options_set_print_strings(options, true);
                break;

            case 'S':
                g_scan_options_set_print_stats(options, true);
                break;

            case 'g':
                g_scan_options_set_print_tags(options, true);
                break;

            case 't':
                g_scan_options_select_tag(options, optarg);
                break;

            case 'V':
                verbosity = strtoul(optarg, NULL, 10);
                break;

        }

    }

    if ((check_only && (optind + 0) != argc && (optind + 1) != argc)
        || (!check_only && (optind + 1) != argc && (optind + 2) != argc))
    {
        goto done;
    }

    /* Actions de base */

    if (g_scan_options_get_backend_for_data(options) == G_TYPE_INVALID)
    {
        goto done;
    }

    /* Lancement des choses sérieuses */

    setlocale(LC_ALL, "");
    edir = get_effective_directory(LOCALE_DIR);
    bindtextdomain(PACKAGE, edir);
    free(edir);
    textdomain(PACKAGE);

    /* Initialisation de GTK */
    g_set_prgname("ROST");
    //gtk_init(&argc, &argv);

    /* Initialisation du programme */

    set_batch_mode();

    set_log_verbosity(verbosity);

    if (!load_all_core_components(true))
        goto done;

    init_all_plugins(true);

    /* Traitement des recherches */

    if ((optind + 1) == argc)
        target = argv[optind];
    else
        goto done;

    __AFL_INIT();

    afl_buf = __AFL_FUZZ_TESTCASE_BUF;

    while (__AFL_LOOP(10000))
    {
        afl_len = __AFL_FUZZ_TESTCASE_LEN;

        scanner = g_content_scanner_new_from_text((char *)afl_buf, afl_len);

#if 0
        do
        {
            FILE *stream;

            stream = fopen("/dev/shm/ctrl.log", "a");
            fprintf(stream, "running %d bytes => %p\n", afl_len, scanner);
            fclose(stream);

        } while (0);
#endif

        if (scanner != NULL)
            result = EXIT_SUCCESS;

        if (scanner != NULL && !check_only)
        {
            content = g_file_content_new(target);
            if (content == NULL) goto bad_file_content;

            context = g_content_scanner_analyze(scanner, options, content);

            if (g_scan_options_get_print_json(options))
            {
                padding.data = "   ";
                padding.len = 3;

                g_content_scanner_output_to_json(scanner, context, &padding, 0, STDOUT_FILENO);

            }
            else
            {
                full = g_scan_options_get_print_strings(options);

                g_content_scanner_output_to_text(scanner, context, full, STDOUT_FILENO);

            }

            g_object_unref(G_OBJECT(context));
            g_object_unref(G_OBJECT(content));

 bad_file_content:

            g_object_unref(G_OBJECT(scanner));

        }

    }

    g_object_unref(G_OBJECT(options));

    /* Sortie */

    unload_all_core_components(false);

 done:

    return result;

}