root/sbin/ipf/ipscan/ipscan_y.y

/*
 * Copyright (C) 2012 by Darren Reed.
 *
 * See the IPFILTER.LICENCE file for details on licencing.
 */
%{
#include <sys/types.h>
#include <sys/ioctl.h>
#include "ipf.h"
#include "opts.h"
#include "kmem.h"
#include "ipscan_l.h"
#include "netinet/ip_scan.h"
#include <ctype.h>

#define YYDEBUG 1

extern  char    *optarg;
extern  void    yyerror(char *);
extern  int     yyparse(void);
extern  int     yylex(void);
extern  int     yydebug;
extern  FILE    *yyin;
extern  int     yylineNum;
extern  void    printbuf(char *, int, int);


void            printent(ipscan_t *);
void            showlist(void);
int             getportnum(char *);
struct in_addr  gethostip(char *);
struct in_addr  combine(int, int, int, int);
char            **makepair(char *, char *);
void            addtag(char *, char **, char **, struct action *);
int             cram(char *, char *);
void            usage(char *);
int             main(int, char **);

int             opts = 0;
int             fd = -1;


%}

%union  {
        char    *str;
        char    **astr;
        u_32_t  num;
        struct  in_addr ipa;
        struct  action  act;
        union   i6addr  ip6;
}

%type   <str> tag
%type   <act> action redirect result
%type   <ipa> ipaddr
%type   <num> portnum
%type   <astr> matchup onehalf twohalves

%token  <num>   YY_NUMBER YY_HEX
%token  <str>   YY_STR
%token          YY_COMMENT
%token          YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
%token          YY_RANGE_OUT YY_RANGE_IN
%token  <ip6>   YY_IPV6
%token          IPSL_START IPSL_STARTGROUP IPSL_CONTENT

%token  IPSL_CLOSE IPSL_TRACK IPSL_EOF IPSL_REDIRECT IPSL_ELSE

%%
file:   line ';'
        | assign ';'
        | file line ';'
        | file assign ';'
        | YY_COMMENT
        ;

line:   IPSL_START dline
        | IPSL_STARTGROUP gline
        | IPSL_CONTENT oline
        ;

dline:  cline                                   { resetlexer(); }
        | sline                                 { resetlexer(); }
        | csline                                { resetlexer(); }
        ;

gline:  YY_STR ':' glist '=' action
        ;

oline:  cline
        | sline
        | csline
        ;

assign: YY_STR assigning YY_STR
                                                { set_variable($1, $3);
                                                  resetlexer();
                                                  free($1);
                                                  free($3);
                                                  yyvarnext = 0;
                                                }
        ;

assigning:
        '='                                     { yyvarnext = 1; }
        ;

cline:  tag ':' matchup '=' action              { addtag($1, $3, NULL, &$5); }
        ;

sline:  tag ':' '(' ')' ',' matchup '=' action  { addtag($1, NULL, $6, &$8); }
        ;

csline: tag ':' matchup ',' matchup '=' action  { addtag($1, $3, $5, &$7); }
        ;

glist:  YY_STR
        | glist ',' YY_STR
        ;

tag:    YY_STR                                  { $$ = $1; }
        ;

matchup:
        onehalf                                 { $$ = $1; }
        | twohalves                             { $$ = $1; }
        ;

action: result                          { $$.act_val = $1.act_val;
                                          $$.act_ip = $1.act_ip;
                                          $$.act_port = $1.act_port; }
        | result IPSL_ELSE result       { $$.act_val = $1.act_val;
                                          $$.act_else = $3.act_val;
                                          if ($1.act_val == IPSL_REDIRECT) {
                                                  $$.act_ip = $1.act_ip;
                                                  $$.act_port = $1.act_port;
                                          }
                                          if ($3.act_val == IPSL_REDIRECT) {
                                                  $$.act_eip = $3.act_eip;
                                                  $$.act_eport = $3.act_eport;
                                          }
                                        }

