binrpc.h

00001 /*
00002  * $Id$
00003  *
00004  * Copyright (C) 2006 iptelorg GmbH
00005  *
00006  * This file is part of ser, a free SIP server.
00007  *
00008  * ser is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * For a license to use the ser software under conditions
00014  * other than those described here, or to purchase support for this
00015  * software, please contact iptel.org by e-mail at the following addresses:
00016  *    info@iptel.org
00017  *
00018  * ser is distributed in the hope that it will be useful,
00019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00021  * GNU General Public License for more details.
00022  *
00023  * You should have received a copy of the GNU General Public License 
00024  * along with this program; if not, write to the Free Software 
00025  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00026  */
00027 /* History:
00028  * --------
00029  *  2006-02-08  created by andrei
00030  *  2006-11-03  replies with no payload are allowed (andrei)
00031  */
00032 /* binrpc is  supposed to be a minimalist binary rpc implementation */
00033 
00034 
00035 
00036 /* packet header:
00037  * (big endian where it applies)
00038  *      4b      4b        4b     2b   2b       <var>          <var>
00039  *  | MAGIC  | VERS  || FLAGS  | LL | CL || total_len ... || cookie ... |
00040  *  total_len = payload len (doesn't include the packet header)
00041  *  LL = total length len -1 (number of bytes on which total len is
00042  *        represented)
00043  *  CL = cookie length -1 (number of bytes on which the cookie is represented)
00044  *  E.g.: LL= 0 => total_len is represented on 1 byte (LL+1)
00045  *        CL= 3 => cookie is represneted on 4 bytes (CL+1)
00046  */
00047 /* record format:
00048  *  1b   3b     4b
00049  * |S | size |  type  || <optional value len> ... || <optional value> ... ||
00050  *
00051  * if S==0, size is the size (in bytes) of the value (if size==0 => null value)
00052  * if S==1, optional_value_len is present, and size is it's size
00053  *    (if size==0 => and type==array or struct => marks end, else
00054  *     error, reserved)
00055  *  Examples:
00056  *     int (type=0) 0x1234     -> 0x20 0x12 0x34           (optimal)
00057  *                                0x90 0x02 0x12 0x34      (suboptimal)
00058  *                                0xA0 0x00 0x02 0x12 0x34 (even worse)
00059  *                   0x07      -> 0x10 0x07                (optimal)
00060  *                   0x00      -> 0x00                     (optimal)
00061  *                                0x10 0x00
00062  *
00063  *     str (type=1) - strings are 0 terminated (an extra 0 is added to them
00064  *                    to make the format easier to parse when asciiz strings
00065  *                    are required); the length includes the terminating 0
00066  *                  "abcdef"   -> 0x71 "abcdef" 0x00
00067  *                  "abcdefhij"-> 0x91 0x0A "abcdefhij" 0x00
00068  *                  ""         -> 0x11 0x00     (0 len str)
00069  *     65535 bytes
00070  *     (using str for it)      -> 0xB1 0x01 0x00 0x00 array 0x00
00071  *
00072  *     bytes (type=6) -like str but not 0 terminated
00073  *                  "abcdef"   -> 0x66 "abcdef"
00074  *     65535 bytes   *         -> 0xA6 0xff 0xff bytes
00075  *
00076  *     arrays (array type=4)
00077  *       arrays are implemented as anonymous value lists:
00078  *         array_start value1, value2, ..., array_end
00079  *                               (start) (1st int)  (2nd elem)  (end array)
00080  *      ints   [ 0x01 0x02 ]   -> 0x04   0x10 0x01  0x10 0x02        0x84
00081  *      combo  [ 0x07 "abc"]   -> 0x04   0x10 0x07  0x41 "abc" 0x00  0x84
00082  *
00083  *      structs (struct type=3)
00084  *       structs are implemented as avp list:
00085  *           struct_start, avp1, avp2 .., struct_end
00086  *        an avp is a named value pair:  name, value. 
00087  *          - name behaves like a normal string, but has a diff. type (5)
00088  *          - avps are legal only inside structs.
00089  *        avp example:           name part (str)   val part (int here) 
00090  *         "test", int 0x0b   -> 0x55 "test" 0x00   0x10 0x0b
00091  *
00092  *      struct example:
00093  *                              (start)  (avps)                           (end)
00094  *      struct{                  0x03        (name )        (val)
00095  *           intval: int 0x3  ->       0x75 "intval" 0x00 0x10 0x3
00096  *           s:      str "abc"-        0x25 "s" 0x00      0x41 "abc" 0x00
00097  *      }                                                                  0x83
00098  *
00099  *      Limitations: for now avps cannot have array values =>
00100  *                   structs cannot contain arrays.
00101  */
00102 
00103 
00104 #ifndef _binrpc_h
00105 #define _binrpc_h
00106 
00107 
00108 #include "../../str.h"
00109 #include <string.h>
00110 
00111 #define BINRPC_MAGIC 0xA
00112 #define BINRPC_VERS  1
00113 
00114 /* sizes & offsets */
00115 #define BINRPC_FIXED_HDR_SIZE   2
00116 #define BINRPC_TLEN_OFFSET              BINRPC_FIXED_HDR_SIZE
00117 #define BINRPC_MIN_HDR_SIZE     (BINRPC_FIXED_HDR_SIZE+2)
00118 #define BINRPC_MAX_HDR_SIZE     (BINRPC_FIXED_HDR_SIZE+4+4)
00119 #define BINRPC_MIN_RECORD_SIZE  1
00120 /* min pkt size: min header + min. len (1) + min. cookie (1)*/
00121 #define BINRPC_MIN_PKT_SIZE     BINRPC_MIN_HDR_SIZE
00122 
00123 /* message types */
00124 #define BINRPC_REQ   0
00125 #define BINRPC_REPL  1
00126 #define BINRPC_FAULT 3
00127 
00128 /* values types */
00129 #define BINRPC_T_INT    0
00130 #define BINRPC_T_STR    1 /* 0 term, for easier parsing */
00131 #define BINRPC_T_DOUBLE 2
00132 #define BINRPC_T_STRUCT 3
00133 #define BINRPC_T_ARRAY  4
00134 #define BINRPC_T_AVP    5  /* allowed only in structs */
00135 #define BINRPC_T_BYTES  6 /* like STR, but not 0 term */
00136 
00137 #define BINRPC_T_ALL    0xf /* wildcard type, will match any record type
00138                                                            in the packet (not allowed inside the pkt)*/
00139 
00140 /* errors */
00141 #define E_BINRPC_INVAL          -1  /* invalid function call parameters */
00142 #define E_BINRPC_OVERFLOW       -2  /* buffer overflow */
00143 #define E_BINRPC_BADPKT         -3  /* something went really bad, the packet is
00144                                                                    corrupted*/
00145 #define E_BINRPC_MORE_DATA      -4 /* parsing error: more bytes are needed,
00146                                                                   just repeat the failed op, when you have
00147                                                                   more bytes available */
00148 #define E_BINRPC_EOP            -5      /* end of packet reached */
00149 #define E_BINRPC_NOTINIT        -6  /* parse ctx not initialized */
00150 #define E_BINRPC_TYPE           -7  /* unkown type for record, or requested
00151                                                                    type doesn't match record type */
00152 #define E_BINRPC_RECORD         -8  /* bad record (unexpected, bad struct a.s.o)*/
00153 #define E_BINRPC_BUG            -9  /* internal error, bug */
00154 #define E_BINRPC_LAST           -10 /* used to count the errors, keep always
00155                                                                    last */
00156 
00157 /* flags */
00158 #define BINRPC_F_INIT   1
00159 
00160 struct binrpc_pkt{  /* binrpc body */
00161         unsigned char* body;
00162         unsigned char* end;
00163         unsigned char* crt; /*private */
00164 };
00165 
00166 
00167 struct binrpc_parse_ctx{
00168         /* header */
00169         unsigned int tlen; /* total len */
00170         unsigned int cookie;
00171         int type; /* request, reply, error */
00172         
00173         /* parsing info */
00174         unsigned int flags;   /* parsing flags */
00175         unsigned int offset; /* current offset (inside payload) */
00176         unsigned int in_struct;
00177         unsigned int in_array;
00178 };
00179 
00180 
00181 
00182 struct binrpc_val{
00183         str name; /* used only in structs */
00184         int type;
00185         union{
00186                 str strval;
00187                 double fval;
00188                 int intval;
00189                 int end;
00190         }u;
00191 };
00192 
00193 
00194 
00195 /*  helper functions  */
00196 
00197 /* return int size: minimum number of bytes needed to represent it
00198  * if i=0 returns 0 */
00199 inline static int binrpc_get_int_len(int i)
00200 {
00201         int size;
00202         for (size=4; size && ((i & (0xff<<24))==0); i<<=8, size--);
00203         return size;
00204 }
00205 
00206 
00207 
00208 /* adds a start or end tag (valid only for STRUCT or ARRAY for now */
00209 inline static int binrpc_add_tag(struct binrpc_pkt* pkt, int type, int end)
00210 {
00211         if (pkt->crt>=pkt->end) return E_BINRPC_OVERFLOW;
00212         *pkt->crt=(end<<7)|type;
00213         pkt->crt++;
00214         return 0;
00215 }
00216 
00217 
00218 
00219 /*  writes a minimal int, returns the new offset and sets
00220  * len to the number of bytes written (<=4)
00221  * to check for oveflow use:  returned_value-p != *len
00222  * (Note: if *len==0 using the test above succeeds even if p>=end)
00223  */
00224 inline static unsigned char* binrpc_write_int(  unsigned char* p,
00225                                                                                                 unsigned char* end,
00226                                                                                                 int i, int *len)
00227 {
00228         int size;
00229 
00230         for (size=4; size && ((i & (0xff<<24))==0); i<<=8, size--);
00231         *len=size;
00232         for(; (p<end) && (size); p++, size--){
00233                 *p=(unsigned char)(i>>24);
00234                 i<<=8;
00235         }
00236         return p;
00237 }
00238 
00239 
00240 
00241 /* API functions */
00242 
00243 /* initialize a binrpc_pkt structure, for packet creation
00244  * params: pkt         - binrpc body structure that will be initialized 
00245  *         buf, b_len  -  destination buffer/len
00246  * returns -1 on error, 0 on success
00247  *
00248  * Example usage:
00249  *  binrpc_init_pkt(pkt, body, BODY_SIZE);
00250  *  binrpc_addint(pkt, 1);
00251  *  binrpc_addstr(pkt, "test", sizeof("test")-1);
00252  *  ...
00253  *  bytes=binrpc_build_hdr(pkt, BINRPC_REQ, 0x123, hdr_buf, HDR_BUF_LEN);
00254  *  writev(sock, {{ hdr, bytes}, {pkt->body, pkt->crt-pkt->body}} , 2)*/
00255 inline static int binrpc_init_pkt(struct binrpc_pkt *pkt,
00256                                                                   unsigned char* buf, int b_len)
00257 {
00258         if (b_len<BINRPC_MIN_RECORD_SIZE)
00259                 return E_BINRPC_OVERFLOW;
00260         pkt->body=buf;
00261         pkt->end=buf+b_len;
00262         pkt->crt=pkt->body;
00263         return 0;
00264 };
00265 
00266 
00267 
00268 /* used to update internal contents if the original buffer 
00269  * (from binrpc_init_pkt) was realloc'ed (and has grown) */
00270 inline static int binrpc_pkt_update_buf(struct binrpc_pkt *pkt,
00271                                                                                 unsigned char* new_buf,
00272                                                                                 int new_len)
00273 {
00274         if ((int)(pkt->crt-pkt->body)>new_len){
00275                 return E_BINRPC_OVERFLOW;
00276         }
00277         pkt->crt=new_buf+(pkt->crt-pkt->body);
00278         pkt->body=new_buf;
00279         pkt->end=new_buf+new_len;
00280         return 0;
00281 }
00282 
00283 
00284 
00285 /* builds a binrpc header for the binrpc pkt. body pkt and writes it in buf
00286  * params:  
00287  *          type     - binrpc packet type (request, reply, fault)
00288  *          body_len - body len
00289  *          cookie   - binrpc cookie value
00290  *          buf,len  - destination buffer & len
00291  * returns -1 on error, number of bytes written on success */
00292 inline static int binrpc_build_hdr(     int type, int body_len,
00293                                                                         unsigned int cookie,
00294                                                                         unsigned char* buf, int b_len) 
00295 {
00296         unsigned char* p;
00297         int len_len;
00298         int c_len;
00299         
00300         len_len=binrpc_get_int_len(body_len);
00301         c_len=binrpc_get_int_len(cookie);
00302         if (len_len==0) len_len=1; /* we can't have 0 len */
00303         if (c_len==0) c_len=1;  /* we can't have 0 len */
00304         /* size check: 2 bytes header + len_len + cookie len*/
00305         if (b_len<(BINRPC_FIXED_HDR_SIZE+len_len+c_len)){
00306                 goto error_len;
00307         }
00308         p=buf;
00309         *p=(BINRPC_MAGIC << 4) | BINRPC_VERS;
00310         p++;
00311         *p=(type<<4)|((len_len-1)<<2)|(c_len-1);
00312         p++;
00313         for(;len_len>0; len_len--,p++){
00314                 *p=(unsigned char)(body_len>>((len_len-1)*8));
00315         }
00316         for(;c_len>0; c_len--,p++){
00317                 *p=(unsigned char)(cookie>>((c_len-1)*8));
00318         }
00319         return (int)(p-buf);
00320 error_len:
00321         return E_BINRPC_OVERFLOW;
00322 }
00323 
00324 
00325 
00326 #define binrpc_pkt_len(pkt)             ((int)((pkt)->crt-(pkt)->body))
00327 
00328 
00329 
00330 /* changes the length of a header (enough space must be availale) */
00331 inline static int binrpc_hdr_change_len(unsigned char* hdr, int hdr_len,
00332                                                                                 int new_len)
00333 {
00334         int len_len;
00335         
00336         binrpc_write_int(&hdr[BINRPC_TLEN_OFFSET], hdr+hdr_len, new_len, &len_len);
00337         return 0;
00338 }
00339 
00340 
00341 
00342 /* int format:     size BINRPC_T_INT <val>  */
00343 inline static int binrpc_add_int_type(struct binrpc_pkt* pkt, int i, int type)
00344 {
00345         
00346         unsigned char* p;
00347         int size;
00348         
00349         p=binrpc_write_int(pkt->crt+1, pkt->end, i, &size);
00350         if ((pkt->crt>=pkt->end) || ((int)(p-pkt->crt-1)!=size))
00351                 goto error_len;
00352         *(pkt->crt)=(size<<4) | type;
00353         pkt->crt=p;
00354         return 0;
00355 error_len:
00356         return E_BINRPC_OVERFLOW;
00357 }
00358 
00359 
00360 
00361 /* double format:  FIXME: for now a hack: fixed point represented in
00362  *  an int (=> max 3 decimals, < MAX_INT/1000) */
00363 #define binrpc_add_double_type(pkt, f, type)\
00364         binrpc_add_int_type((pkt), (int)((f)*1000), (type))
00365 
00366 
00367 
00368 /* skip bytes bytes (leaves an empty space, for possible future use)
00369  * WARNING: use with care, low level function
00370  */
00371 inline static int binrpc_add_skip(struct binrpc_pkt* pkt, int bytes)
00372 {
00373         
00374         if ((pkt->crt+bytes)>=pkt->end)
00375                 return E_BINRPC_OVERFLOW;
00376         pkt->crt+=bytes;
00377         return 0;
00378 }
00379 
00380 
00381 
00382 /*
00383  * adds only the string mark and len, you'll have to memcpy the contents
00384  * manually later (and also use binrpc_add_skip(pkt, l) or increase
00385  *  pkt->crt directly if you want to continue adding to this pkt).
00386  *  Usefull for optimizing str writing (e.g. writev(iovec))
00387  *  WARNING: use with care, low level function, binrpc_addstr or 
00388  *           binrpc_add_str_type are probably what you want.
00389  *  WARNING1: BINRPC_T_STR and BINRPC_T_AVP must be  0 term, the len passed to
00390  *            this function, must include the \0 in this case.
00391  */
00392 inline static int binrpc_add_str_mark(struct binrpc_pkt* pkt, int type,
00393                                                                                 int l)
00394 {
00395         int size;
00396         unsigned char* p;
00397         
00398         if (pkt->crt>=pkt->end) goto error_len;
00399         if (l<8){
00400                 size=l;
00401                 p=pkt->crt+1;
00402         }else{ /* we need a separate len */
00403                 p=binrpc_write_int(pkt->crt+1, pkt->end, l, &size);
00404                 if (((int)(p-pkt->crt-1)!=size))
00405                         goto error_len;
00406                 size|=8; /* mark it as having external len  */
00407         }
00408         *(pkt->crt)=(size)<<4|type;
00409         pkt->crt=p;
00410         return 0;
00411 error_len:
00412         return E_BINRPC_OVERFLOW;
00413 }
00414 
00415 
00416 
00417 inline static int binrpc_add_str_type(struct binrpc_pkt* pkt, char* s, int len,
00418                                                                                 int type)
00419 {
00420         int size;
00421         int l;
00422         int zero_term; /* whether or not to add an extra 0 at the end */
00423         unsigned char* p;
00424         
00425         zero_term=((type==BINRPC_T_STR)||(type==BINRPC_T_AVP));
00426         l=len+zero_term;
00427         if (l<8){
00428                 size=l;
00429                 p=pkt->crt+1;
00430         }else{ /* we need a separate len */
00431                 p=binrpc_write_int(pkt->crt+1, pkt->end, l, &size);
00432                 /* if ((int)(p-pkt->crt)<(size+1)) goto error_len;  - not needed,
00433                  *  caught by the next check */
00434                 size|=8; /* mark it as having external len  */
00435         }
00436         if ((p+l)>pkt->end) goto error_len;
00437         *(pkt->crt)=(size)<<4|type;
00438         memcpy(p, s, len);
00439         if (zero_term) p[len]=0;
00440         pkt->crt=p+l;
00441         return 0;
00442 error_len:
00443         return E_BINRPC_OVERFLOW;
00444 }
00445 
00446 
00447 
00448 /* adds an avp (name, value) pair, usefull to add structure members */
00449 inline static int binrpc_addavp(struct binrpc_pkt* pkt, struct binrpc_val* avp)
00450 {
00451         int ret;
00452         unsigned char* bak;
00453         
00454         bak=pkt->crt;
00455         ret=binrpc_add_str_type(pkt, avp->name.s, avp->name.len, BINRPC_T_AVP);
00456         if (ret<0) return ret;
00457         switch (avp->type){
00458                 case BINRPC_T_INT:
00459                         ret=binrpc_add_int_type(pkt, avp->u.intval, avp->type);
00460                         break;
00461                 case BINRPC_T_STR:
00462                 case BINRPC_T_BYTES:
00463                         ret=binrpc_add_str_type(pkt, avp->u.strval.s, 
00464                                                                                 avp->u.strval.len,
00465                                                                                 avp->type);
00466                         break;
00467                 case BINRPC_T_STRUCT:
00468                 case BINRPC_T_ARRAY:
00469                         ret=binrpc_add_tag(pkt, avp->type, 0);
00470                         break;
00471                 case BINRPC_T_DOUBLE: 
00472                         ret=binrpc_add_double_type(pkt, avp->u.fval, avp->type);
00473                         break;
00474                 default:
00475                         ret=E_BINRPC_BUG;
00476         }
00477         if (ret<0)
00478                 pkt->crt=bak; /* roll back */
00479         return ret;
00480 }
00481 
00482 
00483 
00484 #define binrpc_addint(pkt, i)   binrpc_add_int_type((pkt), (i), BINRPC_T_INT) 
00485 
00486 #define binrpc_adddouble(pkt, f)        \
00487         binrpc_add_double_type((pkt), (f), BINRPC_T_DOUBLE)
00488 
00489 #define binrpc_addstr(pkt, s, len)      \
00490         binrpc_add_str_type((pkt), (s), (len), BINRPC_T_STR) 
00491 
00492 #define binrpc_addbytes(pkt, s, len)    \
00493         binrpc_add_str_type((pkt), (s), (len), BINRPC_T_BYTES) 
00494 
00495 /* struct type format:
00496  *  start :         0000 | BINRPC_T_STRUCT 
00497  *  end:            1000 | BINRPC_T_STRUCT
00498  */
00499 #define  binrpc_start_struct(pkt) binrpc_add_tag((pkt), BINRPC_T_STRUCT, 0)
00500 
00501 #define  binrpc_end_struct(pkt) binrpc_add_tag((pkt), BINRPC_T_STRUCT, 1)
00502 
00503 #define  binrpc_start_array(pkt) binrpc_add_tag((pkt), BINRPC_T_ARRAY, 0)
00504 
00505 #define  binrpc_end_array(pkt) binrpc_add_tag((pkt), BINRPC_T_ARRAY, 1)
00506 
00507 
00508 static inline int binrpc_addfault(      struct binrpc_pkt* pkt,
00509                                                                         int code,
00510                                                                         char* s, int len)
00511 {
00512         int ret;
00513         unsigned char* bak;
00514         
00515         bak=pkt->crt;
00516         if ((ret=binrpc_addint(pkt, code))<0)
00517                 return ret;
00518         ret=binrpc_addstr(pkt, s, len);
00519         if (ret<0)
00520                 pkt->crt=bak; /* roll back */
00521         return ret;
00522 }
00523 
00524 /* parsing incoming messages */
00525 
00526 
00527 static inline unsigned char* binrpc_read_int(   int* i,
00528                                                                                                 int len,
00529                                                                                                 unsigned char* s, 
00530                                                                                                 unsigned char* end,
00531                                                                                                 int *err
00532                                                                                                 )
00533 {
00534         unsigned char* start;
00535         
00536         start=s;
00537         *i=0;
00538         *err=0;
00539         for(;len>0; len--, s++){
00540                 if (s>=end){
00541                         *err=E_BINRPC_MORE_DATA;
00542                         return start;
00543                 }
00544                 *i<<=8;
00545                 *i|=*s;
00546         };
00547         return s;
00548 }
00549 
00550 
00551 
00552 /* initialize parsing context, it tries to read the whole message header,
00553  * if there is not enough data, sets *err to E_BINRPC_MORE_DATA. In this
00554  *  case just redo the call when more data is available (len is bigger)
00555  * on success sets *err to 0 and returns the current position in  buf
00556  * (=> you can discard the content between buf & the returned value).
00557  * On error buf is returned back, and *err set.
00558  */
00559 static inline unsigned char* binrpc_parse_init( struct binrpc_parse_ctx* ctx,
00560                                                                                                 unsigned char* buf,
00561                                                                                                 int len,
00562                                                                                                 int *err
00563                                                                                                 )
00564 {
00565         int len_len, c_len;
00566         unsigned char *p;
00567 
00568         *err=0;
00569         ctx->tlen=0;    /* init to 0 */
00570         ctx->cookie=0;  /* init to 0 */
00571         if (len<BINRPC_MIN_PKT_SIZE){
00572                 *err=E_BINRPC_MORE_DATA;
00573                 goto error;
00574         }
00575         if (buf[0]!=((BINRPC_MAGIC<<4)|BINRPC_VERS)){
00576                 *err=E_BINRPC_BADPKT;
00577                 goto error;
00578         }
00579         ctx->type=buf[1]>>4;
00580         /* type check */
00581         switch(ctx->type){
00582                 case BINRPC_REQ:
00583                 case BINRPC_REPL:
00584                 case BINRPC_FAULT:
00585                         break;
00586                 default:
00587                         *err=E_BINRPC_BADPKT;
00588                         goto error;
00589         }
00590         len_len=((buf[1]>>2) & 3) + 1;
00591         c_len=(buf[1]&3) + 1;
00592         if ((BINRPC_TLEN_OFFSET+len_len+c_len)>len){
00593                 *err=E_BINRPC_MORE_DATA;
00594                 goto error;
00595         }
00596         p=binrpc_read_int((int*)&ctx->tlen, len_len, &buf[BINRPC_TLEN_OFFSET],
00597                                                 &buf[len], err);
00598         /* empty packets (replies) are allowed
00599            if (ctx->tlen==0){
00600                 *err=E_BINRPC_BADPKT;
00601                 goto error;
00602         } */
00603         p=binrpc_read_int((int*)&ctx->cookie, c_len, p, &buf[len], err);
00604         ctx->offset=0;
00605         ctx->flags|=BINRPC_F_INIT;
00606         return p;
00607 error:
00608         return buf;
00609 }
00610 
00611 
00612 
00613 /* returns bytes needed (till the end of the packet)
00614  * on error (non. init ctx) returns < 0 
00615  */
00616 inline static int binrpc_bytes_needed(struct binrpc_parse_ctx *ctx)
00617 {
00618         if (ctx->flags & BINRPC_F_INIT)
00619                 return ctx->tlen-ctx->offset;
00620         return E_BINRPC_NOTINIT;
00621 }
00622 
00623 
00624 
00625 /* prefill v with the requested type, if type==BINRPC_T_ALL it 
00626  * will be replaced by the actual record type 
00627  * known problems: no support for arrays inside STRUCT
00628  * returns position after the record and *err==0 if succesfull
00629  *         original position and *err<0 if not */
00630 inline static unsigned char* binrpc_read_record(struct binrpc_parse_ctx* ctx,
00631                                                                                                 unsigned char* buf,
00632                                                                                                 unsigned char* end,
00633                                                                                                 struct binrpc_val* v,
00634                                                                                                 int* err
00635                                                                                                 )
00636 {
00637         int type;
00638         int len;
00639         int end_tag;
00640         int tmp;
00641         unsigned char* p;
00642         int i;
00643         
00644         p=buf;
00645         end_tag=0;
00646         *err=0;
00647         if (!(ctx->flags & BINRPC_F_INIT)){
00648                 *err=E_BINRPC_NOTINIT;
00649                 goto error;
00650         }
00651         if (ctx->offset>=ctx->tlen){
00652                 *err=E_BINRPC_EOP;
00653                 goto error;
00654         }
00655         if (p>=end){
00656                 *err=E_BINRPC_MORE_DATA;
00657                 goto error;
00658         }
00659         /* read type_len */
00660         type=*p & 0xf;
00661         len=*p>>4;
00662         p++;
00663         if (len & 8){
00664                 end_tag=1; /* possible end mark for array or structs */
00665                 /* we have to read len bytes and use them as the new len */
00666                 p=binrpc_read_int(&len, len&7, p, end, err);
00667                 if (*err<0)
00668                         goto error;
00669         }
00670         if ((p+len)>end){
00671                 *err=E_BINRPC_MORE_DATA;
00672                 goto error;
00673         }
00674         if ((v->type!=type) && (v->type !=BINRPC_T_ALL)){
00675                 goto error_type;
00676         }
00677         v->type=type;
00678         if (ctx->in_struct){
00679                 switch(type){
00680                         case BINRPC_T_STRUCT:
00681                                 if (end_tag){
00682                                         ctx->in_struct--;
00683                                         v->u.end=1;
00684                                 }else{
00685                                         goto error_record;
00686                                 }
00687                                 break;
00688                         case BINRPC_T_AVP:
00689                                 /* name | value */
00690                                 v->name.s=(char*)p;
00691                                 v->name.len=(len-1); /* don't include 0 term */
00692                                 p+=len;
00693                                 if (p>=end){
00694                                         *err=E_BINRPC_MORE_DATA;
00695                                         goto error;
00696                                 }
00697                                 /* avp value type */
00698                                 type=*p & 0xf;
00699                                 if ((type!=BINRPC_T_AVP) && (type!=BINRPC_T_ARRAY)){
00700                                         tmp=ctx->in_struct;
00701                                         ctx->in_struct=0; /* hack to parse a normal record */
00702                                         v->type=type; /* hack */
00703                                         p=binrpc_read_record(ctx, p, end, v, err);
00704                                         if (err<0){
00705                                                 ctx->in_struct=tmp;
00706                                                 goto error;
00707                                         }else{
00708                                                 ctx->in_struct+=tmp;
00709                                                 /* the offset is already updated => skip */
00710                                                 goto no_offs_update;
00711                                         }
00712                                 }else{
00713                                         goto  error_record;
00714                                 }
00715                                 break;
00716                         default:
00717                                 goto error_record;
00718                 }
00719         }else{
00720                 switch(type){
00721                         case BINRPC_T_INT:
00722                                 p=binrpc_read_int(&v->u.intval, len, p, end, err);
00723                                 break;
00724                         case BINRPC_T_STR:
00725                                 v->u.strval.s=(char*)p;
00726                                 v->u.strval.len=(len-1); /* don't include terminating 0 */
00727                                 p+=len;
00728                                 break;
00729                         case BINRPC_T_BYTES:
00730                                 v->u.strval.s=(char*)p;
00731                                 v->u.strval.len=len;
00732                                 p+=len;
00733                         case BINRPC_T_STRUCT:
00734                                 if (end_tag)
00735                                         goto error_record;
00736                                 v->u.end=0;
00737                                 ctx->in_struct++;
00738                                 break;
00739                         case BINRPC_T_ARRAY:
00740                                 if (end_tag){
00741                                         if (ctx->in_array>0){
00742                                                 ctx->in_array--;
00743                                                 v->u.end=1;
00744                                         }else
00745                                                 goto error_record;
00746                                 }else{
00747                                         ctx->in_array++;
00748                                         v->u.end=0;
00749                                 }
00750                                 break;
00751                         case BINRPC_T_DOUBLE: /* FIXME: hack: represented as fixed point
00752                                                               inside an int */
00753                                 p=binrpc_read_int(&i, len, p, end, err);
00754                                 v->u.fval=((double)i)/1000;
00755                                 break;
00756                         default:
00757                                 goto error_type;
00758                 }
00759         }
00760         ctx->offset+=(int)(p-buf);
00761 no_offs_update:
00762         return p;
00763 error_type:
00764         *err=E_BINRPC_TYPE;
00765         return buf;
00766 error_record:
00767         *err=E_BINRPC_RECORD;
00768 error:
00769         return buf;
00770 }
00771 
00772 
00773 
00774 /* reads/skips an entire struct
00775  * the struct start/end are saved in v->u.strval.s, v->u.strval.len 
00776  * return:  - new buffer position  and set *err to 0 if successfull
00777  *          - original buffer and *err<0 on error */
00778 inline static unsigned char* binrpc_read_struct(struct binrpc_parse_ctx* ctx,
00779                                                                                                 unsigned char* buf,
00780                                                                                                 unsigned char* end,
00781                                                                                                 struct binrpc_val* v,
00782                                                                                                 int* err
00783                                                                                                 )
00784 {
00785 
00786         int type;
00787         int len;
00788         int end_tag;
00789         unsigned char* p;
00790         int in_struct;
00791         
00792         *err=0;
00793         p=buf;
00794         end_tag=0;
00795         if (!(ctx->flags & BINRPC_F_INIT)){
00796                 *err=E_BINRPC_NOTINIT;
00797                 goto error;
00798         }
00799         if (ctx->offset>=ctx->tlen){
00800                 *err=E_BINRPC_EOP;
00801                 goto error;
00802         }
00803         if (p>=end){
00804                 *err=E_BINRPC_MORE_DATA;
00805                 goto error;
00806         }
00807         /* read type_len */
00808         type=*p & 0xf;
00809         len=*p>>4;
00810         p++;
00811         if (len & 8){
00812                 end_tag=1; /* possible end mark for array or structs */
00813                 /* we have to read len bytes and use them as the new len */
00814                 p=binrpc_read_int(&len, len&7, p, end, err);
00815                 if (*err<0)
00816                         goto error;
00817         }
00818         if ((p+len)>=end){
00819                 *err=E_BINRPC_MORE_DATA;
00820                 goto error;
00821         }
00822         if (type!=BINRPC_T_STRUCT){
00823                 goto error_type;
00824         }
00825         if (end_tag){
00826                 goto error_record;
00827         }
00828         p+=len; /* len should be 0 for a struct tag */
00829         in_struct=1;
00830         v->type=type;
00831         v->u.strval.s=(char*)p; /* it will conain the inside of the struc */
00832         while(in_struct){
00833                 /* read name */
00834                 type=*p & 0xf;
00835                 len=*p>>4;
00836                 p++;
00837                 if (len & 8){
00838                         end_tag=1; /* possible end mark for array or structs */
00839                         /* we have to read len bytes and use them as the new len */
00840                         p=binrpc_read_int(&len, len&7, p, end, err);
00841                         if (*err<0)
00842                                 goto error;
00843                 }
00844                 if ((type==BINRPC_T_STRUCT) && end_tag){
00845                         in_struct--;
00846                         if (in_struct<0)
00847                                 goto error_record;
00848                         continue;
00849                 }else if (type!=BINRPC_T_AVP){
00850                         goto error_record;
00851                 }
00852                 /* skip over it */
00853                 p+=len;
00854                 if (p>=end){
00855                         *err=E_BINRPC_MORE_DATA;
00856                         goto error;
00857                 }
00858                 /* read value */
00859                 type=*p & 0xf;
00860                 len=*p>>4;
00861                 p++;
00862                 if (len & 8){
00863                         end_tag=1; /* possible end mark for array or structs */
00864                         /* we have to read len bytes and use them as the new len */
00865                         p=binrpc_read_int(&len, len&7, p, end, err);
00866                         if (*err<0)
00867                                 goto error;
00868                 }
00869                 if (type==BINRPC_T_STRUCT){
00870                         if (end_tag)
00871                                 goto error_record;
00872                         in_struct++;
00873                 };
00874                 p+=len;
00875                 if (p>=end){
00876                         *err=E_BINRPC_MORE_DATA;
00877                         goto error;
00878                 }
00879         }
00880         /* don't include the end tag */;
00881         v->u.strval.len=(int)(p-(unsigned char*)v->u.strval.s)-1;
00882         return p;
00883         
00884 error_type:
00885         *err=E_BINRPC_RECORD;
00886         return buf;
00887 error_record:
00888         *err=E_BINRPC_TYPE;
00889 error:
00890         return buf;
00891 }
00892 
00893 
00894 
00895 /* error code to string */
00896 const char* binrpc_error(int err);
00897 #endif