/*****
*
* Copyright (C) 2005 PreludeIDS Technologies. All Rights Reserved.
* Author: Yoann Vandoorselaere <yoann.v@prelude-ids.com>
*
* This file is part of the Snort program.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.  You may not use, modify or
* distribute this program under any other version of the GNU General
* Public License.
*
* This program 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; see the file COPYING.  If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*****/

#ifdef HAVE_CONFIG_H
 #include "config.h"
#endif

#ifdef HAVE_LIBPRELUDE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <libprelude/prelude.h>

#include "decode.h"
#include "plugbase.h"
#include "parser.h"
#include "debug.h"
#include "util.h"
#include "mstring.h"
#include "map.h"
#include "unified2.h"
#include "barnyard2.h"
#include "ipv6_port.h"

#define ANALYZER_CLASS "NIDS"
#define ANALYZER_MODEL "Snort"
#define ANALYZER_MANUFACTURER "http://www.snort.org"
#define ANALYZER_SID_URL "http://www.snort.org/pub-bin/sigs.cgi?sid="

#define SNORT_MAX_OWNED_SID 1000000
#define DEFAULT_ANALYZER_NAME "snort"

static char *init_args = NULL;
static unsigned int info_priority = 4;
static unsigned int low_priority  = 3;
static unsigned int mid_priority  = 2;
static prelude_bool_t initialized = FALSE;


static int setup_analyzer(idmef_analyzer_t *analyzer)
{
        int ret;
        prelude_string_t *string;
        
        ret = idmef_analyzer_new_model(analyzer, &string);
        if ( ret < 0 )
                return ret;
        prelude_string_set_constant(string, ANALYZER_MODEL);

        ret = idmef_analyzer_new_class(analyzer, &string);
        if ( ret < 0 )
                return ret;
        prelude_string_set_constant(string, ANALYZER_CLASS);

        ret = idmef_analyzer_new_manufacturer(analyzer, &string);
        if ( ret < 0 ) 
                return ret;
        prelude_string_set_constant(string, ANALYZER_MANUFACTURER);

        ret = idmef_analyzer_new_version(analyzer, &string);
        if ( ret < 0 )
                return ret;
        prelude_string_set_constant(string, VERSION);

        return 0;
}



static idmef_reference_origin_t reference_to_origin(const char *name)
{
        int i, ret;
        struct {
                const char *name;
                idmef_reference_origin_t origin;
        } tbl[] = {
                { "cve", IDMEF_REFERENCE_ORIGIN_CVE             },
                { "bugtraq", IDMEF_REFERENCE_ORIGIN_BUGTRAQID   },
                { "osvdb", IDMEF_REFERENCE_ORIGIN_OSVDB         },
                { NULL, 0                                       }
        };

        for ( i = 0; tbl[i].name; i++ ) {
                ret = strcmp(tbl[i].name, name);
                if ( ret == 0 )
                        return tbl[i].origin;
        }

        return IDMEF_REFERENCE_ORIGIN_VENDOR_SPECIFIC;
}