result: IPSL_CLOSE                              { $$.act_val = IPSL_CLOSE; }
        | IPSL_TRACK                            { $$.act_val = IPSL_TRACK; }
        | redirect                              { $$.act_val = IPSL_REDIRECT;
                                                  $$.act_ip = $1.act_ip;
                                                  $$.act_port = $1.act_port; }
        ;

onehalf:
        '(' YY_STR ')'                  { $$ = makepair($2, NULL); }
        ;

twohalves:
        '(' YY_STR ',' YY_STR ')'       { $$ = makepair($2, $4); }
        ;

redirect:
        IPSL_REDIRECT '(' ipaddr ')'            { $$.act_ip = $3;
                                                  $$.act_port = 0; }
        | IPSL_REDIRECT '(' ipaddr ',' portnum ')'
                                                { $$.act_ip = $3;
                                                  $$.act_port = $5; }
        ;


ipaddr: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER
                                                { $$ = combine($1,$3,$5,$7); }
        | YY_STR                                { $$ = gethostip($1);
                                                  free($1);
                                                }
        ;

portnum:
        YY_NUMBER                               { $$ = htons($1); }
        | YY_STR                                { $$ = getportnum($1);
                                                  free($1);
                                                }
        ;

%%


static  struct  wordtab yywords[] = {
        { "close",              IPSL_CLOSE },
        { "content",            IPSL_CONTENT },
        { "else",               IPSL_ELSE },
        { "start-group",        IPSL_STARTGROUP },
        { "redirect",           IPSL_REDIRECT },
        { "start",              IPSL_START },
        { "track",              IPSL_TRACK },
        { NULL,         0 }
};


int
cram(char *dst, char *src)
{
        char c, *s, *t, *u;
        int i, j, k;

        c = *src;
        s = src + 1;
        t = strchr(s, c);
        *t = '\0';
        for (u = dst, i = 0; (i <= ISC_TLEN) && (s < t); ) {
                c = *s++;
                if (c == '\\') {
                        if (s >= t)
                                break;
                        j = k = 0;
                        do {
                                c = *s++;
                                if (j && (!ISDIGIT(c) || (c > '7') ||
                                     (k >= 248))) {
                                        *u++ = k, i++;
                                        j = k = 0;
                                        s--;
                                        break;
                                }
                                i++;

                                if (ISALPHA(c) || (c > '7')) {
                                        switch (c)
                                        {
                                        case 'n' :
                                                *u++ = '\n';
                                                break;
                                        case 'r' :
                                                *u++ = '\r';
                                                break;
                                        case 't' :
                                                *u++ = '\t';
                                                break;
                                        default :
                                                *u++ = c;
                                                break;
                                        }
                                } else if (ISDIGIT(c)) {
                                        j = 1;
                                        k <<= 3;
                                        k |= (c - '0');
                                        i--;
                                } else
                                                *u++ = c;
                        } while ((i <= ISC_TLEN) && (s <= t) && (j > 0));
                } else
                        *u++ = c, i++;
        }
        return(i);
}


