/*
 * 5n0r7.c - a snort alert file parser
 * Copyright (C) 2000  Michel "MaXX" Kaempf <maxx@via.ecp.fr>
 *
 * 5n0r7 is a snort alert file parser. It sorts the alerts on source IP,
 * destination IP and frequency to stdout. It is not intended to support
 * the portscan preprocessor which messes up snort's alert files. 5n0r7
 * allows to detect attacks (portscans, probes, or whatever snort logs),
 * at the first sight when displaying a sorted alert file (typical usage :
 * `./5n0r7 ./alert'). Here is an example of 5n0r7's output :
 *
 * * a.b.c.d (21242)
 *     - x.y.z.t (1242)
 *         [**] NULL Scan [**] (212)
 *             04/18-14:10:01.504832
 *             srcport min/max = 62825/62826
 *             dstport min/max = 1/65301
 *
 * The computer a.b.c.d triggered 21242 times the snort alert mechanism,
 * and 1242 of these attacks were directed to the computer x.y.z.t.
 * Among these attacks were 212 NULL Scan packets, and the first NULL Scan
 * packet was logged at 04/18-14:10:01.504832. The lowest port that sent
 * these NULL Scan packets is 62825, the highest is 62826. The lowest port
 * on x.y.z.t hit by this NULL Scan is 1, the highest is 65301 (a typical
 * NMAP NULL Scan).
 *
 * I recommend the use of snort's scan-lib rules and Jim Forster's excellent
 * rules (http://www.rapidnet.com).
 *
 * 5n0r7.c 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 2 of the License, or
 * (at your option) any later version.
 *
 * 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; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define HASHSIZE 12
#define IPADDRSIZE 15

/*****************************************************************************
 * struct snort_nlist
 *****************************************************************************/
struct snort_nlist
{
        struct snort_nlist * next;
        char * p_char;
        unsigned int ui_n;

        void * p;
};

/*****************************************************************************
 * struct snort_src
 *****************************************************************************/
struct snort_src
{
        struct snort_nlist * dst_hashtab[ HASHSIZE ];
};

/*****************************************************************************
 * struct snort_dst
 *****************************************************************************/
struct snort_dst
{
        struct snort_nlist * msg_hashtab[ HASHSIZE ];
};

/*****************************************************************************
 * struct snort_msg
 *****************************************************************************/
struct snort_msg
{
        char * pc_timestamp;
        unsigned int ui_srcminport, ui_srcmaxport;
        unsigned int ui_dstminport, ui_dstmaxport;
};

/*****************************************************************************
 * struct snort_file
 *****************************************************************************/
struct snort_file
{
        FILE * p_file;
        int i_bufsize;
        char * pc_buffer;
};

/*****************************************************************************
 * snort_swap()
 *****************************************************************************/
void snort_swap( struct snort_nlist * v[], int i, int j )
{
        struct snort_nlist * temp;

        temp = v[i];
        v[i] = v[j];
        v[j] = temp;
}

/*****************************************************************************
 * snort_qsort()
 *****************************************************************************/
void snort_qsort( struct snort_nlist * v[], int left, int right )
{
        int i, last;

        if ( left >= right )
        {
                return;
        }
        snort_swap( v, left, (left + right) / 2 );
        last = left;
        for ( i = left + 1; i <= right; i++ )
        {
                if ( v[i]->ui_n > v[left]->ui_n )
                {
                        snort_swap( v, ++last, i );
                }
        }
        snort_swap( v, left, last );
        snort_qsort( v, left, last - 1 );
        snort_qsort( v, last + 1, right );
}

/*****************************************************************************
 * snort_hash()
 *****************************************************************************/
unsigned int snort_hash( char * p_char )
{
        unsigned int ui_hashval;

        for ( ui_hashval = 0; *p_char != '\0'; p_char++ )
        {
                ui_hashval = *p_char + 31 * ui_hashval;
        }

        return( ui_hashval % HASHSIZE );
}

/*****************************************************************************
 * snort_lookup()
 *****************************************************************************/
struct snort_nlist * snort_lookup( struct snort_nlist * hashtab[], char * p_char )
{
        struct snort_nlist * np;