static int event_to_source_target(Packet *p, idmef_alert_t *alert)
{
        int ret;
        idmef_node_t *node;
        idmef_source_t *source;
        idmef_target_t *target;
        idmef_address_t *address;
        idmef_service_t *service;
        prelude_string_t *string;
        static char saddr[128], daddr[128];

        if ( !p )
            return 0;

        if ( ! IPH_IS_VALID(p) )
                return 0;
        
        ret = idmef_alert_new_source(alert, &source, IDMEF_LIST_APPEND);
        if ( ret < 0 )
                return ret;

        if (barnyard2_conf->interface != NULL) {
                ret = idmef_source_new_interface(source, &string);
                if ( ret < 0 )
                        return ret;
                prelude_string_set_ref(string, PRINT_INTERFACE(barnyard2_conf->interface));
        }
        
        ret = idmef_source_new_service(source, &service);
        if ( ret < 0 )
                return ret;

        if ( p->tcph || p->udph )
                idmef_service_set_port(service, p->sp);
        
        idmef_service_set_ip_version(service, GET_IPH_VER(p));
        idmef_service_set_iana_protocol_number(service, GET_IPH_PROTO(p));
        
        ret = idmef_source_new_node(source, &node);
        if ( ret < 0 )
                return ret;

        ret = idmef_node_new_address(node, &address, IDMEF_LIST_APPEND);
        if ( ret < 0 )
                return ret;

        ret = idmef_address_new_address(address, &string);
        if ( ret < 0 )
                return ret;
        
        SnortSnprintf(saddr, sizeof(saddr), "%s", inet_ntoa(GET_SRC_ADDR(p)));
        prelude_string_set_ref(string, saddr);

        ret = idmef_alert_new_target(alert, &target, IDMEF_LIST_APPEND);
        if ( ret < 0 )
                return ret;

        if (barnyard2_conf->interface != NULL) {
            ret = idmef_target_new_interface(target, &string);
            if ( ret < 0 )
                return ret;
            prelude_string_set_ref(string, barnyard2_conf->interface);
        }

        ret = idmef_target_new_service(target, &service);
        if ( ! ret < 0 )
                return ret;
        
        if ( p->tcph || p->udph )                
                idmef_service_set_port(service, p->dp);
        
        idmef_service_set_ip_version(service, GET_IPH_VER(p));
        idmef_service_set_iana_protocol_number(service, GET_IPH_PROTO(p));
        
        ret = idmef_target_new_node(target, &node);
        if ( ret < 0 )
                return ret;
        
        ret = idmef_node_new_address(node, &address, IDMEF_LIST_APPEND);
        if ( ret < 0 )
                return ret;
        
        ret = idmef_address_new_address(address, &string);
        if ( ret < 0 )
                return ret;
                
        SnortSnprintf(daddr, sizeof(daddr), "%s", inet_ntoa(GET_DST_ADDR(p)));
        prelude_string_set_ref(string, daddr);
        
        return 0;
}



static int add_byte_data(idmef_alert_t *alert, const char *meaning, const unsigned char *data, size_t size)
{
        int ret;
        prelude_string_t *str;
        idmef_additional_data_t *ad;

        if ( ! data || ! size )
                return 0;
        
        ret = idmef_alert_new_additional_data(alert, &ad, IDMEF_LIST_APPEND);
        if ( ret < 0 )
                return ret;

        ret = idmef_additional_data_set_byte_string_ref(ad, data, size);
        if ( ret < 0 ) {
                ErrorMessage("%s: error setting byte string data: %s.\n",
                             prelude_strsource(ret), prelude_strerror(ret));
                return -1;
        }

        ret = idmef_additional_data_new_meaning(ad, &str);
        if ( ret < 0 ) {
                ErrorMessage("%s: error creating additional-data meaning: %s.\n",
                             prelude_strsource(ret), prelude_strerror(ret));
                return -1;
        }
        
        ret = prelude_string_set_ref(str, meaning);
        if ( ret < 0 ) {
                ErrorMessage("%s: error setting byte string data meaning: %s.\n",
                             prelude_strsource(ret), prelude_strerror(ret));
                return -1;
        }
                
        return 0;
}



static int add_string_data(idmef_alert_t *alert, const char *meaning, const char *data)
{
        int ret;
        prelude_string_t *str;
        idmef_additional_data_t *ad;

        if ( ! data )
                return 0;
        
        ret = idmef_alert_new_additional_data(alert, &ad, IDMEF_LIST_APPEND);
        if ( ret < 0 )
                return ret;

        ret = idmef_additional_data_set_string_ref(ad, data);
        if ( ret < 0 ) {
                ErrorMessage("%s: error setting string data: %s.\n",
                             prelude_strsource(ret), prelude_strerror(ret));
                return -1;
        }

        ret = idmef_additional_data_new_meaning(ad, &str);
        if ( ret < 0 ) {
                ErrorMessage("%s: error creating additional-data meaning: %s.\n",
                             prelude_strsource(ret), prelude_strerror(ret));
                return -1;
        }
        
        ret = prelude_string_set_ref(str, meaning);
        if ( ret < 0 ) {
                ErrorMessage("%s: error setting string data meaning: %s.\n",
                             prelude_strsource(ret), prelude_strerror(ret));
                return -1;
        }
        
        return 0;
}



static int add_int_data(idmef_alert_t *alert, const char *meaning, uint32_t data)
{
        int ret;
        prelude_string_t *str;
        idmef_additional_data_t *ad;
        
        ret = idmef_alert_new_additional_data(alert, &ad, IDMEF_LIST_APPEND);
        if ( ret < 0 )
                return ret;
        
        idmef_additional_data_set_integer(ad, data);

        ret = idmef_additional_data_new_meaning(ad, &str);
        if ( ret < 0 ) {
                ErrorMessage("%s: error creating additional-data meaning: %s.\n",
                             prelude_strsource(ret), prelude_strerror(ret));
                return -1;
        }
        
        ret = prelude_string_set_ref(str, meaning);
        if ( ret < 0 ) {
                ErrorMessage("%s: error setting integer data meaning: %s.\n",
                             prelude_strsource(ret), prelude_strerror(ret));
                return -1;
        }
        
        return 0;
}