void
printent(ipscan_t *isc)
{
        char buf[ISC_TLEN+1];
        u_char *u;
        int i, j;

        buf[ISC_TLEN] = '\0';
        bcopy(isc->ipsc_ctxt, buf, ISC_TLEN);
        printf("%s : (\"", isc->ipsc_tag);
        printbuf(isc->ipsc_ctxt, isc->ipsc_clen, 0);

        bcopy(isc->ipsc_cmsk, buf, ISC_TLEN);
        printf("\", \"%s\"), (\"", buf);

        printbuf(isc->ipsc_stxt, isc->ipsc_slen, 0);

        bcopy(isc->ipsc_smsk, buf, ISC_TLEN);
        printf("\", \"%s\") = ", buf);

        switch (isc->ipsc_action)
        {
        case ISC_A_TRACK :
                printf("track");
                break;
        case ISC_A_REDIRECT :
                printf("redirect");
                printf("(%s", inet_ntoa(isc->ipsc_ip));
                if (isc->ipsc_port)
                        printf(",%d", isc->ipsc_port);
                printf(")");
                break;
        case ISC_A_CLOSE :
                printf("close");
                break;
        default :
                break;
        }

        if (isc->ipsc_else != ISC_A_NONE) {
                printf(" else ");
                switch (isc->ipsc_else)
                {
                case ISC_A_TRACK :
                        printf("track");
                        break;
                case ISC_A_REDIRECT :
                        printf("redirect");
                        printf("(%s", inet_ntoa(isc->ipsc_eip));
                        if (isc->ipsc_eport)
                                printf(",%d", isc->ipsc_eport);
                        printf(")");
                        break;
                case ISC_A_CLOSE :
                        printf("close");
                        break;
                default :
                        break;
                }
        }
        printf("\n");

        if (opts & OPT_DEBUG) {
                for (u = (u_char *)isc, i = sizeof(*isc); i; ) {
                        printf("#");
                        for (j = 32; (j > 0) && (i > 0); j--, i--)
                                printf("%s%02x", (j & 7) ? "" : " ", *u++);
                        printf("\n");
                }
        }
        if (opts & OPT_VERBOSE) {
                printf("# hits %d active %d fref %d sref %d\n",
                        isc->ipsc_hits, isc->ipsc_active, isc->ipsc_fref,
                        isc->ipsc_sref);
        }
}


void
addtag(char *tstr, char **cp, char **sp, struct action *act)
{
        ipscan_t isc, *iscp;

        bzero((char *)&isc, sizeof(isc));

        strncpy(isc.ipsc_tag, tstr, sizeof(isc.ipsc_tag));
        isc.ipsc_tag[sizeof(isc.ipsc_tag) - 1] = '\0';

        if (cp) {
                isc.ipsc_clen = cram(isc.ipsc_ctxt, cp[0]);
                if (cp[1]) {
                        if (cram(isc.ipsc_cmsk, cp[1]) != isc.ipsc_clen) {
                                fprintf(stderr,
                                        "client text/mask strings different length\n");
                                return;
                        }
                }
        }

        if (sp) {
                isc.ipsc_slen = cram(isc.ipsc_stxt, sp[0]);
                if (sp[1]) {
                        if (cram(isc.ipsc_smsk, sp[1]) != isc.ipsc_slen) {
                                fprintf(stderr,
                                        "server text/mask strings different length\n");
                                return;
                        }
                }
        }

        if (act->act_val == IPSL_CLOSE) {
                isc.ipsc_action = ISC_A_CLOSE;
        } else if (act->act_val == IPSL_TRACK) {
                isc.ipsc_action = ISC_A_TRACK;
        } else if (act->act_val == IPSL_REDIRECT) {
                isc.ipsc_action = ISC_A_REDIRECT;
                isc.ipsc_ip = act->act_ip;
                isc.ipsc_port = act->act_port;
                fprintf(stderr, "%d: redirect unsupported\n", yylineNum + 1);
        }

        if (act->act_else == IPSL_CLOSE) {
                isc.ipsc_else = ISC_A_CLOSE;
        } else if (act->act_else == IPSL_TRACK) {
                isc.ipsc_else = ISC_A_TRACK;
        } else if (act->act_else == IPSL_REDIRECT) {
                isc.ipsc_else = ISC_A_REDIRECT;
                isc.ipsc_eip = act->act_eip;
                isc.ipsc_eport = act->act_eport;
                fprintf(stderr, "%d: redirect unsupported\n", yylineNum + 1);
        }

        if (!(opts & OPT_DONOTHING)) {
                iscp = &isc;
                if (opts & OPT_REMOVE) {
                        if (ioctl(fd, SIOCRMSCA, &iscp) == -1)
                                perror("SIOCADSCA");
                } else {
                        if (ioctl(fd, SIOCADSCA, &iscp) == -1)
                                perror("SIOCADSCA");
                }
        }

        if (opts & OPT_VERBOSE)
                printent(&isc);
}


