/* This file is in the public domain. */ #include #include #include #include "str.h" #include "panic.h" #include "nested.h" #include "pkt-util.h" #define PP__INLINE #include "pp.h" typedef struct unwind UNWIND; struct unwind { UNWIND *link; void (*fn)(void *); void *arg; } ; static void parse_it(const char *data, int datalen, void (*f0)(const void *, const char *), void (*f1)(const void *, const char *, int), void (*f2)(const void *, const char *, int, int), va_list ap) { __label__ ret; int key; const unsigned char *ipp; const unsigned char *ipend; int store; UNWIND *unwinds; NESTED void add_unwind(void (*fn)(void *), void *arg) { UNWIND *u; u = malloc(sizeof(UNWIND)); u->fn = fn; u->arg = arg; u->link = unwinds; unwinds = u; } NESTED void *backout_alloc(int nb) { void *blk; blk = malloc(nb); if (! blk) return(0); add_unwind(&pp_free_ptr,blk); return(blk); } NESTED void walk_unwinds(int callthem) { UNWIND *u; while (unwinds) { u = unwinds; unwinds = u->link; if (callthem) (*u->fn)(u->arg); free(u); } } NESTED void fail0(const void *at, const char *fmt) { walk_unwinds(1); (*f0)(at,fmt); panic("f0 returned"); } NESTED void fail1(const void *at, const char *fmt, int arg) { walk_unwinds(1); (*f1)(at,fmt,arg); panic("f1 returned"); } NESTED void fail2(const void *at, const char *fmt, int arg1, int arg2) { walk_unwinds(1); (*f2)(at,fmt,arg1,arg2); panic("f2 returned"); } NESTED void need(unsigned int n) { if (ipend-ipp < n) { walk_unwinds(1); (*f2)(ipp,"packet too short (%d left, need %u)",(int)(ipend-ipp),n); goto ret; } } ipp = data; ipend = ipp + datalen; store = 1; unwinds = 0; while (1) { key = va_arg(ap,int); switch (key) { case PP__SKIP: store = 0; continue; break; case PP__BLOB: { int i; void *vp; i = va_arg(ap,int); vp = va_arg(ap,void *); need(i); if (store) bcopy(ipp,vp,i); ipp += i; } break; case PP__STRING: { int len; STR *sp; sp = va_arg(ap,STR *); need(4); len = get_uint32(ipp); ipp += 4; need(len); if (store) { sp->len = len; sp->data = backout_alloc(sp->len); bcopy(ipp,sp->data,sp->len); } ipp += len; } break; case PP__STRCALL: { ROSTR s; void (*fn)(ROSTR, void *); void *arg; fn = va_arg(ap,__typeof__(fn)); arg = va_arg(ap,void *); need(4); s.len = get_uint32(ipp); ipp += 4; need(s.len); s.data = (const void *) ipp; ipp += s.len; if (store) (*fn)(s,arg); } break; case PP__BOOL: { int *ip; ip = va_arg(ap,int *); need(1); switch (*ipp) { case 0: if (store) *ip = 0; break; case 1: if (store) *ip = 1; break; default: (*f1)(ipp,"invalid boolean 0x%02x",*ipp); break; } ipp ++; } break; case PP__UINT32: { unsigned int *uip; uip = va_arg(ap,unsigned int *); need(4); if (store) *uip = get_uint32(ipp); ipp += 4; } break; case PP__BYTE: { unsigned char *ucp; ucp = va_arg(ap,unsigned char *); need(1); if (store) *ucp = *ipp; ipp ++; } break; case PP__BIGNUM: { int bytes; BIGNUM *mip; mip = va_arg(ap,BIGNUM *); need(4); bytes = get_uint32(ipp); ipp += 4; need(bytes); if (store) { bignum_set_int(mip,0); for (;bytes>0;bytes--) { bignum_shl(mip,mip,8); bignum_add_int(mip,mip,*ipp++); } } else { ipp += bytes; } } break; case PP__ENDSHERE: if (ipp != ipend) (*f0)(ipp,"junk at end of packet"); walk_unwinds(0); return; break; case PP__REST: { const void **bp; int *lenp; bp = va_arg(ap,const void **); lenp = va_arg(ap,int *); if (store) { *bp = ipp; *lenp = ipend - ipp; } } walk_unwinds(0); return; break; case PP__UNWIND: { void (*fn)(void *); void *arg; fn = va_arg(ap,__typeof__(fn)); arg = va_arg(ap,void *); add_unwind(fn,arg); } break; default: panic("bad selector"); break; } store = 1; } ret:; walk_unwinds(0); } /* Annoyingly, we cannot share the bulk of the code between parse_packet and parse_data without going through gyrations with the failure routine. Fortunately, the failure routine is called from only a few places, with only three different call signatures. */ void parse_packet(BPP *bpp, void (*fail)(BPP *, const void *, const char *, ...), ...) { va_list ap; NESTED void f0(const void *ipp, const char *s) { (*fail)(bpp,ipp,s); } NESTED void f1(const void *ipp, const char *s, int i) { (*fail)(bpp,ipp,s,i); } NESTED void f2(const void *ipp, const char *s, int i1, int i2) { (*fail)(bpp,ipp,s,i1,i2); } va_start(ap,fail); parse_it(&bpp->ipkt[0],bpp->iplen,f0,f1,f2,ap); va_end(ap); } void parse_data(const void *data, int datalen, void (*fail)(const void *, const char *, ...), ...) { va_list ap; NESTED void f0(const void *ipp, const char *s) { (*fail)(ipp,s); } NESTED void f1(const void *ipp, const char *s, int i) { (*fail)(ipp,s,i); } NESTED void f2(const void *ipp, const char *s, int i1, int i2) { (*fail)(ipp,s,i1,i2); } va_start(ap,fail); parse_it(data,datalen,f0,f1,f2,ap); va_end(ap); } void pp_free_ptr(void *arg) { free(arg); } void pp_free_str(void *arg) { free_str(*(STR *)arg); }