static int packet_to_data(Packet *p, void *event, idmef_alert_t *alert)
{
        int i;
        
        if ( ! p )
            return 0;

        add_int_data(alert, "snort_rule_sid", ntohl(((Unified2EventCommon *)event)->signature_id));
        add_int_data(alert, "snort_rule_rev", ntohl(((Unified2EventCommon *)event)->signature_revision));
        
        if ( IPH_IS_VALID(p) ) {
                add_int_data(alert, "ip_ver", GET_IPH_VER(p));
                add_int_data(alert, "ip_hlen", GET_IPH_HLEN(p));
                add_int_data(alert, "ip_tos", GET_IPH_TOS(p));
                add_int_data(alert, "ip_len", ntohs(GET_IPH_LEN(p)));
#ifdef SUP_IP6
// XXX-IPv6 need fragmentation ID
#else
                add_int_data(alert, "ip_id", ntohs(p->iph->ip_id));
#endif
#ifdef SUP_IP6
// XXX-IPv6 need fragmentation offset
#else
                add_int_data(alert, "ip_off", ntohs(p->iph->ip_off));
#endif
                add_int_data(alert, "ip_ttl", GET_IPH_TTL(p));
                add_int_data(alert, "ip_proto", GET_IPH_PROTO(p));
#ifdef SUP_IP6
// XXX-IPv6 need checksum
#else
                add_int_data(alert, "ip_sum", ntohs(p->iph->ip_csum));
#endif
                
                for ( i = 0; i < p->ip_option_count; i++ ) {
                        add_int_data(alert, "ip_option_code", p->ip_options[i].code);
                        add_byte_data(alert, "ip_option_data", 
                            p->ip_options[i].data, p->ip_options[i].len);        
                }
        }
        
        if ( p->tcph ) {
                add_int_data(alert, "tcp_seq", ntohl(p->tcph->th_seq));
                add_int_data(alert, "tcp_ack", ntohl(p->tcph->th_ack));
                
                add_int_data(alert, "tcp_off", TCP_OFFSET(p->tcph));
                add_int_data(alert, "tcp_res", TCP_X2(p->tcph));
                add_int_data(alert, "tcp_flags", p->tcph->th_flags);

                add_int_data(alert, "tcp_win", ntohs(p->tcph->th_win));
                add_int_data(alert, "tcp_sum", ntohs(p->tcph->th_sum));
                add_int_data(alert, "tcp_urp", ntohs(p->tcph->th_urp));

                
                for ( i = 0; i < p->tcp_option_count; i++ ) {
                        add_int_data(alert, "tcp_option_code", p->tcp_options[i].code);
                        add_byte_data(alert, "tcp_option_data", p->tcp_options[i].data, p->tcp_options[i].len);        
                }
        }

        else if ( p->udph ) {
                add_int_data(alert, "udp_len", ntohs(p->udph->uh_len));
                add_int_data(alert, "udp_sum", ntohs(p->udph->uh_chk));
        }

        else if ( p->icmph ) {
                add_int_data(alert, "icmp_type", p->icmph->type);
                add_int_data(alert, "icmp_code", p->icmph->code);
                add_int_data(alert, "icmp_sum", ntohs(p->icmph->csum));

                switch ( p->icmph->type ) {
                        
                case ICMP_ECHO:
                case ICMP_ECHOREPLY:
                case ICMP_INFO_REQUEST:
                case ICMP_INFO_REPLY:
                case ICMP_ADDRESS:
                case ICMP_TIMESTAMP:
                        add_int_data(alert, "icmp_id", ntohs(p->icmph->s_icmp_id));
                        add_int_data(alert, "icmp_seq", ntohs(p->icmph->s_icmp_seq));
                        break;
                        
                case ICMP_ADDRESSREPLY:
                        add_int_data(alert, "icmp_id", ntohs(p->icmph->s_icmp_id));
                        add_int_data(alert, "icmp_seq", ntohs(p->icmph->s_icmp_seq));
                        add_int_data(alert, "icmp_mask", (uint32_t) ntohl(p->icmph->s_icmp_mask));
                        break;
                
                case ICMP_REDIRECT:
#ifndef SUP_IP6
                        add_string_data(alert, "icmp_gwaddr", inet_ntoa(p->icmph->s_icmp_gwaddr));
#else
                        {
                            sfip_t gwaddr;
                            sfip_set_raw(&gwaddr, (void *)&p->icmph->s_icmp_gwaddr.s_addr, AF_INET);
                            add_string_data(alert, "icmp_gwaddr", inet_ntoa(&gwaddr));
                        }
#endif
                        break;
                
                case ICMP_ROUTER_ADVERTISE:
                        add_int_data(alert, "icmp_num_addrs", p->icmph->s_icmp_num_addrs);
                        add_int_data(alert, "icmp_wpa", p->icmph->s_icmp_wpa);
                        add_int_data(alert, "icmp_lifetime", ntohs(p->icmph->s_icmp_lifetime));
                        break;
                
                case ICMP_TIMESTAMPREPLY:
                        add_int_data(alert, "icmp_id", ntohs(p->icmph->s_icmp_id));
                        add_int_data(alert, "icmp_seq", ntohs(p->icmph->s_icmp_seq));
                        add_int_data(alert, "icmp_otime", p->icmph->s_icmp_otime);
                        add_int_data(alert, "icmp_rtime", p->icmph->s_icmp_rtime);
                        add_int_data(alert, "icmp_ttime", p->icmph->s_icmp_ttime);
                        break;
                }
        }

        add_byte_data(alert, "payload", p->data, p->dsize);
        
        return 0;
}