char **
makepair(char *s1, char *s2)
{
        char **a;

        a = malloc(sizeof(char *) * 2);
        a[0] = s1;
        a[1] = s2;
        return(a);
}


struct in_addr
combine(int a1, int a2, int a3, int a4)
{
        struct in_addr in;

        a1 &= 0xff;
        in.s_addr = a1 << 24;
        a2 &= 0xff;
        in.s_addr |= (a2 << 16);
        a3 &= 0xff;
        in.s_addr |= (a3 << 8);
        a4 &= 0xff;
        in.s_addr |= a4;
        in.s_addr = htonl(in.s_addr);
        return(in);
}


struct in_addr
gethostip(char *host)
{
        struct hostent *hp;
        struct in_addr in;

        in.s_addr = 0;

        hp = gethostbyname(host);
        if (!hp)
                return(in);
        bcopy(hp->h_addr, (char *)&in, sizeof(in));
        return(in);
}


int
getportnum(char *port)
{
        struct servent *s;

        s = getservbyname(port, "tcp");
        if (s == NULL)
                return(-1);
        return(s->s_port);
}


void
showlist(void)
{
        ipscanstat_t ipsc, *ipscp = &ipsc;
        ipscan_t isc;

        if (ioctl(fd, SIOCGSCST, &ipscp) == -1)
                perror("ioctl(SIOCGSCST)");
        else if (opts & OPT_SHOWLIST) {
                while (ipsc.iscs_list != NULL) {
                        if (kmemcpy((char *)&isc, (u_long)ipsc.iscs_list,
                                    sizeof(isc)) == -1) {
                                perror("kmemcpy");
                                break;
                        } else {
                                printent(&isc);
                                ipsc.iscs_list = isc.ipsc_next;
                        }
                }
        } else {
                printf("scan entries loaded\t%d\n", ipsc.iscs_entries);
                printf("scan entries matches\t%ld\n", ipsc.iscs_acted);
                printf("negative matches\t%ld\n", ipsc.iscs_else);
        }
}


void
usage(char *prog)
{
        fprintf(stderr, "Usage:\t%s [-dnrv] -f <filename>\n", prog);
        fprintf(stderr, "\t%s [-dlv]\n", prog);
        exit(1);
}


int
main(int argc, char *argv[])
{
        FILE *fp = NULL;
        int c;

        (void) yysettab(yywords);

        if (argc < 2)
                usage(argv[0]);

        while ((c = getopt(argc, argv, "df:lnrsv")) != -1)
                switch (c)
                {
                case 'd' :
                        opts |= OPT_DEBUG;
                        yydebug++;
                        break;
                case 'f' :
                        if (!strcmp(optarg, "-"))
                                fp = stdin;
                        else {
                                fp = fopen(optarg, "r");
                                if (!fp) {
                                        perror("open");
                                        exit(1);
                                }
                        }
                        yyin = fp;
                        break;
                case 'l' :
                        opts |= OPT_SHOWLIST;
                        break;
                case 'n' :
                        opts |= OPT_DONOTHING;
                        break;
                case 'r' :
                        opts |= OPT_REMOVE;
                        break;
                case 's' :
                        opts |= OPT_STAT;
                        break;
                case 'v' :
                        opts |= OPT_VERBOSE;
                        break;
                }

        if (!(opts & OPT_DONOTHING)) {
                fd = open(IPL_SCAN, O_RDWR);
                if (fd == -1) {
                        perror("open(IPL_SCAN)");
                        exit(1);
                }
        }

        if (fp != NULL) {
                yylineNum = 1;

                while (!feof(fp))
                        yyparse();
                fclose(fp);
                exit(0);
        }

        if (opts & (OPT_SHOWLIST|OPT_STAT)) {
                showlist();
                exit(0);
        }
        exit(1);
}