#include #include #include #include #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;ibuffer_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;i8) 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; }