static int event_to_impact(void *event, idmef_alert_t *alert)
{
        int ret;
        ClassType *cn;
        prelude_string_t *str;
        idmef_impact_t *impact;
        idmef_assessment_t *assessment;
        idmef_impact_severity_t severity;

		/* store and convert once */
		/*TODO: detemine required return code for event being NULL */
		u_int32_t event_priority = ntohl(((Unified2EventCommon *)event)->priority_id);
        
        ret = idmef_alert_new_assessment(alert, &assessment);
        if ( ret < 0 )
                return ret;

        ret = idmef_assessment_new_impact(assessment, &impact);
        if ( ret < 0 )
                return ret;

        if ( event_priority < mid_priority )
                severity = IDMEF_IMPACT_SEVERITY_HIGH;

        else if ( event_priority < low_priority )
                severity = IDMEF_IMPACT_SEVERITY_MEDIUM;

        else if ( event_priority < info_priority )
                severity = IDMEF_IMPACT_SEVERITY_LOW;

        else    severity = IDMEF_IMPACT_SEVERITY_INFO;

        idmef_impact_set_severity(impact, severity);

	    cn = ClassTypeLookupById(barnyard2_conf, ntohl(((Unified2EventCommon *)event)->classification_id));

        if ( cn != NULL ) {
                ret = idmef_impact_new_description(impact, &str);
                if ( ret < 0 )
                        return ret;

                prelude_string_set_ref(str, cn->name);
        }
        
        return 0;
}



static int add_snort_reference(idmef_classification_t *class, int gen_id, int sig_id)
{
        int ret;
        prelude_string_t *str;
        idmef_reference_t *ref;

        if ( sig_id >= SNORT_MAX_OWNED_SID )
                return 0;
        
        ret = idmef_classification_new_reference(class, &ref, IDMEF_LIST_APPEND);
        if ( ret < 0 )
                return ret;
        
        ret = idmef_reference_new_name(ref, &str);
        if ( ret < 0 )
                return ret;
        
        idmef_reference_set_origin(ref, IDMEF_REFERENCE_ORIGIN_VENDOR_SPECIFIC);

        if ( gen_id == 0 )
                ret = prelude_string_sprintf(str, "%u", sig_id);
        else
                ret = prelude_string_sprintf(str, "%u:%u", gen_id, sig_id);

        if ( ret < 0 )
                return ret;

        ret = idmef_reference_new_meaning(ref, &str);
        if ( ret < 0 )
                return ret;

        ret = prelude_string_sprintf(str, "Snort Signature ID");
        if ( ret < 0 )
                return ret;
        
        ret = idmef_reference_new_url(ref, &str);
        if ( ret < 0 )
                return ret;

        if ( gen_id == 0 )
                ret = prelude_string_sprintf(str, ANALYZER_SID_URL "%u", sig_id);
        else
                ret = prelude_string_sprintf(str, ANALYZER_SID_URL "%u:%u", gen_id, sig_id);
         
        return ret;
}



