123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897 |
- /*
- * $Id$
- *
- * Copyright (C) 2006 iptelorg GmbH
- *
- * This file is part of ser, a free SIP server.
- *
- * ser is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- * [email protected]
- *
- * ser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
- /* History:
- * --------
- * 2006-02-08 created by andrei
- * 2006-11-03 replies with no payload are allowed (andrei)
- */
- /* binrpc is supposed to be a minimalist binary rpc implementation */
- /* packet header:
- * (big endian where it applies)
- * 4b 4b 4b 2b 2b <var> <var>
- * | MAGIC | VERS || FLAGS | LL | CL || total_len ... || cookie ... |
- * total_len = payload len (doesn't include the packet header)
- * LL = total length len -1 (number of bytes on which total len is
- * represented)
- * CL = cookie length -1 (number of bytes on which the cookie is represented)
- * E.g.: LL= 0 => total_len is represented on 1 byte (LL+1)
- * CL= 3 => cookie is represneted on 4 bytes (CL+1)
- */
- /* record format:
- * 1b 3b 4b
- * |S | size | type || <optional value len> ... || <optional value> ... ||
- *
- * if S==0, size is the size (in bytes) of the value (if size==0 => null value)
- * if S==1, optional_value_len is present, and size is it's size
- * (if size==0 => and type==array or struct => marks end, else
- * error, reserved)
- * Examples:
- * int (type=0) 0x1234 -> 0x20 0x12 0x34 (optimal)
- * 0x90 0x02 0x12 0x34 (suboptimal)
- * 0xA0 0x00 0x02 0x12 0x34 (even worse)
- * 0x07 -> 0x10 0x07 (optimal)
- * 0x00 -> 0x00 (optimal)
- * 0x10 0x00
- *
- * str (type=1) - strings are 0 terminated (an extra 0 is added to them
- * to make the format easier to parse when asciiz strings
- * are required); the length includes the terminating 0
- * "abcdef" -> 0x71 "abcdef" 0x00
- * "abcdefhij"-> 0x91 0x0A "abcdefhij" 0x00
- * "" -> 0x11 0x00 (0 len str)
- * 65535 bytes
- * (using str for it) -> 0xB1 0x01 0x00 0x00 array 0x00
- *
- * bytes (type=6) -like str but not 0 terminated
- * "abcdef" -> 0x66 "abcdef"
- * 65535 bytes * -> 0xA6 0xff 0xff bytes
- *
- * arrays (array type=4)
- * arrays are implemented as anonymous value lists:
- * array_start value1, value2, ..., array_end
- * (start) (1st int) (2nd elem) (end array)
- * ints [ 0x01 0x02 ] -> 0x04 0x10 0x01 0x10 0x02 0x84
- * combo [ 0x07 "abc"] -> 0x04 0x10 0x07 0x41 "abc" 0x00 0x84
- *
- * structs (struct type=3)
- * structs are implemented as avp list:
- * struct_start, avp1, avp2 .., struct_end
- * an avp is a named value pair: name, value.
- * - name behaves like a normal string, but has a diff. type (5)
- * - avps are legal only inside structs.
- * avp example: name part (str) val part (int here)
- * "test", int 0x0b -> 0x55 "test" 0x00 0x10 0x0b
- *
- * struct example:
- * (start) (avps) (end)
- * struct{ 0x03 (name ) (val)
- * intval: int 0x3 -> 0x75 "intval" 0x00 0x10 0x3
- * s: str "abc"- 0x25 "s" 0x00 0x41 "abc" 0x00
- * } 0x83
- *
- * Limitations: for now avps cannot have array values =>
- * structs cannot contain arrays.
- */
- #ifndef _binrpc_h
- #define _binrpc_h
- #include "../../str.h"
- #include <string.h>
- #define BINRPC_MAGIC 0xA
- #define BINRPC_VERS 1
- /* sizes & offsets */
- #define BINRPC_FIXED_HDR_SIZE 2
- #define BINRPC_TLEN_OFFSET BINRPC_FIXED_HDR_SIZE
- #define BINRPC_MIN_HDR_SIZE (BINRPC_FIXED_HDR_SIZE+2)
- #define BINRPC_MAX_HDR_SIZE (BINRPC_FIXED_HDR_SIZE+4+4)
- #define BINRPC_MIN_RECORD_SIZE 1
- /* min pkt size: min header + min. len (1) + min. cookie (1)*/
- #define BINRPC_MIN_PKT_SIZE BINRPC_MIN_HDR_SIZE
- /* message types */
- #define BINRPC_REQ 0
- #define BINRPC_REPL 1
- #define BINRPC_FAULT 3
- /* values types */
- #define BINRPC_T_INT 0
- #define BINRPC_T_STR 1 /* 0 term, for easier parsing */
- #define BINRPC_T_DOUBLE 2
- #define BINRPC_T_STRUCT 3
- #define BINRPC_T_ARRAY 4
- #define BINRPC_T_AVP 5 /* allowed only in structs */
- #define BINRPC_T_BYTES 6 /* like STR, but not 0 term */
- #define BINRPC_T_ALL 0xf /* wildcard type, will match any record type
- in the packet (not allowed inside the pkt)*/
- /* errors */
- #define E_BINRPC_INVAL -1 /* invalid function call parameters */
- #define E_BINRPC_OVERFLOW -2 /* buffer overflow */
- #define E_BINRPC_BADPKT -3 /* something went really bad, the packet is
- corrupted*/
- #define E_BINRPC_MORE_DATA -4 /* parsing error: more bytes are needed,
- just repeat the failed op, when you have
- more bytes available */
- #define E_BINRPC_EOP -5 /* end of packet reached */
- #define E_BINRPC_NOTINIT -6 /* parse ctx not initialized */
- #define E_BINRPC_TYPE -7 /* unkown type for record, or requested
- type doesn't match record type */
- #define E_BINRPC_RECORD -8 /* bad record (unexpected, bad struct a.s.o)*/
- #define E_BINRPC_BUG -9 /* internal error, bug */
- #define E_BINRPC_LAST -10 /* used to count the errors, keep always
- last */
- /* flags */
- #define BINRPC_F_INIT 1
- struct binrpc_pkt{ /* binrpc body */
- unsigned char* body;
- unsigned char* end;
- unsigned char* crt; /*private */
- };
- struct binrpc_parse_ctx{
- /* header */
- unsigned int tlen; /* total len */
- unsigned int cookie;
- int type; /* request, reply, error */
-
- /* parsing info */
- unsigned int flags; /* parsing flags */
- unsigned int offset; /* current offset (inside payload) */
- unsigned int in_struct;
- unsigned int in_array;
- };
- struct binrpc_val{
- str name; /* used only in structs */
- int type;
- union{
- str strval;
- double fval;
- int intval;
- int end;
- }u;
- };
- /* helper functions */
- /* return int size: minimum number of bytes needed to represent it
- * if i=0 returns 0 */
- inline static int binrpc_get_int_len(int i)
- {
- int size;
- for (size=4; size && ((i & (0xff<<24))==0); i<<=8, size--);
- return size;
- }
- /* adds a start or end tag (valid only for STRUCT or ARRAY for now */
- inline static int binrpc_add_tag(struct binrpc_pkt* pkt, int type, int end)
- {
- if (pkt->crt>=pkt->end) return E_BINRPC_OVERFLOW;
- *pkt->crt=(end<<7)|type;
- pkt->crt++;
- return 0;
- }
- /* writes a minimal int, returns the new offset and sets
- * len to the number of bytes written (<=4)
- * to check for oveflow use: returned_value-p != *len
- * (Note: if *len==0 using the test above succeeds even if p>=end)
- */
- inline static unsigned char* binrpc_write_int( unsigned char* p,
- unsigned char* end,
- int i, int *len)
- {
- int size;
- for (size=4; size && ((i & (0xff<<24))==0); i<<=8, size--);
- *len=size;
- for(; (p<end) && (size); p++, size--){
- *p=(unsigned char)(i>>24);
- i<<=8;
- }
- return p;
- }
- /* API functions */
- /* initialize a binrpc_pkt structure, for packet creation
- * params: pkt - binrpc body structure that will be initialized
- * buf, b_len - destination buffer/len
- * returns -1 on error, 0 on success
- *
- * Example usage:
- * binrpc_init_pkt(pkt, body, BODY_SIZE);
- * binrpc_addint(pkt, 1);
- * binrpc_addstr(pkt, "test", sizeof("test")-1);
- * ...
- * bytes=binrpc_build_hdr(pkt, BINRPC_REQ, 0x123, hdr_buf, HDR_BUF_LEN);
- * writev(sock, {{ hdr, bytes}, {pkt->body, pkt->crt-pkt->body}} , 2)*/
- inline static int binrpc_init_pkt(struct binrpc_pkt *pkt,
- unsigned char* buf, int b_len)
- {
- if (b_len<BINRPC_MIN_RECORD_SIZE)
- return E_BINRPC_OVERFLOW;
- pkt->body=buf;
- pkt->end=buf+b_len;
- pkt->crt=pkt->body;
- return 0;
- };
- /* used to update internal contents if the original buffer
- * (from binrpc_init_pkt) was realloc'ed (and has grown) */
- inline static int binrpc_pkt_update_buf(struct binrpc_pkt *pkt,
- unsigned char* new_buf,
- int new_len)
- {
- if ((int)(pkt->crt-pkt->body)>new_len){
- return E_BINRPC_OVERFLOW;
- }
- pkt->crt=new_buf+(pkt->crt-pkt->body);
- pkt->body=new_buf;
- pkt->end=new_buf+new_len;
- return 0;
- }
- /* builds a binrpc header for the binrpc pkt. body pkt and writes it in buf
- * params:
- * type - binrpc packet type (request, reply, fault)
- * body_len - body len
- * cookie - binrpc cookie value
- * buf,len - destination buffer & len
- * returns -1 on error, number of bytes written on success */
- inline static int binrpc_build_hdr( int type, int body_len,
- unsigned int cookie,
- unsigned char* buf, int b_len)
- {
- unsigned char* p;
- int len_len;
- int c_len;
-
- len_len=binrpc_get_int_len(body_len);
- c_len=binrpc_get_int_len(cookie);
- if (len_len==0) len_len=1; /* we can't have 0 len */
- if (c_len==0) c_len=1; /* we can't have 0 len */
- /* size check: 2 bytes header + len_len + cookie len*/
- if (b_len<(BINRPC_FIXED_HDR_SIZE+len_len+c_len)){
- goto error_len;
- }
- p=buf;
- *p=(BINRPC_MAGIC << 4) | BINRPC_VERS;
- p++;
- *p=(type<<4)|((len_len-1)<<2)|(c_len-1);
- p++;
- for(;len_len>0; len_len--,p++){
- *p=(unsigned char)(body_len>>((len_len-1)*8));
- }
- for(;c_len>0; c_len--,p++){
- *p=(unsigned char)(cookie>>((c_len-1)*8));
- }
- return (int)(p-buf);
- error_len:
- return E_BINRPC_OVERFLOW;
- }
- #define binrpc_pkt_len(pkt) ((int)((pkt)->crt-(pkt)->body))
- /* changes the length of a header (enough space must be availale) */
- inline static int binrpc_hdr_change_len(unsigned char* hdr, int hdr_len,
- int new_len)
- {
- int len_len;
-
- binrpc_write_int(&hdr[BINRPC_TLEN_OFFSET], hdr+hdr_len, new_len, &len_len);
- return 0;
- }
- /* int format: size BINRPC_T_INT <val> */
- inline static int binrpc_add_int_type(struct binrpc_pkt* pkt, int i, int type)
- {
-
- unsigned char* p;
- int size;
-
- p=binrpc_write_int(pkt->crt+1, pkt->end, i, &size);
- if ((pkt->crt>=pkt->end) || ((int)(p-pkt->crt-1)!=size))
- goto error_len;
- *(pkt->crt)=(size<<4) | type;
- pkt->crt=p;
- return 0;
- error_len:
- return E_BINRPC_OVERFLOW;
- }
- /* double format: FIXME: for now a hack: fixed point represented in
- * an int (=> max 3 decimals, < MAX_INT/1000) */
- #define binrpc_add_double_type(pkt, f, type)\
- binrpc_add_int_type((pkt), (int)((f)*1000), (type))
- /* skip bytes bytes (leaves an empty space, for possible future use)
- * WARNING: use with care, low level function
- */
- inline static int binrpc_add_skip(struct binrpc_pkt* pkt, int bytes)
- {
-
- if ((pkt->crt+bytes)>=pkt->end)
- return E_BINRPC_OVERFLOW;
- pkt->crt+=bytes;
- return 0;
- }
- /*
- * adds only the string mark and len, you'll have to memcpy the contents
- * manually later (and also use binrpc_add_skip(pkt, l) or increase
- * pkt->crt directly if you want to continue adding to this pkt).
- * Usefull for optimizing str writing (e.g. writev(iovec))
- * WARNING: use with care, low level function, binrpc_addstr or
- * binrpc_add_str_type are probably what you want.
- * WARNING1: BINRPC_T_STR and BINRPC_T_AVP must be 0 term, the len passed to
- * this function, must include the \0 in this case.
- */
- inline static int binrpc_add_str_mark(struct binrpc_pkt* pkt, int type,
- int l)
- {
- int size;
- unsigned char* p;
-
- if (pkt->crt>=pkt->end) goto error_len;
- if (l<8){
- size=l;
- p=pkt->crt+1;
- }else{ /* we need a separate len */
- p=binrpc_write_int(pkt->crt+1, pkt->end, l, &size);
- if (((int)(p-pkt->crt-1)!=size))
- goto error_len;
- size|=8; /* mark it as having external len */
- }
- *(pkt->crt)=(size)<<4|type;
- pkt->crt=p;
- return 0;
- error_len:
- return E_BINRPC_OVERFLOW;
- }
- inline static int binrpc_add_str_type(struct binrpc_pkt* pkt, char* s, int len,
- int type)
- {
- int size;
- int l;
- int zero_term; /* whether or not to add an extra 0 at the end */
- unsigned char* p;
-
- zero_term=((type==BINRPC_T_STR)||(type==BINRPC_T_AVP));
- l=len+zero_term;
- if (l<8){
- size=l;
- p=pkt->crt+1;
- }else{ /* we need a separate len */
- p=binrpc_write_int(pkt->crt+1, pkt->end, l, &size);
- /* if ((int)(p-pkt->crt)<(size+1)) goto error_len; - not needed,
- * caught by the next check */
- size|=8; /* mark it as having external len */
- }
- if ((p+l)>pkt->end) goto error_len;
- *(pkt->crt)=(size)<<4|type;
- memcpy(p, s, len);
- if (zero_term) p[len]=0;
- pkt->crt=p+l;
- return 0;
- error_len:
- return E_BINRPC_OVERFLOW;
- }
- /* adds an avp (name, value) pair, usefull to add structure members */
- inline static int binrpc_addavp(struct binrpc_pkt* pkt, struct binrpc_val* avp)
- {
- int ret;
- unsigned char* bak;
-
- bak=pkt->crt;
- ret=binrpc_add_str_type(pkt, avp->name.s, avp->name.len, BINRPC_T_AVP);
- if (ret<0) return ret;
- switch (avp->type){
- case BINRPC_T_INT:
- ret=binrpc_add_int_type(pkt, avp->u.intval, avp->type);
- break;
- case BINRPC_T_STR:
- case BINRPC_T_BYTES:
- ret=binrpc_add_str_type(pkt, avp->u.strval.s,
- avp->u.strval.len,
- avp->type);
- break;
- case BINRPC_T_STRUCT:
- case BINRPC_T_ARRAY:
- ret=binrpc_add_tag(pkt, avp->type, 0);
- break;
- case BINRPC_T_DOUBLE:
- ret=binrpc_add_double_type(pkt, avp->u.fval, avp->type);
- break;
- default:
- ret=E_BINRPC_BUG;
- }
- if (ret<0)
- pkt->crt=bak; /* roll back */
- return ret;
- }
- #define binrpc_addint(pkt, i) binrpc_add_int_type((pkt), (i), BINRPC_T_INT)
- #define binrpc_adddouble(pkt, f) \
- binrpc_add_double_type((pkt), (f), BINRPC_T_DOUBLE)
- #define binrpc_addstr(pkt, s, len) \
- binrpc_add_str_type((pkt), (s), (len), BINRPC_T_STR)
- #define binrpc_addbytes(pkt, s, len) \
- binrpc_add_str_type((pkt), (s), (len), BINRPC_T_BYTES)
- /* struct type format:
- * start : 0000 | BINRPC_T_STRUCT
- * end: 1000 | BINRPC_T_STRUCT
- */
- #define binrpc_start_struct(pkt) binrpc_add_tag((pkt), BINRPC_T_STRUCT, 0)
- #define binrpc_end_struct(pkt) binrpc_add_tag((pkt), BINRPC_T_STRUCT, 1)
- #define binrpc_start_array(pkt) binrpc_add_tag((pkt), BINRPC_T_ARRAY, 0)
- #define binrpc_end_array(pkt) binrpc_add_tag((pkt), BINRPC_T_ARRAY, 1)
- static inline int binrpc_addfault( struct binrpc_pkt* pkt,
- int code,
- char* s, int len)
- {
- int ret;
- unsigned char* bak;
-
- bak=pkt->crt;
- if ((ret=binrpc_addint(pkt, code))<0)
- return ret;
- ret=binrpc_addstr(pkt, s, len);
- if (ret<0)
- pkt->crt=bak; /* roll back */
- return ret;
- }
- /* parsing incoming messages */
- static inline unsigned char* binrpc_read_int( int* i,
- int len,
- unsigned char* s,
- unsigned char* end,
- int *err
- )
- {
- unsigned char* start;
-
- start=s;
- *i=0;
- *err=0;
- for(;len>0; len--, s++){
- if (s>=end){
- *err=E_BINRPC_MORE_DATA;
- return start;
- }
- *i<<=8;
- *i|=*s;
- };
- return s;
- }
- /* initialize parsing context, it tries to read the whole message header,
- * if there is not enough data, sets *err to E_BINRPC_MORE_DATA. In this
- * case just redo the call when more data is available (len is bigger)
- * on success sets *err to 0 and returns the current position in buf
- * (=> you can discard the content between buf & the returned value).
- * On error buf is returned back, and *err set.
- */
- static inline unsigned char* binrpc_parse_init( struct binrpc_parse_ctx* ctx,
- unsigned char* buf,
- int len,
- int *err
- )
- {
- int len_len, c_len;
- unsigned char *p;
- *err=0;
- ctx->tlen=0; /* init to 0 */
- ctx->cookie=0; /* init to 0 */
- if (len<BINRPC_MIN_PKT_SIZE){
- *err=E_BINRPC_MORE_DATA;
- goto error;
- }
- if (buf[0]!=((BINRPC_MAGIC<<4)|BINRPC_VERS)){
- *err=E_BINRPC_BADPKT;
- goto error;
- }
- ctx->type=buf[1]>>4;
- /* type check */
- switch(ctx->type){
- case BINRPC_REQ:
- case BINRPC_REPL:
- case BINRPC_FAULT:
- break;
- default:
- *err=E_BINRPC_BADPKT;
- goto error;
- }
- len_len=((buf[1]>>2) & 3) + 1;
- c_len=(buf[1]&3) + 1;
- if ((BINRPC_TLEN_OFFSET+len_len+c_len)>len){
- *err=E_BINRPC_MORE_DATA;
- goto error;
- }
- p=binrpc_read_int((int*)&ctx->tlen, len_len, &buf[BINRPC_TLEN_OFFSET],
- &buf[len], err);
- /* empty packets (replies) are allowed
- if (ctx->tlen==0){
- *err=E_BINRPC_BADPKT;
- goto error;
- } */
- p=binrpc_read_int((int*)&ctx->cookie, c_len, p, &buf[len], err);
- ctx->offset=0;
- ctx->flags|=BINRPC_F_INIT;
- return p;
- error:
- return buf;
- }
- /* returns bytes needed (till the end of the packet)
- * on error (non. init ctx) returns < 0
- */
- inline static int binrpc_bytes_needed(struct binrpc_parse_ctx *ctx)
- {
- if (ctx->flags & BINRPC_F_INIT)
- return ctx->tlen-ctx->offset;
- return E_BINRPC_NOTINIT;
- }
- /* prefill v with the requested type, if type==BINRPC_T_ALL it
- * will be replaced by the actual record type
- * known problems: no support for arrays inside STRUCT
- * returns position after the record and *err==0 if succesfull
- * original position and *err<0 if not */
- inline static unsigned char* binrpc_read_record(struct binrpc_parse_ctx* ctx,
- unsigned char* buf,
- unsigned char* end,
- struct binrpc_val* v,
- int* err
- )
- {
- int type;
- int len;
- int end_tag;
- int tmp;
- unsigned char* p;
- int i;
-
- p=buf;
- end_tag=0;
- *err=0;
- if (!(ctx->flags & BINRPC_F_INIT)){
- *err=E_BINRPC_NOTINIT;
- goto error;
- }
- if (ctx->offset>=ctx->tlen){
- *err=E_BINRPC_EOP;
- goto error;
- }
- if (p>=end){
- *err=E_BINRPC_MORE_DATA;
- goto error;
- }
- /* read type_len */
- type=*p & 0xf;
- len=*p>>4;
- p++;
- if (len & 8){
- end_tag=1; /* possible end mark for array or structs */
- /* we have to read len bytes and use them as the new len */
- p=binrpc_read_int(&len, len&7, p, end, err);
- if (*err<0)
- goto error;
- }
- if ((p+len)>end){
- *err=E_BINRPC_MORE_DATA;
- goto error;
- }
- if ((v->type!=type) && (v->type !=BINRPC_T_ALL)){
- goto error_type;
- }
- v->type=type;
- if (ctx->in_struct){
- switch(type){
- case BINRPC_T_STRUCT:
- if (end_tag){
- ctx->in_struct--;
- v->u.end=1;
- }else{
- goto error_record;
- }
- break;
- case BINRPC_T_AVP:
- /* name | value */
- v->name.s=(char*)p;
- v->name.len=(len-1); /* don't include 0 term */
- p+=len;
- if (p>=end){
- *err=E_BINRPC_MORE_DATA;
- goto error;
- }
- /* avp value type */
- type=*p & 0xf;
- if ((type!=BINRPC_T_AVP) && (type!=BINRPC_T_ARRAY)){
- tmp=ctx->in_struct;
- ctx->in_struct=0; /* hack to parse a normal record */
- v->type=type; /* hack */
- p=binrpc_read_record(ctx, p, end, v, err);
- if (err<0){
- ctx->in_struct=tmp;
- goto error;
- }else{
- ctx->in_struct+=tmp;
- /* the offset is already updated => skip */
- goto no_offs_update;
- }
- }else{
- goto error_record;
- }
- break;
- default:
- goto error_record;
- }
- }else{
- switch(type){
- case BINRPC_T_INT:
- p=binrpc_read_int(&v->u.intval, len, p, end, err);
- break;
- case BINRPC_T_STR:
- v->u.strval.s=(char*)p;
- v->u.strval.len=(len-1); /* don't include terminating 0 */
- p+=len;
- break;
- case BINRPC_T_BYTES:
- v->u.strval.s=(char*)p;
- v->u.strval.len=len;
- p+=len;
- case BINRPC_T_STRUCT:
- if (end_tag)
- goto error_record;
- v->u.end=0;
- ctx->in_struct++;
- break;
- case BINRPC_T_ARRAY:
- if (end_tag){
- if (ctx->in_array>0){
- ctx->in_array--;
- v->u.end=1;
- }else
- goto error_record;
- }else{
- ctx->in_array++;
- v->u.end=0;
- }
- break;
- case BINRPC_T_DOUBLE: /* FIXME: hack: represented as fixed point
- inside an int */
- p=binrpc_read_int(&i, len, p, end, err);
- v->u.fval=((double)i)/1000;
- break;
- default:
- goto error_type;
- }
- }
- ctx->offset+=(int)(p-buf);
- no_offs_update:
- return p;
- error_type:
- *err=E_BINRPC_TYPE;
- return buf;
- error_record:
- *err=E_BINRPC_RECORD;
- error:
- return buf;
- }
- /* reads/skips an entire struct
- * the struct start/end are saved in v->u.strval.s, v->u.strval.len
- * return: - new buffer position and set *err to 0 if successfull
- * - original buffer and *err<0 on error */
- inline static unsigned char* binrpc_read_struct(struct binrpc_parse_ctx* ctx,
- unsigned char* buf,
- unsigned char* end,
- struct binrpc_val* v,
- int* err
- )
- {
- int type;
- int len;
- int end_tag;
- unsigned char* p;
- int in_struct;
-
- *err=0;
- p=buf;
- end_tag=0;
- if (!(ctx->flags & BINRPC_F_INIT)){
- *err=E_BINRPC_NOTINIT;
- goto error;
- }
- if (ctx->offset>=ctx->tlen){
- *err=E_BINRPC_EOP;
- goto error;
- }
- if (p>=end){
- *err=E_BINRPC_MORE_DATA;
- goto error;
- }
- /* read type_len */
- type=*p & 0xf;
- len=*p>>4;
- p++;
- if (len & 8){
- end_tag=1; /* possible end mark for array or structs */
- /* we have to read len bytes and use them as the new len */
- p=binrpc_read_int(&len, len&7, p, end, err);
- if (*err<0)
- goto error;
- }
- if ((p+len)>=end){
- *err=E_BINRPC_MORE_DATA;
- goto error;
- }
- if (type!=BINRPC_T_STRUCT){
- goto error_type;
- }
- if (end_tag){
- goto error_record;
- }
- p+=len; /* len should be 0 for a struct tag */
- in_struct=1;
- v->type=type;
- v->u.strval.s=(char*)p; /* it will conain the inside of the struc */
- while(in_struct){
- /* read name */
- type=*p & 0xf;
- len=*p>>4;
- p++;
- if (len & 8){
- end_tag=1; /* possible end mark for array or structs */
- /* we have to read len bytes and use them as the new len */
- p=binrpc_read_int(&len, len&7, p, end, err);
- if (*err<0)
- goto error;
- }
- if ((type==BINRPC_T_STRUCT) && end_tag){
- in_struct--;
- if (in_struct<0)
- goto error_record;
- continue;
- }else if (type!=BINRPC_T_AVP){
- goto error_record;
- }
- /* skip over it */
- p+=len;
- if (p>=end){
- *err=E_BINRPC_MORE_DATA;
- goto error;
- }
- /* read value */
- type=*p & 0xf;
- len=*p>>4;
- p++;
- if (len & 8){
- end_tag=1; /* possible end mark for array or structs */
- /* we have to read len bytes and use them as the new len */
- p=binrpc_read_int(&len, len&7, p, end, err);
- if (*err<0)
- goto error;
- }
- if (type==BINRPC_T_STRUCT){
- if (end_tag)
- goto error_record;
- in_struct++;
- };
- p+=len;
- if (p>=end){
- *err=E_BINRPC_MORE_DATA;
- goto error;
- }
- }
- /* don't include the end tag */;
- v->u.strval.len=(int)(p-(unsigned char*)v->u.strval.s)-1;
- return p;
-
- error_type:
- *err=E_BINRPC_RECORD;
- return buf;
- error_record:
- *err=E_BINRPC_TYPE;
- error:
- return buf;
- }
- /* error code to string */
- const char* binrpc_error(int err);
- #endif
|