You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/ctl/binrpc.h

903 lines
23 KiB

/*
* Copyright (C) 2006 iptelorg GmbH
*
* This file is part of Kamailio, a free SIP server.
*
* Kamailio 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
*
* Kamailio 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
*/
/* 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
* param smode: allow simple vals inside struct (needed for
* not-strict-formatted rpc responses)
* 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 smode,
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;
switch(type){
case BINRPC_T_STRUCT:
if (ctx->in_struct){
if (end_tag){
ctx->in_struct--;
v->u.end=1;
}else{
if(smode==0) {
goto error_record;
} else {
v->u.end=0;
ctx->in_struct++;
}
}
} else {
if (end_tag)
goto error_record;
v->u.end=0;
ctx->in_struct++;
}
break;
case BINRPC_T_AVP:
/* name | value */
if (ctx->in_struct){
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, smode, 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;
}
} else {
goto error_type;
}
break;
case BINRPC_T_INT:
if (ctx->in_struct && smode==0) goto error_record;
p=binrpc_read_int(&v->u.intval, len, p, end, err);
break;
case BINRPC_T_STR:
if (ctx->in_struct && smode==0) goto error_record;
v->u.strval.s=(char*)p;
v->u.strval.len=(len-1); /* don't include terminating 0 */
p+=len;
break;
case BINRPC_T_BYTES:
if (ctx->in_struct && smode==0) goto error_record;
v->u.strval.s=(char*)p;
v->u.strval.len=len;
p+=len;
case BINRPC_T_ARRAY:
if (ctx->in_struct && smode==0) goto error_record;
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 */
if (ctx->in_struct && smode==0) goto error_record;
p=binrpc_read_int(&i, len, p, end, err);
v->u.fval=((double)i)/1000;
break;
default:
if (ctx->in_struct){
goto error_record;
} else {
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