        for ( np = hashtab[snort_hash(p_char)]; np != NULL; np = np->next )
        {
                if ( strcmp(p_char, np->p_char) == 0 )
                {
                        return( np );
                }
        }

        return( NULL );
}

/*****************************************************************************
 * snort_install()
 *****************************************************************************/
struct snort_nlist * snort_install( struct snort_nlist * hashtab[], char * p_char )
{
        struct snort_nlist * np;
        unsigned int ui_hashval;

        if ( (np = snort_lookup(hashtab, p_char)) == NULL )
        {
                if ( (np = (struct snort_nlist *)malloc(sizeof(struct snort_nlist))) == NULL )
                {
                        return( NULL );
                }

                if ( (np->p_char = strdup(p_char)) == NULL )
                {
                        free( np );
                        return( NULL );
                }

                ui_hashval = snort_hash( p_char );
                np->next = hashtab[ ui_hashval ];
                hashtab[ ui_hashval ] = np;

                np->ui_n = 1;

                np->p = NULL;
        }
        else
        {
                np->ui_n += 1;
        }

        return( np );
}

/*****************************************************************************
 * snort_init()
 *****************************************************************************/
void snort_init( struct snort_nlist * hashtab[] )
{
        int i;

        for ( i = 0; i < HASHSIZE; i++ )
        {
                hashtab[ i ] = NULL;
        }
}

/*****************************************************************************
 * snort_cleanup()
 *****************************************************************************/
void snort_cleanup( struct snort_nlist * hashtab[] )
{
        int i, j, k;
        struct snort_nlist * np_src, * np_dst, * np_msg, * np;

        for ( i = 0; i < HASHSIZE; i++ )
        {
        np_src = hashtab[i];
        while ( np_src != NULL )
        {
                for ( j = 0; j < HASHSIZE; j++ )
                {
                np_dst = ((struct snort_src *)np_src->p)->dst_hashtab[j];
                while ( np_dst != NULL )
                {
                        for ( k = 0; k < HASHSIZE; k++ )
                        {
                        np_msg = ((struct snort_dst *)np_dst->p)->msg_hashtab[k];
                        while ( np_msg != NULL )
                        {
                                if ( np_msg->p_char != NULL )
                                {
                                        free( np_msg->p_char );
                                }
                                if ( np_msg->p != NULL )
                                {
                                        if ( ((struct snort_msg *)np_msg->p)->pc_timestamp != NULL )
                                        {
                                                free( ((struct snort_msg *)np_msg->p)->pc_timestamp );
                                        }
                                        free( np_msg->p );
                                }
                                np = np_msg->next;
                                free( np_msg );
                                np_msg = np;
                        }
                        }

                        if ( np_dst->p_char != NULL )
                        {
                                free( np_dst->p_char );
                        }
                        if ( np_dst->p != NULL )
                        {
                                free( np_dst->p );
                        }
                        np = np_dst->next;
                        free( np_dst );
                        np_dst = np;
                }
                }

                if ( np_src->p_char != NULL )
                {
                        free( np_src->p_char );
                }
                if ( np_src->p != NULL )
                {
                        free( np_src->p );
                }
                np = np_src->next;
                free( np_src );
                np_src = np;
        }
        }
}

/*****************************************************************************
 * snort_fopen()
 *****************************************************************************/
struct snort_file * snort_fopen( char * pc_path, char * pc_mode )
{
        struct snort_file * p_sf;

        if ( (p_sf = (struct snort_file *)malloc(sizeof(struct snort_file))) == NULL )
        {
                return( NULL );
        }

        if ( (p_sf->p_file = fopen(pc_path, pc_mode)) == NULL )
        {
                free( p_sf );
                return( NULL );
        }

        p_sf->i_bufsize = 256;

        if ( (p_sf->pc_buffer = (char *)malloc(p_sf->i_bufsize)) == NULL )
        {
                fclose( p_sf->p_file );
                free( p_sf );
                return( NULL );
        }

        return( p_sf );
}

/*****************************************************************************
 * snort_fgets()
 *****************************************************************************/