static int event_to_reference(void *event, idmef_classification_t *class)
{
	int					ret;
	SigNode				*sn;
	ReferenceNode		*refs;
    prelude_string_t	*str;
    idmef_reference_t	*ref;

    ret = idmef_classification_new_ident(class, &str);
    if ( ret < 0 )
		return ret;

    if ( ntohl(((Unified2EventCommon *)event)->generator_id) == 0 )
        ret = prelude_string_sprintf(str, "%u", ntohl(((Unified2EventCommon *)event)->signature_id));
    else
        ret = prelude_string_sprintf(str, "%u:%u",
				ntohl(((Unified2EventCommon *)event)->generator_id),
				ntohl(((Unified2EventCommon *)event)->signature_id));
    if ( ret < 0 )
        return ret;

    ret = add_snort_reference(class,
				ntohl(((Unified2EventCommon *)event)->generator_id),
				ntohl(((Unified2EventCommon *)event)->signature_id));

    if ( ret < 0 )
	    return ret;

    /*
     * return if we have no information about the rule.
     */
	sn = GetSigByGidSid(ntohl(((Unified2EventCommon *)event)->generator_id),
			    ntohl(((Unified2EventCommon *)event)->signature_id),
			    ntohl(((Unified2EventCommon *)event)->signature_revision));
			 

	if (sn == NULL)
        return 0;

    for ( refs = sn->refs; refs != NULL; refs = refs->next )
	{

        ret = idmef_classification_new_reference(class, &ref, IDMEF_LIST_APPEND);
	    if ( ret < 0 )
		    return ret;

        ret = idmef_reference_new_name(ref, &str);
        if ( ret < 0 )
			return ret;
                
        idmef_reference_set_origin(ref, reference_to_origin(refs->system->name));
        if ( idmef_reference_get_origin(ref) != IDMEF_REFERENCE_ORIGIN_VENDOR_SPECIFIC )
	        prelude_string_set_ref(str, refs->id);
        else
		    prelude_string_set_constant(str, "url");
 
		ret = idmef_reference_new_url(ref, &str);
        if ( ret < 0 )
			return ret;
                
        prelude_string_sprintf(str, "%s%s", refs->system->url ? refs->system->url : "", refs->id ? refs->id : "");
    }        

    return 0;
}



void snort_alert_prelude(Packet *p, void *event, u_int32_t event_type, void *data)
{
        int ret;
        idmef_time_t *time;
        idmef_alert_t *alert;
        prelude_string_t *str;
        idmef_message_t *idmef;
        idmef_classification_t *class;
        prelude_client_t *client = data;
		SigNode			*sn;

        if ( event == NULL || p == NULL )
            return;

		sn = GetSigByGidSid(ntohl(((Unified2EventCommon *)event)->generator_id),
				    ntohl(((Unified2EventCommon *)event)->signature_id),
				    ntohl(((Unified2EventCommon *)event)->signature_revision));

		if (sn == NULL)
			return;

        ret = idmef_message_new(&idmef);
        if ( ret < 0 )
                return;

        ret = idmef_message_new_alert(idmef, &alert);
        if ( ret < 0 )
                goto err;

        ret = idmef_alert_new_classification(alert, &class);
        if ( ret < 0 )
                goto err;

        ret = idmef_classification_new_text(class, &str);
        if ( ret < 0 )
                goto err;

        prelude_string_set_ref(str, sn->msg);

        ret = event_to_impact(event, alert);
        if ( ret < 0 )
                goto err;

        ret = event_to_reference(event, class);
        if ( ret < 0 )
                goto err;
        
        ret = event_to_source_target(p, alert);
        if ( ret < 0 )
                goto err;
        
        ret = packet_to_data(p, event, alert);
        if ( ret < 0 )
                goto err;
        
        ret = idmef_alert_new_detect_time(alert, &time);
        if ( ret < 0 )
                goto err;
        idmef_time_set_from_timeval(time, &p->pkth->ts);
        
        ret = idmef_time_new_from_gettimeofday(&time);
        if ( ret < 0 )
                goto err; 
        idmef_alert_set_create_time(alert, time);
                
        idmef_alert_set_analyzer(alert, idmef_analyzer_ref(prelude_client_get_analyzer(client)), IDMEF_LIST_PREPEND);
        prelude_client_send_idmef(client, idmef);
                
 err:
        idmef_message_destroy(idmef);
}



