diff options
-rw-r--r-- | source3/aparser/Makefile | 20 | ||||
-rw-r--r-- | source3/aparser/parser.awk | 200 | ||||
-rw-r--r-- | source3/aparser/parser.c | 411 | ||||
-rw-r--r-- | source3/aparser/parser.h | 98 | ||||
-rw-r--r-- | source3/aparser/test.struct | 23 | ||||
-rw-r--r-- | source3/aparser/vluke.c | 38 |
6 files changed, 790 insertions, 0 deletions
diff --git a/source3/aparser/Makefile b/source3/aparser/Makefile new file mode 100644 index 0000000000..e9ff8ad010 --- /dev/null +++ b/source3/aparser/Makefile @@ -0,0 +1,20 @@ +CFLAGS=-Wall -g +CC=gcc + +%.h : %.struct + awk -f parser.awk < $*.struct > $*.h + +OBJ = harness.o parser.o + +all: test.h harness + +test.h : test.struct + +harness: test.h $(OBJ) + $(CC) $(CFLAGS) -o harness $(OBJ) + +clean: + rm -f *.o test.h + +test: harness + ./harness test.dat > test.out diff --git a/source3/aparser/parser.awk b/source3/aparser/parser.awk new file mode 100644 index 0000000000..a32d4b727b --- /dev/null +++ b/source3/aparser/parser.awk @@ -0,0 +1,200 @@ +function add_elem(type, elem) +{ + types[num_elems] = type; + elems[num_elems] = elem; + num_elems++; +} + +function produce_preamble() { + printf("#define TEST_STRUCT %s\n", struct_name); + printf("#define TEST_FUNC %s\n", func_name); + printf("#define TEST_NAME \"%s\"\n", func_name); + printf("\n\n"); +} + +function produce_header() { + printf("\n/* %s structure */\n", struct_name); + printf("typedef struct {\n"); + for (i=0;i<num_elems;i++) { + if (types[i] == "UNISTR2") { + printf("\tuint32 %s_ptr;\n", elems[i]); + } else if (types[i] == "BUFFER5") { + printf("\tuint32 %s_len;\n", elems[i]); + printf("\tuint32 %s_ptr;\n", elems[i]); + } else { + printf("\t%s\t%s;\n", types[i], elems[i]); + } + } + for (i=0;i<num_elems;i++) { + if (types[i] == "UNISTR2" || + types[i] == "BUFFER5") { + printf("\t%s\t%s;\n", types[i], elems[i]); + } + } + printf("} %s;\n\n", struct_name); +} + + +function parse_structs() { + printf("\n\t/* parse the structures in the packet */\n\n"); + for (i=0;i<num_elems;i++) { + if (types[i] == "UNISTR2") { + io_unistr2(elems[i]); + } + if (types[i] == "BUFFER5") { + io_buffer5(elems[i]); + } + } +} + +function io_unistr2(elem) { + printf("\ + if(!smb_io_unistr2(\"%s\", &il->%s, il->%s_ptr, ps, depth))\n\ + return False;\n\ + if(!prs_align(ps))\n\ + return False;\n\ +", elem, elem, elem); +} + +function io_buffer5(elem) { + printf("\ + if (il->%s_ptr) {\n\ + if(!smb_io_buffer5(\"%s\", &il->%s, ps, depth))\n\ + return False;\n\ + if(!prs_align(ps))\n\ + return False;\n\ + }\n\ +", elem, elem, elem); +} + +function start_struct(name) { + num_elems=0; + struct_name=toupper(module"_"name); + func_name=tolower(module"_io_"name); +} + +function parse_elems() { + printf("\n\t/* parse the main elements the packet */\n\n"); + for (i=0;i<num_elems;i++) { + if (types[i] == "uint32") { + uint32_parser(elems[i]); + } + if (types[i] == "UINT64_S") { + uint64_parser(elems[i]); + } + if (types[i] == "UNISTR2") { + unistr2_parser(elems[i]); + } + if (types[i] == "BUFFER5") { + buffer5_parser(elems[i]); + } + if (types[i] == "NTTIME") { + nttime_parser(elems[i]); + } + } +} + +function end_struct() { + produce_preamble(); + produce_header(); + func_header(func_name, struct_name); + parse_elems(); + parse_structs(); + func_footer(); +} + + + +function func_footer() { + printf("\n\ +\n\ + return True;\n\ +}\n"); +} + +function func_header(func_name, struct_name) +{ + printf("\ +/*******************************************************************\n\ +parse a %s structure\n\ +********************************************************************/ \n\ +BOOL %s(char *desc, %s **q_u, \n\ + prs_struct *ps, int depth)\n\ +{ \n\ + %s *il;\n\ + \n\ + prs_debug(ps, depth, desc, \"%s\");\n\ + depth++;\n\ + \n\ + /* reading */\n\ + if (UNMARSHALLING(ps)) {\n\ + il=(%s *)malloc(sizeof(%s));\n\ + if(il == NULL)\n\ + return False;\n\ + ZERO_STRUCTP(il);\n\ + *q_u=il;\n\ + }\n\ + else {\n\ + il=*q_u;\n\ + }\n\ + \n\ + if(!prs_align(ps))\n\ + return False;\n\ +\n\ +", struct_name, func_name, struct_name, struct_name, func_name, struct_name, struct_name); +} + +function uint32_parser(elem) { + printf("\ + if(!prs_uint32(\"%s\", ps, depth, &il->%s))\n\ + return False;\n\ +", elem, elem); +} + +function unistr2_parser(elem) { + printf("\ + if(!prs_uint32(\"%s_ptr\", ps, depth, &il->%s_ptr))\n\ + return False;\n\ +", elem, elem); +} + +function buffer5_parser(elem) { + printf("\ + if(!prs_uint32(\"%s_len\", ps, depth, &il->%s_len))\n\ + return False;\n\ + if(!prs_uint32(\"%s_ptr\", ps, depth, &il->%s_ptr))\n\ + return False;\n\ +", elem, elem, elem, elem); +} + +function nttime_parser(elem) { + printf("\ + if(!smb_io_time(\"%s\", &il->%s, ps, depth))\n\ + return False;\n\ +", elem, elem); +} + +function uint64_parser(elem) { + printf("\ + if(!prs_uint64(\"%s\", ps, depth, &il->%s))\n\ + return False;\n\ +", elem, elem); +} + +/^module/ { + module=$2; +} + +/^struct/ { + start_struct($2); +} + + +/^\}/ { + end_struct(); +} + +/uint32|UINT64_S|UNISTR2|BUFFER5|NTTIME/ { + split($0,a,"[ ;]*"); + add_elem(a[2], a[3]); +} diff --git a/source3/aparser/parser.c b/source3/aparser/parser.c new file mode 100644 index 0000000000..f938c58e8b --- /dev/null +++ b/source3/aparser/parser.c @@ -0,0 +1,411 @@ +#include <stdio.h> +#include <stdlib.h> +#include <malloc.h> +#include <unistd.h> +#include "parser.h" + +char *tab_depth(int depth) +{ + static pstring spaces; + memset(spaces, ' ', depth * 4); + spaces[depth * 4] = 0; + return spaces; +} + +/**************************************************************************** +expand a pointer to be a particular size +****************************************************************************/ +void *Realloc(void *p,size_t size) +{ + void *ret=NULL; + + if (size == 0) { + if (p) free(p); + DEBUG(5,("Realloc asked for 0 bytes\n")); + return NULL; + } + + if (!p) + ret = (void *)malloc(size); + else + ret = (void *)realloc(p,size); + + if (!ret) + DEBUG(0,("Memory allocation error: failed to expand to %d bytes\n",(int)size)); + + return(ret); +} + +/******************************************************************* + Attempt, if needed, to grow a data buffer. + Also depends on the data stream mode (io). + ********************************************************************/ + +BOOL prs_grow(prs_struct *ps, uint32 extra_space) +{ + uint32 new_size; + char *new_data; + + if(ps->data_offset + extra_space <= ps->buffer_size) + return True; + + /* + * We cannot grow the buffer if we're not reading + * into the prs_struct, or if we don't own the memory. + */ + + if(UNMARSHALLING(ps) || !ps->is_dynamic) { + DEBUG(0,("prs_grow: Buffer overflow - unable to expand buffer by %u bytes.\n", + (unsigned int)extra_space)); + return False; + } + + /* + * Decide how much extra space we really need. + */ + + extra_space -= (ps->buffer_size - ps->data_offset); + if(ps->buffer_size == 0) { + /* + * Ensure we have at least a PDU's length, or extra_space, whichever + * is greater. + */ + + new_size = MAX(MAX_PDU_FRAG_LEN,extra_space); + + if((new_data = malloc(new_size)) == NULL) { + DEBUG(0,("prs_grow: Malloc failure for size %u.\n", (unsigned int)new_size)); + return False; + } + memset(new_data, '\0', new_size ); + } else { + /* + * If the current buffer size is bigger than the space needed, just + * double it, else add extra_space. + */ + new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space); + + if ((new_data = Realloc(ps->data_p, new_size)) == NULL) { + DEBUG(0,("prs_grow: Realloc failure for size %u.\n", + (unsigned int)new_size)); + return False; + } + } + ps->buffer_size = new_size; + ps->data_p = new_data; + + return True; +} + + +/******************************************************************* + Ensure we can read/write to a given offset. + ********************************************************************/ + +char *prs_mem_get(prs_struct *ps, uint32 extra_size) +{ + if(UNMARSHALLING(ps)) { + /* + * If reading, ensure that we can read the requested size item. + */ + if (ps->data_offset + extra_size > ps->buffer_size) { + DEBUG(0,("prs_mem_get: reading data of size %u would overrun buffer.\n", + (unsigned int)extra_size )); + return NULL; + } + } else { + /* + * Writing - grow the buffer if needed. + */ + if(!prs_grow(ps, extra_size)) + return False; + } + return &ps->data_p[ps->data_offset]; +} + + +/******************************************************************* + Stream a uint32. + ********************************************************************/ + +BOOL prs_uint32(char *name, prs_struct *ps, int depth, uint32 *data32) +{ + char *q = prs_mem_get(ps, sizeof(uint32)); + if (q == NULL) + return False; + + DBG_RW_IVAL(name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, *data32) + ps->data_offset += sizeof(uint32); + + return True; +} + +/******************************************************************* + Initialise a parse structure - malloc the data if requested. + ********************************************************************/ + +BOOL prs_init(prs_struct *ps, uint32 size, uint8 align, BOOL io) +{ + ZERO_STRUCTP(ps); + ps->io = io; + ps->bigendian_data = False; + ps->align = align; + ps->is_dynamic = False; + ps->data_offset = 0; + ps->buffer_size = 0; + ps->data_p = NULL; + + if (size != 0) { + ps->buffer_size = size; + if((ps->data_p = (char *)malloc((size_t)size)) == NULL) { + DEBUG(0,("prs_init: malloc fail for %u bytes.\n", (unsigned int)size)); + return False; + } + ps->is_dynamic = True; /* We own this memory. */ + } + + return True; +} + +/******************************************************************* + debug output for parsing info. + + XXXX side-effect of this function is to increase the debug depth XXXX + + ********************************************************************/ +void prs_debug(prs_struct *ps, int depth, char *desc, char *fn_name) +{ + DEBUG(5+depth, ("%s%06x %s %s\n", tab_depth(depth), ps->data_offset, fn_name, desc)); +} + +/******************************************************************* + Align a the data_len to a multiple of align bytes - filling with + zeros. + ********************************************************************/ + +BOOL prs_align(prs_struct *ps) +{ + uint32 mod = ps->data_offset & (ps->align-1); + + if (ps->align != 0 && mod != 0) { + uint32 extra_space = (ps->align - mod); + if(!prs_grow(ps, extra_space)) + return False; + memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space); + ps->data_offset += extra_space; + } + + return True; +} + +/******************************************************************* + Reads or writes an NTTIME structure. +********************************************************************/ + +BOOL smb_io_time(char *desc, NTTIME *nttime, prs_struct *ps, int depth) +{ + if (nttime == NULL) + return False; + + prs_debug(ps, depth, desc, "smb_io_time"); + depth++; + + if(!prs_align(ps)) + return False; + + if(!prs_uint32("low ", ps, depth, &nttime->low)) /* low part */ + return False; + if(!prs_uint32("high", ps, depth, &nttime->high)) /* high part */ + return False; + + return True; +} + + +/******************************************************************* + Reads or writes a UNISTR2 structure. + XXXX NOTE: UNISTR2 structures need NOT be null-terminated. + the uni_str_len member tells you how long the string is; + the uni_max_len member tells you how large the buffer is. +********************************************************************/ + +BOOL smb_io_unistr2(char *desc, UNISTR2 *uni2, uint32 buffer, prs_struct *ps, int depth) +{ + if (uni2 == NULL) + return False; + + if (buffer) { + + prs_debug(ps, depth, desc, "smb_io_unistr2"); + depth++; + + if(!prs_align(ps)) + return False; + + if(!prs_uint32("uni_max_len", ps, depth, &uni2->uni_max_len)) + return False; + if(!prs_uint32("undoc ", ps, depth, &uni2->undoc)) + return False; + if(!prs_uint32("uni_str_len", ps, depth, &uni2->uni_str_len)) + return False; + + /* oops! XXXX maybe issue a warning that this is happening... */ + if (uni2->uni_max_len > MAX_UNISTRLEN) + uni2->uni_max_len = MAX_UNISTRLEN; + if (uni2->uni_str_len > MAX_UNISTRLEN) + uni2->uni_str_len = MAX_UNISTRLEN; + + /* buffer advanced by indicated length of string + NOT by searching for null-termination */ + if(!prs_unistr2(True, "buffer ", ps, depth, uni2)) + return False; + + } else { + + prs_debug(ps, depth, desc, "smb_io_unistr2 - NULL"); + depth++; + memset((char *)uni2, '\0', sizeof(*uni2)); + + } + + return True; +} + +/****************************************************************** + Stream a unicode string, length/buffer specified separately, + in uint16 chars. We use DBG_RW_PCVAL, not DBG_RW_PSVAL here + as the unicode string is already in little-endian format. + ********************************************************************/ + +BOOL prs_unistr2(BOOL charmode, char *name, prs_struct *ps, int depth, UNISTR2 *str) +{ + char *p = (char *)str->buffer; + char *q = prs_mem_get(ps, str->uni_str_len * sizeof(uint16)); + if (q == NULL) + return False; + + /* If we're using big-endian, reverse to get little-endian. */ + if(ps->bigendian_data) + DBG_RW_PSVAL(charmode, name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, p, str->uni_str_len) + else + DBG_RW_PCVAL(charmode, name, depth, ps->data_offset, ps->io, q, p, str->uni_str_len * 2) + ps->data_offset += (str->uni_str_len * sizeof(uint16)); + + return True; +} + +void print_asc(int level, unsigned char *buf,int len) +{ + int i; + for (i=0;i<len;i++) + DEBUG(level,("%c", isprint(buf[i])?buf[i]:'.')); +} + +/******************************************************************* + read from a socket into memory. + ********************************************************************/ +BOOL prs_read(prs_struct *ps, int fd, size_t len, int timeout) +{ + BOOL ok; + size_t prev_size = ps->buffer_size; + if (!prs_grow(ps, len)) + { + return False; + } + + if (timeout > 0) + { + ok = (read(fd, &ps->data_p[prev_size], len) == len); + } + else + { + ok = (read(fd, &ps->data_p[prev_size], len) == len); + } + return ok; +} + +void dump_data(int level,char *buf1,int len) +{ + unsigned char *buf = (unsigned char *)buf1; + int i=0; + if (len<=0) return; + + DEBUG(level,("[%03X] ",i)); + for (i=0;i<len;) { + DEBUG(level,("%02X ",(int)buf[i])); + i++; + if (i%8 == 0) DEBUG(level,(" ")); + if (i%16 == 0) { + print_asc(level,&buf[i-16],8); DEBUG(level,(" ")); + print_asc(level,&buf[i-8],8); DEBUG(level,("\n")); + if (i<len) DEBUG(level,("[%03X] ",i)); + } + } + if (i%16) { + int n; + + n = 16 - (i%16); + DEBUG(level,(" ")); + if (n>8) DEBUG(level,(" ")); + while (n--) DEBUG(level,(" ")); + + n = MIN(8,i%16); + print_asc(level,&buf[i-(i%16)],n); DEBUG(level,(" ")); + n = (i%16) - n; + if (n>0) print_asc(level,&buf[i-n],n); + DEBUG(level,("\n")); + } +} + +/******************************************************************* + Stream a uint64_struct + ********************************************************************/ +BOOL prs_uint64(char *desc, prs_struct *ps, int depth, UINT64_S *data64) +{ + prs_debug(ps, depth, desc, "prs_uint64"); + return prs_uint32("low", ps, depth+1, &data64->low) && + prs_uint32("high", ps, depth+1, &data64->high); +} + + + +/******************************************************************* +reads or writes a BUFFER5 structure. +the buf_len member tells you how large the buffer is. +********************************************************************/ +BOOL smb_io_buffer5(char *desc, BUFFER5 *buf5, prs_struct *ps, int depth) +{ + prs_debug(ps, depth, desc, "smb_io_buffer5"); + depth++; + + if (buf5 == NULL) return False; + + prs_align(ps); + prs_uint32("buf_len", ps, depth, &(buf5->buf_len)); + + /* reading: alloc the buffer first */ + if ( ps->io ) + { + buf5->buffer=(uint16 *)malloc( sizeof(uint16)*buf5->buf_len ); + } + + prs_uint16s(True, "buffer", ps, depth, buf5->buffer, buf5->buf_len); + + return True; +} + +/****************************************************************** + Stream an array of uint16s. Length is number of uint16s. + ********************************************************************/ + +BOOL prs_uint16s(BOOL charmode, char *name, prs_struct *ps, int depth, uint16 *data16s, int len) +{ + char *q = prs_mem_get(ps, len * sizeof(uint16)); + if (q == NULL) + return False; + + DBG_RW_PSVAL(charmode, name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, data16s, len) + ps->data_offset += (len * sizeof(uint16)); + + return True; +} diff --git a/source3/aparser/parser.h b/source3/aparser/parser.h new file mode 100644 index 0000000000..196aa6b855 --- /dev/null +++ b/source3/aparser/parser.h @@ -0,0 +1,98 @@ +#include <ctype.h> +#include "../include/byteorder.h" + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif +/* Maximum PDU fragment size. */ +#define MAX_PDU_FRAG_LEN 0x1630 + +#define DEBUG(lvl, str) printf str; + +#define MARSHALL 0 +#define UNMARSHALL 1 + +#define MARSHALLING(ps) (!(ps)->io) +#define UNMARSHALLING(ps) ((ps)->io) + +typedef int BOOL; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned uint32; + +#define False 0 +#define True 1 + +typedef char pstring[1024]; + +/* zero a structure given a pointer to the structure */ +#define ZERO_STRUCTP(x) { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } + +typedef struct { + uint32 low; + uint32 high; +} UINT64_S; + +typedef struct +{ + uint32 low; + uint32 high; +} NTTIME; + +#define MAX_UNISTRLEN 256 +#define MAX_STRINGLEN 256 +#define MAX_BUFFERLEN 512 + +/* UNISTR2 - unicode string size (in uint16 unicode chars) and buffer */ +typedef struct unistr2_info +{ + uint32 uni_max_len; + uint32 undoc; + uint32 uni_str_len; + /* unicode characters. ***MUST*** be little-endian. **NOT** necessarily null-terminated */ + uint16 buffer[MAX_UNISTRLEN]; + +} UNISTR2; + +/* BUFFER5 */ +typedef struct buffer5_info +{ + uint32 buf_len; + uint16 *buffer; /* data */ +} BUFFER5; + +typedef struct _prs_struct +{ + BOOL io; /* parsing in or out of data stream */ + /* + * If the (incoming) data is big-endian. On output we are + * always little-endian. + */ + BOOL bigendian_data; + uint8 align; /* data alignment */ + BOOL is_dynamic; /* Do we own this memory or not ? */ + uint32 data_offset; /* Current working offset into data. */ + uint32 buffer_size; /* Current size of the buffer. */ + char *data_p; /* The buffer itself. */ +} prs_struct; + + +char *prs_mem_get(prs_struct *ps, uint32 extra_size); +BOOL prs_uint32(char *name, prs_struct *ps, int depth, uint32 *data32); +BOOL prs_init(prs_struct *ps, uint32 size, uint8 align, BOOL io); +void prs_debug(prs_struct *ps, int depth, char *desc, char *fn_name); +BOOL prs_align(prs_struct *ps); +BOOL smb_io_time(char *desc, NTTIME *nttime, prs_struct *ps, int depth); +BOOL smb_io_unistr2(char *desc, UNISTR2 *uni2, uint32 buffer, prs_struct *ps, int depth); +BOOL prs_unistr2(BOOL charmode, char *name, prs_struct *ps, int depth, UNISTR2 *str); +void print_asc(int level, unsigned char *buf,int len); +BOOL prs_read(prs_struct *ps, int fd, size_t len, int timeout); +void dump_data(int level,char *buf1,int len); +BOOL prs_uint64(char *name, prs_struct *ps, int depth, UINT64_S *data64); +BOOL smb_io_buffer5(char *desc, BUFFER5 *buf5, prs_struct *ps, int depth); +BOOL prs_uint16s(BOOL charmode, char *name, prs_struct *ps, int depth, uint16 *data16s, int len); + diff --git a/source3/aparser/test.struct b/source3/aparser/test.struct new file mode 100644 index 0000000000..517197bac3 --- /dev/null +++ b/source3/aparser/test.struct @@ -0,0 +1,23 @@ +module spool +struct PRINTER_DRIVER_INFO_LEVEL_6 { + uint32 dummy1; + uint32 version; + UNISTR2 name; + UNISTR2 environment; + UNISTR2 driverpath; + UNISTR2 datafile; + UNISTR2 configfile; + UNISTR2 helpfile; + UNISTR2 monitorname; + UNISTR2 defaultdatatype; + BUFFER5 dependentfiles; + BUFFER5 previousnames; + NTTIME driverdate; + UINT64_S driverversion; + uint32 dummy4; + UNISTR2 mfgname; + UNISTR2 oemurl; + UNISTR2 hardwareid; + UNISTR2 provider; +}; + diff --git a/source3/aparser/vluke.c b/source3/aparser/vluke.c new file mode 100644 index 0000000000..5e143016be --- /dev/null +++ b/source3/aparser/vluke.c @@ -0,0 +1,38 @@ +#include <stdio.h> +#include <stdlib.h> +#include <malloc.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include "parser.h" +#include "test.h" + +int main(int argc, char *argv[]) +{ + BOOL ret; + TEST_STRUCT *q_u; + char *desc = TEST_NAME; + char *fname = argv[1]; + int fd; + struct stat st; + prs_struct ps; + + if (argc < 2) { + printf("usage: harness <file>\n"); + exit(1); + } + + fd = open(fname,O_RDONLY); + fstat(fd, &st); + + prs_init(&ps, 0, 4, MARSHALL); + ps.is_dynamic=True; + prs_read(&ps, fd, st.st_size, 0); + dump_data(0, ps.data_p, ps.buffer_size); + ps.data_offset = 0; + ps.io = UNMARSHALL; + ret = TEST_FUNC(desc, &q_u, &ps, 1); + printf("ret=%d\n", ret); + return !ret; +} |