int snort_fgets( struct snort_file * p_sf )
{
        char * p_char;

        if ( fgets(p_sf->pc_buffer, p_sf->i_bufsize, p_sf->p_file) == NULL )
        {
                return( -1 );
        }

        while ( (p_char = strchr(p_sf->pc_buffer, '\n')) == NULL )
        {
                p_sf->i_bufsize *= 2;

                if ( (p_char = (char *)realloc(p_sf->pc_buffer, p_sf->i_bufsize)) == NULL )
                {
                        return( -1 );
                }
                fprintf( stderr, "snort_fgets(): reallocating buffer from %i bytes to %i bytes.\n", p_sf->i_bufsize / 2, p_sf->i
_bufsize );

                p_sf->pc_buffer = p_char;

                if ( fgets(p_sf->pc_buffer + strlen(p_sf->pc_buffer),
                        p_sf->i_bufsize - strlen(p_sf->pc_buffer),
                        p_sf->p_file) == NULL )
                {
                        return( -1 );
                }
        }

        *p_char = '\0';

        return( 0 );
}

/*****************************************************************************
 * snort_fclose()
 *****************************************************************************/
void snort_fclose( struct snort_file * p_sf )
{
        free( p_sf->pc_buffer );
        fclose( p_sf->p_file );
        free( p_sf );
}

/*****************************************************************************
 * snort_parse()
 *****************************************************************************/
int snort_parse( struct snort_file * p_sf, struct snort_nlist * src_hashtab[] )
{
        int b_msg = 0;
        char pc_src[ IPADDRSIZE + 1 ];
        char pc_dst[ IPADDRSIZE + 1 ];
        char * pc_msg;
        int i_msgsize = p_sf->i_bufsize;
        char * p_char;
        char * pc_srcchr;
        char * pc_dstchr;
        char * pc_srcport;
        char * pc_dstport;
        unsigned int ui_port;
        struct snort_nlist * np;

        if ( (pc_msg = (char *)malloc(i_msgsize)) == NULL )
        {
                return( -1 );
        }

        while ( snort_fgets(p_sf) == 0 )
        {
                if ( p_sf->pc_buffer[0] == '[' )
                {
                        b_msg = 1;

                        if ( i_msgsize != p_sf->i_bufsize )
                        {
                                i_msgsize = p_sf->i_bufsize;
                                if ( (p_char = (char *)realloc(pc_msg, i_msgsize)) == NULL )
                                {
                                        free( pc_msg );
                                        return( -1 );
                                }
                                pc_msg = p_char;
                        }

                        strcpy( pc_msg, p_sf->pc_buffer );
                }
                else if ( b_msg == 1 )
                {
                        if ( (p_char = strstr(p_sf->pc_buffer, " -> ")) != NULL )
                        {
                                *p_char = '\0';

                                pc_dstchr = p_char + 4;
                                pc_dstport = NULL;
                                if ( (p_char = strchr(pc_dstchr, ':')) != NULL )
                                {
                                        *p_char = '\0';
                                        pc_dstport = p_char + 1;
                                }
                                strncpy( pc_dst, pc_dstchr, IPADDRSIZE );
                                pc_dst[ IPADDRSIZE ] = '\0';

                                if ( (p_char = strchr(p_sf->pc_buffer, ' ')) != NULL )
                                {
                                        *p_char = '\0';

                                        pc_srcchr = p_char + 1;
                                        pc_srcport = NULL;
                                        if ( (p_char = strchr(pc_srcchr, ':')) != NULL )
                                        {
                                                *p_char = '\0';
                                                pc_srcport = p_char + 1;
                                        }
                                        strncpy( pc_src, pc_srcchr, IPADDRSIZE );
                                        pc_src[ IPADDRSIZE ] = '\0';

                                        if ( (np = snort_install(src_hashtab, pc_src)) == NULL )
                                        {
                                                free( pc_msg );
                                                return( -1 );
                                        }
                                        if ( np->p == NULL )
                                        {
                                                if ( (np->p = malloc(sizeof(struct snort_src))) == NULL )
                                                {
                                                        free( pc_msg );
                                                        return( -1 );
                                                }
                                                snort_init( ((struct snort_src *)(np->p))->dst_hashtab );
                                        }

                                        if ( (np = snort_install(((struct snort_src *)(np->p))->dst_hashtab, pc_dst)) == NULL )
                                        {
                                                free( pc_msg );
                                                return( -1 );
                                        }
                                        if ( np->p == NULL )
                                        {
                                                if ( (np->p = malloc(sizeof(struct snort_dst))) == NULL )
                                                {
                                                        free( pc_msg );
                                                        return( -1 );
                                                }
                                                snort_init( ((struct snort_dst *)(np->p))->msg_hashtab );
                                        }

                                        if ( (np = snort_install(((struct snort_dst *)(np->p))->msg_hashtab, pc_msg)) == NULL )
                                        {
                                                free( pc_msg );
                                                return( -1 );
                                        }
                                        if ( np->p == NULL )
                                        {
                                                if ( (np->p = malloc(sizeof(struct snort_msg))) == NULL )
                                                {
                                                        free( pc_msg );
                                                        return( -1 );
                                                }

                                                ((struct snort_msg *)np->p)->pc_timestamp = strdup( p_sf->pc_buffer );

                                                ((struct snort_msg *)np->p)->ui_srcminport = (unsigned int)(-1);
                                                ((struct snort_msg *)np->p)->ui_srcmaxport = 0;
                                                ((struct snort_msg *)np->p)->ui_dstminport = (unsigned int)(-1);
                                                ((struct snort_msg *)np->p)->ui_dstmaxport = 0;
                                        }

                                        if ( pc_srcport != NULL )
                                        {
                                                ui_port = atoi( pc_srcport );
                                                if ( ui_port < ((struct snort_msg *)np->p)->ui_srcminport )
                                                {
                                                        ((struct snort_msg *)np->p)->ui_srcminport = ui_port;
                                                }
                                                if ( ui_port > ((struct snort_msg *)np->p)->ui_srcmaxport )
                                                {
                                                        ((struct snort_msg *)np->p)->ui_srcmaxport = ui_port;
                                                }
                                        }
                                        if ( pc_dstport != NULL )
                                        {
                                                ui_port = atoi( pc_dstport );
                                                if ( ui_port < ((struct snort_msg *)np->p)->ui_dstminport )
                                                {
                                                        ((struct snort_msg *)np->p)->ui_dstminport = ui_port;
                                                }
                                                if ( ui_port > ((struct snort_msg *)np->p)->ui_dstmaxport )
                                                {
                                                        ((struct snort_msg *)np->p)->ui_dstmaxport = ui_port;
                                                }
                                        }

                                        b_msg = 0;
                                }
                        }
                }
        }

        free( pc_msg );

        return( 0 );
}