static void snort_alert_prelude_clean_exit(int signal, void *data)

{
    /*
     * A Snort sensor reporting to Prelude shall never go offline,
     * which is why we use PRELUDE_CLIENT_EXIT_STATUS_FAILURE.
     */
    prelude_client_destroy(data, PRELUDE_CLIENT_EXIT_STATUS_FAILURE);
    
    /*
     * Free libprelude relevant data and synchronize asynchronous thread.
     */
    prelude_deinit();
}



static void parse_args(char *args, char **profile)
{
        int i, tokens, ret;
        char **args_table, *value, *key;
                
        args_table = mSplit(args, " \t", 0, &tokens, '\\');
        for ( i = 0; i < tokens; i++ ) {
                
                key = args_table[i];
                strtok(key, "=");
                
                value = strtok(NULL, "");
                if ( ! value )
                        FatalError("spo_alert_prelude: missing value for keyword '%s'.\n", key);
                
                ret = strcasecmp("profile", key);
                if ( ret == 0 ) {
                        if ( *profile )
                                free(*profile);
                        
                        *profile = strdup(value);
                        continue;
                }
                
                ret = strcasecmp("info", key);
                if ( ret == 0 ) {
                        info_priority = atoi(value);
                        continue;
                }

                ret = strcasecmp("low", key);
                if ( ret == 0 ) {
                        low_priority = atoi(value);
                        continue;
                }

                ret = strcasecmp("medium", key);
                if ( ret == 0 ) {
                        mid_priority = atoi(value);
                        continue;
                }

                FatalError("spo_alert_prelude: Invalid parameter found: '%s'.\n", key);
        }

        mSplitFree(&args_table, tokens);
}


void AlertPreludeSetupAfterSetuid(void)
{
        int ret;
        char *profile = NULL;
        prelude_client_t *client;
        prelude_client_flags_t flags;

        if ( ! initialized )
                return;
        
        parse_args(init_args, &profile);
        free(init_args);
       
        ret = prelude_thread_init(NULL);
        if ( ret < 0 )
            FatalError("%s: Unable to initialize the Prelude thread subsystem: %s.\n",
                       prelude_strsource(ret), prelude_strerror(ret));

        ret = prelude_init(NULL, NULL);
        if ( ret < 0 )
                FatalError("%s: Unable to initialize the Prelude library: %s.\n",
                           prelude_strsource(ret), prelude_strerror(ret));
        
        ret = prelude_client_new(&client, profile ? profile : DEFAULT_ANALYZER_NAME);
        if ( profile )
                free(profile);
        
        if ( ret < 0 )
                FatalError("%s: Unable to create a prelude client object: %s.\n",
                           prelude_strsource(ret), prelude_strerror(ret));

        
        flags = PRELUDE_CLIENT_FLAGS_ASYNC_SEND|PRELUDE_CLIENT_FLAGS_ASYNC_TIMER;
        
        ret = prelude_client_set_flags(client, prelude_client_get_flags(client) | flags);
        if ( ret < 0 )
                FatalError("%s: Unable to set asynchronous send and timer: %s.\n",
                           prelude_strsource(ret), prelude_strerror(ret));

        
        setup_analyzer(prelude_client_get_analyzer(client));
        
        ret = prelude_client_start(client);
        if ( ret < 0 ) {
                if ( prelude_client_is_setup_needed(ret) )
                        prelude_client_print_setup_error(client);

                FatalError("%s: Unable to initialize prelude client: %s.\n",
                           prelude_strsource(ret), prelude_strerror(ret));
        }
                
        AddFuncToOutputList(snort_alert_prelude, OUTPUT_TYPE__ALERT, client);
        AddFuncToCleanExitList(snort_alert_prelude_clean_exit, client);
        AddFuncToRestartList(snort_alert_prelude_clean_exit, client);
}


static void snort_alert_prelude_init(char *args)
{
        /*
         * Do nothing here. Wait until AlertPreludeSetupAfterSetuid is called.
         */
        if ( args )
                init_args = strdup((char *) args);

        initialized = TRUE;
}


void AlertPreludeSetup(void)
{
    RegisterOutputPlugin("alert_prelude", OUTPUT_TYPE_FLAG__ALERT, snort_alert_prelude_init);
}


#endif /* HAVE_LIBPRELUDE */
