#include #include #include #include #include #include #include #include extern const char *__progname; /* Why is this not in a .h, rather than existing only in tcpdump source? */ #define TCPDUMP_MAGIC 0xa1b2c3d4 typedef struct pcap_file_header PFH; typedef struct pcap_pkthdr PP; typedef enum { OP_I_CONST = 1, OP_S_CONST, OP_LT, OP_LE, OP_GT, OP_GE, OP_EQ, OP_NE, OP_NEG, OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_DIV0, OP_MOD0, OP_LSH, OP_RSH, OP_LOGAND, OP_LOGOR, OP_LOGNOT, OP_COND, OP_TIME, OP_YEAR, OP_MONTH, OP_DAY, OP_DATE, OP_HOUR, OP_MIN, OP_SEC, OP_HMS, OP_CONTAINS } OP; typedef enum { PSET_NODE = 1, PSET_TOK } PSETYPE; typedef enum { TT_EOF = 1, TT_I, TT_S, TT_LPAR, TT_RPAR, TT_LT, TT_LE, TT_GT, TT_GE, TT_EQ, TT_NE, TT_ADD, TT_SUB, TT_MUL, TT_DIV, TT_MOD, TT_DIV0, TT_MOD0, TT_LSH, TT_RSH, TT_LOGAND, TT_LOGOR, TT_LOGNOT, TT_QUES, TT_COLON, TT_TIME, TT_YEAR, TT_MONTH, TT_DAY, TT_DATE, TT_HOUR, TT_MIN, TT_SEC, TT_HMS, TT_CONTAINS } TOKTYPE; typedef struct node NODE; typedef struct pse PSE; typedef struct token TOKEN; struct token { TOKTYPE type; union { unsigned int ival; struct { int off; int len; } sval; } ; } ; struct pse { PSETYPE type; union { NODE *n; TOKTYPE tok; } ; } ; struct node { OP op; union { unsigned int iconst; char *sconst; struct { NODE *arg; } unary; struct { NODE *lhs; NODE *rhs; } binary; struct { NODE *test; NODE *ift; NODE *iff; } cond; struct { int len; unsigned char (*tbl)[256]; } contains; } ; } ; static void (*ipktfixup)(PP *); static PFH ifh; static PP iph; static struct tm *iphtm; static char *ipktbuf; static int ipktalloc; static PFH ohdr; static char *parse_str; static int parse_len; static int parse_off; static NODE *expr; static PSE **tokvec; static int tva; static int tvn; static int tvx; #define N4(x) (x), (x)+1, (x)+2, (x)+3 #define N16(x) N4(x), N4((x)+4), N4((x)+8), N4((x)+12) #define N64(x) N16(x), N16((x)+16), N16((x)+32), N16((x)+48) static const unsigned char trivial_equiv[256] = { N64(0), N64(64), N64(128), N64(192) }; #undef N4 #undef N16 #undef N64 #define Cisspace(x) isspace((unsigned char)(x)) #define Cisdigit(x) isdigit((unsigned char)(x)) #define Cisalpha(x) isalpha((unsigned char)(x)) static unsigned int swap32(unsigned int v) { return( ((v >> 24) & 0x000000ff) | ((v >> 8) & 0x0000ff00) | ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24) ); } static unsigned int swap16(unsigned int v) { return( ((v >> 8) & 0x00ff) | ((v & 0x00ff) << 8) ); } static void pktfixup_native(PP *pp __attribute__((__unused__))) { } static void pktfixup_swapped(PP *pp) { pp->ts.tv_sec = swap32(pp->ts.tv_sec); pp->ts.tv_usec = swap32(pp->ts.tv_usec); pp->caplen = swap32(pp->caplen); pp->len = swap32(pp->len); } static void start_input(void) { if (fread(&ifh,sizeof(PFH),1,stdin) != 1) { fprintf(stderr,"%s: can't read input header\n",__progname); exit(1); } if (ifh.magic == TCPDUMP_MAGIC) { ipktfixup = &pktfixup_native; } else if (ifh.magic == swap32(TCPDUMP_MAGIC)) { ifh.version_major = swap16(ifh.version_major); ifh.version_minor = swap16(ifh.version_minor); ifh.thiszone = swap32(ifh.thiszone); ifh.sigfigs = swap32(ifh.sigfigs); ifh.snaplen = swap32(ifh.snaplen); ifh.linktype = swap32(ifh.linktype); ipktfixup = &pktfixup_swapped; } else { fprintf(stderr,"%s: input is not pcap (bad magic)\n",__progname); exit(1); } ipktbuf = 0; ipktalloc = 0; } static TOKEN next_token(void) { int i; TOKEN tok; while (1) { if (parse_off >= parse_len) return((TOKEN){.type=TT_EOF}); if (! Cisspace(parse_str[parse_off])) break; parse_off ++; } if (Cisdigit(parse_str[parse_off])) { int base; int ndig; unsigned int nval; int dval; i = parse_off; if ( (parse_len-i >= 2) && (parse_str[i] == '0') && ( (parse_str[i+1] == 'x') || (parse_str[i+1] == 'X') ) ) { base = 16; i += 2; } else if (parse_str[i] == '0') { base = 8; } else { base = 10; } ndig = 0; nval = 0; while (1) { if (i >= parse_len) break; switch (parse_str[i]) { case '0': dval = 0; break; case '1': dval = 1; break; case '2': dval = 2; break; case '3': dval = 3; break; case '4': dval = 4; break; case '5': dval = 5; break; case '6': dval = 6; break; case '7': dval = 7; break; case '8': dval = 8; break; case '9': dval = 9; break; case 'a': case 'A': dval = 10; break; case 'b': case 'B': dval = 11; break; case 'c': case 'C': dval = 12; break; case 'd': case 'D': dval = 13; break; case 'e': case 'E': dval = 14; break; case 'f': case 'F': dval = 15; break; default: dval = base; break; } if (dval >= base) break; nval = (nval * base) + dval; ndig ++; i ++; } if (ndig < 1) { fprintf(stderr,"%s: invalid number\n",__progname); exit(1); } parse_off = i; tok.type = TT_I; tok.ival = nval; return(tok); } else if (parse_str[parse_off] == '"') { int bq; bq = 0; i = parse_off + 1; while <"string"> (1) { if (i >= parse_len) { fprintf(stderr,"%s: unclosed \"\n",__progname); exit(1); } if (bq) { bq = 0; } else { switch (parse_str[i]) { case '\\': bq = 1; break; case '"': break <"string">; } } i ++; } tok.type = TT_S; tok.sval.off = parse_off + 1; tok.sval.len = i - tok.sval.off; parse_off = i + 1; return(tok); } else if (Cisalpha(parse_str[parse_off])) { for (i=parse_off;(i= 2) #define TWOCHAR(a,b) ((256*(unsigned char)(a))+(unsigned char)(b)) { parse_off += 2; switch (TWOCHAR(parse_str[parse_off-2],parse_str[parse_off-1])) { case TWOCHAR('<','='): return((TOKEN){.type=TT_LE}); break; case TWOCHAR('>','='): return((TOKEN){.type=TT_GE}); break; case TWOCHAR('=','='): return((TOKEN){.type=TT_EQ}); break; case TWOCHAR('!','='): return((TOKEN){.type=TT_NE}); break; case TWOCHAR('/','0'): return((TOKEN){.type=TT_DIV0}); break; case TWOCHAR('%','0'): return((TOKEN){.type=TT_MOD0}); break; case TWOCHAR('<','<'): return((TOKEN){.type=TT_LSH}); break; case TWOCHAR('>','>'): return((TOKEN){.type=TT_RSH}); break; case TWOCHAR('&','&'): return((TOKEN){.type=TT_LOGAND}); break; case TWOCHAR('|','|'): return((TOKEN){.type=TT_LOGOR}); break; } parse_off -= 2; } #undef TWOCHAR parse_off ++; switch (parse_str[parse_off-1]) { case '(': return((TOKEN){.type=TT_LPAR }); break; case ')': return((TOKEN){.type=TT_RPAR }); break; case '<': return((TOKEN){.type=TT_LT }); break; case '>': return((TOKEN){.type=TT_GT }); break; case '+': return((TOKEN){.type=TT_ADD }); break; case '-': return((TOKEN){.type=TT_SUB }); break; case '*': return((TOKEN){.type=TT_MUL }); break; case '/': return((TOKEN){.type=TT_DIV }); break; case '%': return((TOKEN){.type=TT_MOD }); break; case '?': return((TOKEN){.type=TT_QUES }); break; case ':': return((TOKEN){.type=TT_COLON }); break; case '!': return((TOKEN){.type=TT_LOGNOT}); break; } parse_off --; fprintf(stderr,"%s: bad operator char `%.1s'\n",__progname,parse_str+parse_off); exit(1); } } static const char *tok_name(TOKTYPE tt) { switch (tt) { case TT_EOF: return("end of expression"); break; case TT_I: return("numeric constant"); break; case TT_S: return("string constant"); break; case TT_LPAR: return("("); break; case TT_RPAR: return(")"); break; case TT_LT: return("<"); break; case TT_LE: return("<="); break; case TT_GT: return(">"); break; case TT_GE: return(">="); break; case TT_EQ: return("=="); break; case TT_NE: return("!="); break; case TT_ADD: return("+"); break; case TT_SUB: return("-"); break; case TT_MUL: return("*"); break; case TT_DIV: return("/"); break; case TT_MOD: return("%"); break; case TT_DIV0: return("/0"); break; case TT_MOD0: return("%0"); break; case TT_LSH: return("<<"); break; case TT_RSH: return(">>"); break; case TT_LOGAND: return("&&"); break; case TT_LOGOR: return("||"); break; case TT_LOGNOT: return("!"); break; case TT_QUES: return("?"); break; case TT_COLON: return(":"); break; case TT_TIME: return("time"); break; case TT_YEAR: return("year"); break; case TT_MONTH: return("month"); break; case TT_DAY: return("day"); break; case TT_DATE: return("date"); break; case TT_HOUR: return("hour"); break; case TT_MIN: return("min"); break; case TT_SEC: return("sec"); break; case TT_HMS: return("hms"); break; case TT_CONTAINS: return("contains"); break; } abort(); } static NODE *new_node(void) { return(malloc(sizeof(NODE))); } static PSE *curpse(void) { if ((tvx < 0) || (tvx > tvn)) abort(); if (tvx == tvn) return(0); return(tokvec[tvx]); } static NODE *parse_leftbin(NODE *(*sub)(void), int nops, ...) { va_list ap; NODE *lhs; NODE *rhs; NODE *n; int i; TOKTYPE tt; OP op; PSE *e; lhs = (*sub)(); while (1) { e = curpse(); if (! e) break; if (e->type != PSET_TOK) break; va_start(ap,nops); do <"found"> { for (i=nops;i>0;i--) { tt = va_arg(ap,TOKTYPE); op = va_arg(ap,OP); if (e->tok == tt) break <"found">; } return(lhs); } while (0); va_end(ap); tvx ++; rhs = (*sub)(); if (! rhs) { fprintf(stderr,"%s: missing RHS to %s\n",__progname,tok_name(e->tok)); exit(1); } n = new_node(); n->op = op; n->binary.lhs = lhs; n->binary.rhs = rhs; lhs = n; } return(lhs); } static NODE *parse_top(void); // forward static NODE *parse_unary(void) { PSE *e; NODE *sub; NODE *n; if (tvx >= tvn) return(0); e = curpse(); if (! e) return(0); if ((e->type == PSET_TOK) && (e->tok == TT_CONTAINS)) { tvx ++; sub = parse_unary(); if (! sub) { fprintf(stderr,"%s: missing argument to `contains'\n",__progname); exit(1); } if (sub->op != OP_S_CONST) { fprintf(stderr,"%s: `contains' argument must be a string\n",__progname); exit(1); } n = new_node(); n->op = OP_CONTAINS; n->contains.len = strlen(sub->sconst); if (n->contains.len > 255) { fprintf(stderr,"%s: `contains' argument too long (max 255)\n",__progname); exit(1); } n->contains.tbl = malloc(n->contains.len*sizeof(*n->contains.tbl)); searchstr_maketbl(n->contains.tbl,sub->sconst,n->contains.len,&trivial_equiv[0]); } else if ((e->type == PSET_TOK) && (e->tok == TT_SUB)) { tvx ++; sub = parse_unary(); if (! sub) { fprintf(stderr,"%s: missing argument to `-'\n",__progname); exit(1); } n = new_node(); n->op = OP_NEG; n->unary.arg = sub; } else if ((e->type == PSET_TOK) && (e->tok == TT_LOGNOT)) { tvx ++; sub = parse_unary(); if (! sub) { fprintf(stderr,"%s: missing argument to `!'\n",__progname); exit(1); } n = new_node(); n->op = OP_LOGNOT; n->unary.arg = sub; } else if ((e->type == PSET_TOK) && (e->tok == TT_LPAR)) { tvx ++; n = parse_top(); if (! n) return(0); e = curpse(); if (! e) { fprintf(stderr,"%s: unclosed (\n",__progname); exit(1); } if ((e->type == PSET_TOK) && (e->tok == TT_RPAR)) { tvx ++; } else { fprintf(stderr,"%s: improperly closed (\n",__progname); exit(1); } } else if (e->type == PSET_NODE) { tvx ++; n = e->n; } return(n); } static NODE *parse_muldiv(void) { return(parse_leftbin(&parse_unary,5, TT_MUL,OP_MUL,TT_DIV,OP_DIV,TT_MOD,OP_MOD, TT_DIV0,OP_DIV0,TT_MOD0,OP_MOD0)); } static NODE *parse_addsub(void) { return(parse_leftbin(&parse_muldiv,2,TT_ADD,OP_ADD,TT_SUB,OP_SUB)); } static NODE *parse_cmp(void) { return(parse_leftbin(&parse_addsub,6, TT_LT,OP_LT,TT_LE,OP_LE,TT_EQ,OP_EQ, TT_GT,OP_GT,TT_GE,OP_GE,TT_NE,OP_NE)); } static NODE *parse_shift(void) { return(parse_leftbin(&parse_cmp,2,TT_LSH,OP_LSH,TT_RSH,OP_RSH)); } static NODE *parse_cond(void) { NODE *lhs; NODE *mhs; NODE *rhs; NODE *n; lhs = parse_shift(); if (lhs && (tvx < tvn) && (curpse()->type == PSET_TOK) && (curpse()->tok == TT_QUES)) { tvx ++; mhs = parse_cond(); if (mhs && (tvx < tvn) && (curpse()->type == PSET_TOK) && (curpse()->tok == TT_COLON)) { tvx ++; rhs = parse_cond(); if (rhs) { n = new_node(); n->op = OP_COND; n->cond.test = lhs; n->cond.ift = mhs; n->cond.iff = rhs; return(n); } else { fprintf(stderr,"%s: botched ? : operator\n",__progname); exit(1); } } else { fprintf(stderr,"%s: botched ? : operator\n",__progname); exit(1); } } else { return(lhs); } } static NODE *parse_logand(void) { return(parse_leftbin(&parse_cond,1,TT_LOGAND,OP_LOGAND)); } static NODE *parse_logor(void) { return(parse_leftbin(&parse_logand,1,TT_LOGOR,OP_LOGOR)); } static NODE *parse_top(void) { return(parse_logor()); } static void parse_expr(int ac, char **av) { int i; int l; int o; TOKEN t; NODE *n; PSE *e; parse_len = -1; for (i=1;i 1) parse_str[o++] = ' '; l = strlen(av[i]); bcopy(av[i],parse_str+o,l); o += l; } if (o != parse_len) abort(); parse_off = 0; tokvec = 0; tva = 0; tvn = 0; while <"tokenize"> (1) { t = next_token(); switch (t.type) { default: abort(); break; case TT_EOF: break <"tokenize">; case TT_I: n = new_node(); n->op = OP_I_CONST; n->iconst = t.ival; break; case TT_S: n = new_node(); n->op = OP_S_CONST; n->sconst = malloc(t.sval.len+1); bcopy(parse_str+t.sval.off,n->sconst,t.sval.len); n->sconst[t.sval.len] = '\0'; break; #define NILADIC(o) do { n = new_node(); n->op = (o); } while (0) case TT_TIME: NILADIC(OP_TIME); break; case TT_YEAR: NILADIC(OP_YEAR); break; case TT_MONTH: NILADIC(OP_MONTH); break; case TT_DAY: NILADIC(OP_DAY); break; case TT_DATE: NILADIC(OP_DATE); break; case TT_HOUR: NILADIC(OP_HOUR); break; case TT_MIN: NILADIC(OP_MIN); break; case TT_SEC: NILADIC(OP_SEC); break; case TT_HMS: NILADIC(OP_HMS); break; #undef NILADIC case TT_LPAR: case TT_RPAR: case TT_LT: case TT_LE: case TT_GT: case TT_GE: case TT_EQ: case TT_NE: case TT_ADD: case TT_SUB: case TT_MUL: case TT_DIV: case TT_MOD: case TT_DIV0: case TT_MOD0: case TT_LSH: case TT_RSH: case TT_LOGAND: case TT_LOGOR: case TT_LOGNOT: case TT_QUES: case TT_COLON: case TT_CONTAINS: n = 0; break; } e = malloc(sizeof(PSE)); if (n) { e->type = PSET_NODE; e->n = n; } else { e->type = PSET_TOK; e->tok = t.type; } if (tvn >= tva) tokvec = realloc(tokvec,(tva=tvn+8)*sizeof(PSE *)); tokvec[tvn++] = e; } tvx = 0; expr = parse_top(); } static int read_packet(void) { int n; n = fread(&iph,1,sizeof(PP),stdin); if (n == sizeof(PP)) { (*ipktfixup)(&iph); if (iph.caplen > ipktalloc) { free(ipktbuf); ipktalloc = iph.caplen; ipktbuf = malloc(ipktalloc); if (ipktbuf == 0) { fprintf(stderr,"%s: can't allocate packet buffer (len=%d)\n",__progname,ipktalloc); return(0); } } n = fread(ipktbuf,1,iph.caplen,stdin); if (n != iph.caplen) { fprintf(stderr,"%s: packet data truncated\n",__progname); return(0); } } else { if (n > 0) { fprintf(stderr,"%s: packet header truncated\n",__progname); } return(0); } iphtm = 0; return(1); } static struct tm *iph_tm(void) { if (! iphtm) { time_t tt; tt = iph.ts.tv_sec; iphtm = localtime(&tt); } return(iphtm); } static int eval(NODE *); // forward static int eval_cmp(NODE *lhs, NODE *rhs, int mask) { int v1; int v2; int bit; v1 = eval(lhs); v2 = eval(rhs); if (v1 < v2) bit = 1; else if (v1 > v2) bit = 4; else bit = 2; return((mask&bit)?1:0); } static int eval_arith(NODE *lhs, NODE *rhs, int (*doit)(int, int)) { int v1; int v2; v1 = eval(lhs); v2 = eval(rhs); return((*doit)(v1,v2)); } static int perform_add(int a, int b) { return(a+b); } static int perform_sub(int a, int b) { return(a-b); } static int perform_mul(int a, int b) { return(a*b); } static int perform_div(int a, int b) { if (b == 0) { fprintf(stderr,"%s: / by zero\n",__progname); exit(1); } return(a/b); } static int perform_mod(int a, int b) { if (b == 0) { fprintf(stderr,"%s: %% by zero\n",__progname); exit(1); } return(a%b); } static int perform_div0(int a, int b) { if (b == 0) return(0); return(a/b); } static int perform_mod0(int a, int b) { if (b == 0) return(0); return(a%b); } static int perform_rsh(int a, int b) { return((b>0)?(a>>b):(a<<-b)); } static int perform_lsh(int a, int b) { return((b>0)?(a<>-b)); } static int eval_logand(NODE *lhs, NODE *rhs) { int v; v = eval(lhs); if (! v) return(0); v = eval(rhs); return(v?1:0); } static int eval_logor(NODE *lhs, NODE *rhs) { int v; v = eval(lhs); if (v) return(1); v = eval(rhs); return(v?1:0); } static int eval(NODE *n) { switch (n->op) { case OP_I_CONST: return(n->iconst); break; case OP_S_CONST: fprintf(stderr,"%s: strings may be used only with `contains'\n",__progname); exit(1); break; case OP_LT: return(eval_cmp(n->binary.lhs,n->binary.rhs,1)); break; case OP_LE: return(eval_cmp(n->binary.lhs,n->binary.rhs,3)); break; case OP_GT: return(eval_cmp(n->binary.lhs,n->binary.rhs,4)); break; case OP_GE: return(eval_cmp(n->binary.lhs,n->binary.rhs,6)); break; case OP_EQ: return(eval_cmp(n->binary.lhs,n->binary.rhs,2)); break; case OP_NE: return(eval_cmp(n->binary.lhs,n->binary.rhs,5)); break; case OP_NEG: return(-eval(n->unary.arg)); break; case OP_ADD: return(eval_arith(n->binary.lhs,n->binary.rhs,&perform_add)); break; case OP_SUB: return(eval_arith(n->binary.lhs,n->binary.rhs,&perform_sub)); break; case OP_MUL: return(eval_arith(n->binary.lhs,n->binary.rhs,&perform_mul)); break; case OP_DIV: return(eval_arith(n->binary.lhs,n->binary.rhs,&perform_div)); break; case OP_MOD: return(eval_arith(n->binary.lhs,n->binary.rhs,&perform_mod)); break; case OP_DIV0: return(eval_arith(n->binary.lhs,n->binary.rhs,&perform_div0)); break; case OP_MOD0: return(eval_arith(n->binary.lhs,n->binary.rhs,&perform_mod0)); break; case OP_LSH: return(eval_arith(n->binary.lhs,n->binary.rhs,&perform_lsh)); break; case OP_RSH: return(eval_arith(n->binary.lhs,n->binary.rhs,&perform_rsh)); break; case OP_LOGAND: return(eval_logand(n->binary.lhs,n->binary.rhs)); break; case OP_LOGOR: return(eval_logor(n->binary.lhs,n->binary.rhs)); break; case OP_LOGNOT: return(!eval(n->unary.arg)); break; case OP_COND: return(eval(eval(n->cond.test)?n->cond.ift:n->cond.iff)); break; case OP_TIME: return(iph.ts.tv_sec); break; case OP_YEAR: return(iph_tm()->tm_year+1900); break; case OP_MONTH: return(iph_tm()->tm_mon+1); break; case OP_DAY: return(iph_tm()->tm_mday); break; case OP_DATE: return( (iph_tm()->tm_year * 10000) + (iph_tm()->tm_mon * 100) + iph_tm()->tm_mday + 19000100 ); break; case OP_HOUR: return(iph_tm()->tm_hour); break; case OP_MIN: return(iph_tm()->tm_min); break; case OP_SEC: return(iph_tm()->tm_sec); break; case OP_HMS: return( (iph_tm()->tm_hour * 10000) + (iph_tm()->tm_min * 100) + iph_tm()->tm_sec ); break; case OP_CONTAINS: return(searchstr(ipktbuf,iph.caplen,n->contains.tbl,n->contains.len)>=0); break; default: abort(); break; } } static void write_packet(void) { fwrite(&iph,sizeof(PP),1,stdout); fwrite(ipktbuf,1,iph.caplen,stdout); } static void write_ohdr(void) { ohdr.magic = TCPDUMP_MAGIC; ohdr.sigfigs = ifh.sigfigs; ohdr.snaplen = ifh.snaplen; ohdr.version_major = ifh.version_major; ohdr.version_minor = ifh.version_minor; ohdr.thiszone = ifh.thiszone; ohdr.linktype = ifh.linktype; fwrite(&ohdr,sizeof(PFH),1,stdout); } int main(int, char **); int main(int ac, char **av) { parse_expr(ac,av); start_input(); write_ohdr(); while (read_packet()) if (eval(expr)) write_packet(); #if 0 while (write_packet()); #endif return(0); }