/*****************************************************************************
 * snort_qsortinit()
 *****************************************************************************/
struct snort_nlist ** snort_qsortinit( struct snort_nlist * hashtab[] )
{
        int i, j;
        struct snort_nlist * np;
        struct snort_nlist ** qsorttab;

        i = 0;
        for ( j = 0; j < HASHSIZE; j++ )
        {
        for ( np = hashtab[j]; np != NULL; np = np->next )
        {
                i += 1;
        }
        }

        if ( (qsorttab = (struct snort_nlist **)malloc((i + 1) * sizeof(struct snort_nlist *))) == NULL )
        {
                return( NULL );
        }

        i = 0;
        for ( j = 0; j < HASHSIZE; j++ )
        {
        for ( np = hashtab[j]; np != NULL; np = np->next )
        {
                qsorttab[ i++ ] = np;
        }
        }
        qsorttab[ i ] = NULL;

        return( qsorttab );
}

/*****************************************************************************
 * snort_qsortcleanup()
 *****************************************************************************/
void snort_qsortcleanup( struct snort_nlist ** qsorttab )
{
        free( qsorttab );
}

/*****************************************************************************
 * snort_result()
 *****************************************************************************/
void snort_result( struct snort_nlist * src_hashtab[] )
{
        int i, j, k;
        struct snort_nlist ** src_qsorttab, ** dst_qsorttab, ** msg_qsorttab;

        if ( (src_qsorttab = snort_qsortinit(src_hashtab)) == NULL )
        {
                return;
        }
        for ( i = 0; src_qsorttab[i] != NULL; i++ )
        {
                ;
        }
        snort_qsort( src_qsorttab, 0, i - 1 );
        for ( i = 0; src_qsorttab[i] != NULL; i++ )
        {
                printf( "\n* %s (%u)\n", src_qsorttab[i]->p_char, src_qsorttab[i]->ui_n );
                if ( (dst_qsorttab = snort_qsortinit(((struct snort_src *)src_qsorttab[i]->p)->dst_hashtab)) == NULL )
                {
                        snort_qsortcleanup( src_qsorttab );
                        return;
                }
                for ( j = 0; dst_qsorttab[j] != NULL; j++ )
                {
                        ;
                }
                snort_qsort( dst_qsorttab, 0, j - 1 );
                for ( j = 0; dst_qsorttab[j] != NULL; j++ )
                {
                        printf( "    - %s (%u)\n", dst_qsorttab[j]->p_char, dst_qsorttab[j]->ui_n );
                        if ( (msg_qsorttab = snort_qsortinit(((struct snort_dst *)dst_qsorttab[j]->p)->msg_hashtab)) == NULL )
                        {
                                snort_qsortcleanup( dst_qsorttab );
                                snort_qsortcleanup( src_qsorttab );
                                return;
                        }
                        for ( k = 0; msg_qsorttab[k] != NULL; k++ )
                        {
                                ;
                        }
                        snort_qsort( msg_qsorttab, 0, k - 1 );
                        for ( k = 0; msg_qsorttab[k] != NULL; k++ )
                        {
                                fprintf( stdout, "        %s (%u)\n", msg_qsorttab[k]->p_char, msg_qsorttab[k]->ui_n );

                                fprintf( stdout, "            " );

                                if ( ((struct snort_msg *)msg_qsorttab[k]->p)->pc_timestamp == NULL )
                                {
                                        fprintf( stdout, "NULL" );
                                }
                                else
                                {
                                        fprintf( stdout, "%s", ((struct snort_msg *)msg_qsorttab[k]->p)->pc_timestamp );
                                }

                                if ( ((struct snort_msg *)msg_qsorttab[k]->p)->ui_srcmaxport )
                                {
                                        if ( ((struct snort_msg *)msg_qsorttab[k]->p)->ui_srcminport == ((struct snort_msg *)msg
_qsorttab[k]->p)->ui_srcmaxport )
                                        {
                                                fprintf( stdout, ", srcport = %u", ((struct snort_msg *)msg_qsorttab[k]->p)->ui_
srcminport );
                                        }
                                        else
                                        {
                                                fprintf( stdout, ", srcport min/max = %u/%u", ((struct snort_msg *)msg_qsorttab[
k]->p)->ui_srcminport, ((struct snort_msg *)msg_qsorttab[k]->p)->ui_srcmaxport );
                                        }
                                }

                                if ( ((struct snort_msg *)msg_qsorttab[k]->p)->ui_dstmaxport )
                                {
                                        if ( ((struct snort_msg *)msg_qsorttab[k]->p)->ui_dstminport == ((struct snort_msg *)msg
_qsorttab[k]->p)->ui_dstmaxport )
                                        {
                                                fprintf( stdout, ", dstport = %u", ((struct snort_msg *)msg_qsorttab[k]->p)->ui_
dstminport );
                                        }
                                        else
                                        {
                                                fprintf( stdout, ", dstport min/max = %u/%u", ((struct snort_msg *)msg_qsorttab[
k]->p)->ui_dstminport, ((struct snort_msg *)msg_qsorttab[k]->p)->ui_dstmaxport );
                                        }
                                }

                                fprintf( stdout, "\n" );
                        }
                        snort_qsortcleanup( msg_qsorttab );
                }
                snort_qsortcleanup( dst_qsorttab );
        }
        snort_qsortcleanup( src_qsorttab );
}

/*****************************************************************************
 * main()
 *****************************************************************************/
int main( int i_argc, char * ppc_argv[] )
{
        struct snort_file * p_sf;
        struct snort_nlist * src_hashtab[ HASHSIZE ];

        if ( i_argc != 2 )
        {
                return( -1 );
        }

        if ( (p_sf = snort_fopen(ppc_argv[1], "r")) == NULL )
        {
                return( -1 );
        }

        snort_init( src_hashtab );
        if ( snort_parse(p_sf, src_hashtab) == 0 )
        {
                snort_result( src_hashtab );
        }
        snort_cleanup( src_hashtab );

        snort_fclose( p_sf );

        return( 0 );
}


