浏览代码

binrpc library - offers API for using Andrei's binary RPC functionality

Vladimir Marek 18 年之前
父节点
当前提交
781c62214a
共有 5 个文件被更改,包括 1583 次插入0 次删除
  1. 12 0
      lib/binrpc/Makefile
  2. 8 0
      lib/binrpc/Makefile.ser
  3. 34 0
      lib/binrpc/README
  4. 1238 0
      lib/binrpc/binrpc_api.c
  5. 291 0
      lib/binrpc/binrpc_api.h

+ 12 - 0
lib/binrpc/Makefile

@@ -0,0 +1,12 @@
+# warning: set correct structure aligment!
+CFLAGS   += -ggdb -fPIC -malign-double
+
+# name of result executable or library
+NAME = binrpc
+
+# override using 'make lib-type=static'
+# TYPE=lib => shared or static library, executable otherwise
+TYPE = lib
+
+include ../Makefile.defs
+

+ 8 - 0
lib/binrpc/Makefile.ser

@@ -0,0 +1,8 @@
+LIBNAME  = binrpc
+OUT_NAME = libbinrpc.so
+OUT_TYPE = lib
+
+CFLAGS += -g0 -O9 -fPIC
+
+include ../Makefile.ser.defs
+

+ 34 - 0
lib/binrpc/README

@@ -0,0 +1,34 @@
+binrcp library
+--------------
+
+prepared by Vladimir Marek
+inspirated by sercmd from Andrei Pelinescu-Onciul
+
+Description:
+------------
+
+This library using binrpc implementation from CTL module of SER and encapsulets
+it for socket communication. It offers simple functional interface for sending
+messages over UNIX sockets, TCP or UDP protocol. The messages are encoded into
+binary RCP format that is much more efficient then standard XMLRPC.
+
+
+Purpose:
+--------
+
+This library offers common interface for communicating with CTL module over UNIX
+socket, TCP and UDP protocol. Binary RCP makes XMLRPC more efficient using 
+binary format for transporting messgae from client to server and vice versa.
+
+
+Description of interface:
+-------------------------
+
+The detail description of interface is a part of binrpc_api.h header file. It is
+also possible generate documentation from this file by using DoxyGen.
+
+Warning
+-------
+
+Double check alignment which is used for compilation to avoid nasty problem with
+structure size (if using libbinrpc library). This may occur e.g. in binrpc_parse_response!!!

+ 1238 - 0
lib/binrpc/binrpc_api.c

@@ -0,0 +1,1238 @@
+/*
+ * $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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+ 
+/*
+ * send commands using binrpc
+ *
+ * History:
+ * --------
+ *  2006-11-09  created by vlada
+ *  2006-12-20  extended by tma
+ */
+
+#include <stdlib.h> /* realloc, rand ... */
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h> /* isprint */
+#include <time.h> /* time */
+#include <stropts.h>
+
+#include "../../modules/ctl/ctl_defaults.h" /* default socket & port */
+#include "../../modules/ctl/init_socks.h"
+#include "../../modules/ctl/binrpc.c" /* ugly hack */
+
+#include "binrpc_api.h"
+
+
+#define IOVEC_CNT 20
+#define TEXT_BUFF_ALLOC_CHUNK   4096
+#define FATAL_ERROR       -1
+
+#ifndef NAME
+#define NAME    "binrpc_api"
+#endif
+
+#define binrpc_malloc internal_malloc
+#define binrpc_realloc internal_realloc
+#define binrpc_free internal_free
+
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX 108
+#endif
+
+#define INT2STR_MAX_LEN  (19+1+1) /* 2^64~= 16*10^18 => 19+1 digits + \0 */
+
+static void* (*internal_malloc)(size_t size) = malloc;
+static void* (*internal_realloc)(void* ptr, size_t size) = realloc;
+static void (*internal_free)(void* ptr) = free;
+
+static char binrpc_last_errs[1024] = "";
+static int verbose = 0;
+
+char *binrpc_get_last_errs() 
+{
+    return binrpc_last_errs;
+}
+
+void binrpc_clear_last_err()
+{
+    binrpc_last_errs[0] = '\0';
+}
+
+void binrpc_set_mallocs(void* _malloc, void* _realloc, void* _free)
+{
+    internal_malloc = _malloc;
+    internal_realloc = _realloc;
+    internal_free = _free;
+}
+
+static int gen_cookie()
+{
+	return rand();
+}
+
+static void hexdump(unsigned char* buf, int len, int ascii)
+{
+	int r, i;
+	
+	/* dump it in hex */
+	for (r=0; r<len; r++){
+		if ((r) && ((r%16)==0)){
+			if (ascii){
+				putchar(' ');
+				for (i=r-16; i<r; i++){
+					if (isprint(buf[i]))
+						putchar(buf[i]);
+					else
+						putchar('.');
+				}
+			}
+			putchar('\n');
+		}
+		printf("%02x ", buf[r]);
+	};
+	if (ascii){
+		for (i=r;i%16; i++)
+			printf("   ");
+		putchar(' ');
+		for (i=16*(r/16); i<r; i++){
+			if (isprint(buf[i]))
+				putchar(buf[i]);
+			else
+				putchar('.');
+		}
+	}
+	putchar('\n');
+}
+
+/* opens,  and  connects on a STREAM unix socket
+ * returns socket fd or -1 on error */
+static int connect_unix_sock(char* name, int type, struct sockaddr_un* mysun,
+                      char* reply_socket, char* sock_dir)
+{
+	struct sockaddr_un ifsun;
+	int s;
+	int len;
+	int ret;
+	int retries;	
+	
+	retries=0;
+	s=-1;
+	memset(&ifsun, 0, sizeof (struct sockaddr_un));
+	len=strlen(name);
+	if (len>UNIX_PATH_MAX){
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1, 
+				"connect_unix_sock: name too long "
+				"(%d > %d): %s", len, UNIX_PATH_MAX, name);
+		goto error;
+	}
+	ifsun.sun_family=AF_UNIX;
+	memcpy(ifsun.sun_path, name, len);
+#ifdef HAVE_SOCKADDR_SA_LEN
+	ifsun.sun_len=len;
+#endif
+	s=socket(PF_UNIX, type, 0);
+	if (s==-1){
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1, 
+			"connect_unix_sock: cannot create unix socket"
+			" %s: %s [%d]", 
+			name, strerror(errno), errno);
+		goto error;
+	}
+	if (type==SOCK_DGRAM){
+		/* we must bind so that we can receive replies */
+		if (reply_socket==0){
+			if (sock_dir==0)
+				sock_dir="/tmp";
+retry:
+			ret=snprintf(mysun->sun_path, UNIX_PATH_MAX, "%s/" NAME "_%d",
+							sock_dir, rand()); 
+			if ((ret<0) ||(ret>=UNIX_PATH_MAX)){
+				snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1, 
+							"connect_unix_sock: buffer overflow while trying to"
+							"generate unix datagram socket name");
+				goto error;
+			}
+		}else{
+			if (strlen(reply_socket)>UNIX_PATH_MAX){
+				snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1, 
+							"connect_unix_sock: buffer overflow while trying to"
+							"use the provided unix datagram socket name (%s)",
+							reply_socket);
+				goto error;
+			}
+			strcpy(mysun->sun_path, reply_socket);
+		}
+		mysun->sun_family=AF_UNIX;
+		if (bind(s, (struct sockaddr*)&mysun, sizeof(struct sockaddr_un))==-1){
+		//if (bind(s, mysun, sizeof(mysun))==-1){
+			if (errno==EADDRINUSE && (reply_socket==0) && (retries < 10)){
+				retries++;
+				/* try another one */
+				goto retry;
+			}
+			snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1, 
+					"connect_unix_sock: could not bind the unix socket to"
+					" %s: %s (%d)",
+					mysun->sun_path, strerror(errno), errno);
+			goto error;
+		}
+	}
+	if (connect(s, (struct sockaddr *)&ifsun, sizeof(ifsun))==-1){
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1, 
+				"connect_unix_sock: connect(%s): %s [%d]",
+				name, strerror(errno), errno);
+		goto error;
+	}
+	return s;
+error:
+	if (s!=-1) close(s);
+	return FATAL_ERROR;
+}
+
+static int connect_tcpudp_socket(char* address, int port, int type)
+{
+	struct sockaddr_in addr;
+	struct hostent* he;
+	int sock;
+	
+	sock=-1;
+	/* resolve destination */
+	he=gethostbyname(address);
+	if (he==0){
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1, 
+				"connect_tcpudp_socket: could not resolve %s", address);
+		goto error;
+	}
+	/* open socket*/
+	addr.sin_family=he->h_addrtype;
+	addr.sin_port=htons(port);
+	memcpy(&addr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
+	
+	sock = socket(he->h_addrtype, type, 0);
+	if (sock==-1){
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1, 
+			"connect_tcpudp_socket: socket: %s", strerror(errno));
+		goto error;
+	}
+	if (connect(sock, (struct sockaddr*) &addr, sizeof(struct sockaddr))!=0){
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1, 
+				"connect_tcpudp_socket: connect: %s", strerror(errno));
+		goto error;
+	}
+	return sock;
+error:
+	if (sock!=-1) close(sock);
+	return FATAL_ERROR;
+}
+
+/* on exit cleanup */
+static void cleanup(struct sockaddr_un* mysun)
+{	
+	if (mysun->sun_path[0] != '\0') {
+		if (unlink(mysun->sun_path) < 0) {
+			fprintf(stderr, "ERROR: failed to delete %s: %s\n",
+					mysun->sun_path, strerror(errno));
+		}
+	}
+}
+
+int binrpc_open_connection(struct binrpc_handle* handle, char* name, int port, int proto,
+		    char* reply_socket, char* sock_dir)
+{
+	struct sockaddr_un mysun;
+		
+	binrpc_last_errs[0] = '\0';
+	binrpc_last_errs[sizeof(binrpc_last_errs)-1] = '\0';  /* snprintf safe terminator */
+
+	handle->socket = -1;
+	handle->buf = NULL;
+	mysun.sun_path[0] = '\0';
+	
+	/* init the random number generator */
+	srand(getpid()+time(0)); /* we don't need very strong random numbers */
+	
+	if (name == NULL) {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"open_connection: invalid IP address or socket name");
+		goto error;
+	}
+	
+	handle->proto = proto;
+	switch(proto) {
+		case UDP_SOCK:
+		case TCP_SOCK:
+			if (port == 0) {
+				port=DEFAULT_CTL_PORT;
+			}
+			
+			handle->sock_type = (proto == UDP_SOCK) ? SOCK_DGRAM : SOCK_STREAM;
+			if ((handle->socket = connect_tcpudp_socket(name, port, handle->sock_type)) < 0) {
+				goto error;
+			}
+			break;
+		case UNIXS_SOCK:
+		case UNIXD_SOCK:
+			handle->sock_type = (proto == UNIXD_SOCK) ? SOCK_DGRAM : SOCK_STREAM;
+			if ((handle->socket = connect_unix_sock(name, handle->sock_type, &mysun, reply_socket, sock_dir)) < 0) {
+				goto error;
+			}
+			break;
+		case UNKNOWN_SOCK:
+			snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+				"open_connection: Bad socket type for %s\n", name); /*vm zmenit*/
+			goto error;
+	}
+	if (handle->sock_type == SOCK_DGRAM) {
+		handle->buf_size = 8192;  /* max size of datagram, < SSIZE_MAX, TODO: does a platform dependent constant exist ? */
+	}
+	else {
+		handle->buf_size = BINRPC_MAX_HDR_SIZE;	
+	}
+	handle->buf = binrpc_malloc(handle->buf_size);
+	if (!handle->buf) {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"open_connection: not enough memory to allocate buffer. Needed %d bytes", handle->buf_size);	
+		binrpc_close_connection(handle);
+	}
+	cleanup(&mysun);
+	return 0;
+	
+error:
+	cleanup(&mysun);
+	return FATAL_ERROR;
+}
+
+int binrpc_open_connection_url(struct binrpc_handle* handle, char* url) {
+	static char name[100];
+	char *c, *c2, *rpl_sock;
+	int port, proto, i;
+	handle->socket = -1;
+	handle->buf = NULL;
+	/* parse proto:name:port|unixd_sock */
+	c = url;
+	if (strncasecmp(c, "udp:", 4) == 0)
+		proto = UDP_SOCK;
+	else if (strncasecmp(c, "tcp:", 4) == 0)
+		proto = TCP_SOCK;
+	else if (strncasecmp(c, "unix:", 5) == 0 || strncasecmp(c, "unixs:", 6) == 0)
+		proto = UNIXS_SOCK;
+	else if (strncasecmp(c, "unixd:", 6) == 0)
+		proto = UNIXD_SOCK;
+	else {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"open_connection_url: bad protocol in '%s'", c);
+		return FATAL_ERROR;	
+	}
+	while (*c != ':') c++;
+	c++;
+	c2 = strchr(c, ':');
+	if (!c2)
+		c2 = c + strlen(c);
+	if (c2 - c > sizeof(name)-1) {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"open_connection_url: name is too long '%s'", c);
+		return FATAL_ERROR;	
+	}
+	for (i=0; c<c2; c++, i++) {
+		name[i] = *c;
+	}
+	name[i] = '\0';
+	if (strlen(name) == 0) {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"open_connection_url: name is not specified in '%s'", url);
+		return FATAL_ERROR;	
+	}
+	c = c2;
+	if (*c == ':') c++;
+
+	port = 0;
+	rpl_sock = NULL;
+	switch (proto) {
+		case UNIXD_SOCK:
+			if (strlen(c) != 0)
+				rpl_sock = c;
+			break;
+		case UNIXS_SOCK:
+			break;
+		default:
+			port = atol(c);
+			if (port == 0) {
+				snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+					"open_connection_url: port is not specified in '%s'", url);
+				return FATAL_ERROR;	
+			}
+			break;
+	}	
+	return binrpc_open_connection(handle, name, port, proto, rpl_sock, NULL);
+}
+
+void binrpc_close_connection(struct binrpc_handle* handle)
+{
+	if (handle->socket != -1) {
+		close(handle->socket);
+		handle->socket = -1;
+	}
+	if (handle->buf) {
+		binrpc_free(handle->buf);
+		handle->buf = NULL;
+	}
+}
+
+/* returns: -1 on error, number of bytes written on success */
+static int send_binrpc_cmd(struct binrpc_handle* handle, struct binrpc_pkt *pkt, int cookie)
+{
+	struct iovec v[IOVEC_CNT];
+	unsigned char msg_hdr[BINRPC_MAX_HDR_SIZE];
+	int n;
+	
+	if ((n=binrpc_build_hdr(BINRPC_REQ, binrpc_pkt_len(pkt), cookie, msg_hdr,
+							BINRPC_MAX_HDR_SIZE)) < 0) {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1, 
+			"send_binrpc_cmd: build header error: %s",
+			binrpc_error(n));
+		return FATAL_ERROR;
+	}
+	v[0].iov_base=msg_hdr;
+	v[0].iov_len=n;
+	v[1].iov_base=pkt->body;
+	v[1].iov_len=binrpc_pkt_len(pkt);
+write_again:
+	if ((n=writev(handle->socket, v, 2))<0){
+		if (errno==EINTR)
+			goto write_again;
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1, 
+			"send_binrpc_cmd: send packet failed: %s (%d)", strerror(errno), errno);
+		return FATAL_ERROR;
+	}
+	return n;
+}
+
+
+/* reads the whole reply
+ * returns < 0 on error, reply size on success + initializes resp_handle */
+static int get_reply(struct binrpc_handle *handle, 
+			int cookie, 
+			struct binrpc_response_handle *resp_handle)
+{
+	unsigned char *crt, *hdr_end;
+	int n, ret, tl;
+	
+	ret = 0;
+	resp_handle->reply_buf = NULL;
+	hdr_end = crt = handle->buf;
+	
+	do {		
+		n = read(handle->socket, crt, handle->buf_size - (crt-handle->buf));
+		if (n < 0){
+			if (errno==EINTR)
+				continue;
+			snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+				"get_reply: read reply failed: %s (%d)",
+				strerror(errno), errno);
+			return FATAL_ERROR;
+		}
+		if (verbose >= 3){
+			/* dump it in hex */
+			printf("received %d bytes in reply (@offset %d):\n", 
+			       n, (int)(crt-handle->buf));
+			hexdump(crt, n, 1);
+		}
+		crt += n;
+		hdr_end = binrpc_parse_init(&resp_handle->in_pkt, handle->buf, crt - handle->buf, &ret);
+		
+	} while (ret == E_BINRPC_MORE_DATA);
+	if (ret < 0){
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"get_reply: reply parsing error: %s",
+			binrpc_error(ret));
+		return FATAL_ERROR;
+	}
+
+	if (verbose>1){
+		printf("new packet: type %02x, len %d, cookie %02x\n",
+				resp_handle->in_pkt.type, resp_handle->in_pkt.tlen, resp_handle->in_pkt.cookie);
+	}
+	if (resp_handle->in_pkt.cookie!=cookie){
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"get_reply: reply parsing error: "
+			"cookie doesn't match: sent: %02x, received: %02x", 
+			cookie, resp_handle->in_pkt.cookie);
+		return FATAL_ERROR;
+	}
+
+	/* we know total size and we can allocate buffer for received data */
+	tl = resp_handle->in_pkt.tlen;
+	
+	if (handle->sock_type == SOCK_DGRAM) {
+		/* we must read all datagram in one read call, otherwise unread part is truncated and lost. Read will block execution */
+		if (crt - hdr_end < tl) {
+			snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+				"get_reply: datagram truncated. Received: %d, Expected: %d.",
+				crt-hdr_end, tl);
+			return FATAL_ERROR;		
+		}
+	}
+	if (crt - hdr_end > tl) {
+		/* header contains probably data from next message, in case of STREAM it could be unread but it's waste of time */
+		crt = hdr_end + tl;	
+	}
+	
+	resp_handle->reply_buf = (unsigned char *) binrpc_malloc(tl);
+	if (!resp_handle->reply_buf) {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"get_reply: not enough memory to allocate reply buffer. %d bytes needed.",
+			resp_handle->in_pkt.tlen);
+		return FATAL_ERROR;
+	}
+	crt = resp_handle->reply_buf + (crt-hdr_end);
+	memcpy(resp_handle->reply_buf, hdr_end, crt - resp_handle->reply_buf);
+	tl -= crt - resp_handle->reply_buf;
+	while (tl > 0) {
+		n=read(handle->socket, crt, tl);
+		if (n < 0){
+			if (errno==EINTR)
+				continue;
+			snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+				"get_reply: read reply failed: %s (%d)",
+				strerror(errno), errno);
+			binrpc_free(resp_handle->reply_buf);
+			resp_handle->reply_buf = NULL;
+			return FATAL_ERROR;
+		}
+		if (verbose >= 3){
+			/* dump it in hex */
+			printf("received %d bytes in reply (@offset %d):\n", 
+			       n, (int)(crt-resp_handle->reply_buf));
+			hexdump(crt, n, 1);
+		}
+		crt += n;
+		tl -= n;
+	}
+	
+	return (int)(crt-resp_handle->reply_buf);
+}
+
+int binrpc_send_command_ex(
+	struct binrpc_handle* handle, struct binrpc_pkt *pkt, 
+	struct binrpc_response_handle *resp_handle)
+{
+	int cookie;
+	
+	cookie = gen_cookie();
+	if (send_binrpc_cmd(handle, pkt, cookie) < 0) {
+		return FATAL_ERROR;
+	}
+	/* read reply */
+	memset(&resp_handle->in_pkt, 0, sizeof(resp_handle->in_pkt));
+	if (get_reply(handle, cookie, resp_handle) < 0) {
+		return FATAL_ERROR;
+	}
+	
+	/* normal exit */
+	return 0;
+}
+
+static int parse_arg(struct binrpc_val* v, char* arg)
+{
+	int i;
+	double f;
+	char* tmp;
+	int len;
+
+  f = 0.0;
+
+	if (*arg)
+		i=strtol(arg, &tmp, 10);
+	else
+		tmp = 0;
+	if ((tmp==0) || (*tmp)){
+		if (*arg)
+			f=strtod(arg, &tmp);
+		if ((tmp==0) || (*tmp)){
+			/* not an int or a float => string */
+			len=strlen(arg);
+			if ((len>=2) && (arg[0]=='s') && (arg[1]==':')){
+				tmp=&arg[2];
+				len-=2;
+			}else{
+				tmp=arg;
+			}
+			v->type=BINRPC_T_STR;
+			v->u.strval.s=tmp;
+			v->u.strval.len=len;
+		}else{ /* float */
+			v->type=BINRPC_T_DOUBLE;
+			v->u.fval=f;
+		}
+	}else{ /* int */
+		v->type=BINRPC_T_INT;
+		v->u.intval=i;
+	}
+	return 0;
+}
+
+
+/* parse the body into a malloc allocated,  binrpc_val array */
+int binrpc_send_command(
+	struct binrpc_handle* handle, char* method, char** args, int arg_count,
+	struct binrpc_response_handle *resp_handle)
+{
+	struct binrpc_pkt req_pkt;
+	struct binrpc_val v;
+	int i, size, res = FATAL_ERROR, ret = 0;
+	unsigned char *req_buf = NULL;
+
+	memset(&resp_handle->in_pkt, 0, sizeof(resp_handle->in_pkt));	
+	if (!method || strlen(method) == 0) {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"send_command: method name not specified");
+		goto fail;
+	
+	}
+	size = BINRPC_MIN_RECORD_SIZE + 8 + strlen(method) + 1; /*max.possible optional value len */	
+	for (i=0; i<arg_count; i++) {
+		if (parse_arg(&v, args[i]) < 0)
+			goto fail;
+		switch (v.type) {
+			case BINRPC_T_STR:
+				size += v.u.strval.len + 1;
+				break;
+			case BINRPC_T_INT:
+			case BINRPC_T_DOUBLE:
+				size += sizeof(int);
+				break;
+			default:
+				snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+					"BUG: send_command: unexpected value type");
+				goto fail;
+		}
+		size +=  BINRPC_MIN_RECORD_SIZE + 8;
+	}
+	req_buf = binrpc_malloc(size);
+	if (!req_buf) {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"send_command: not enough memory to allocate buffer. Needed %d bytes", size);
+		goto fail;
+	}
+	if ((ret = binrpc_init_pkt(&req_pkt, req_buf, size)) < 0) goto fail2;
+
+	if ((ret = binrpc_addstr(&req_pkt, method, strlen(method))) < 0) goto fail2;
+
+	for (i=0; i<arg_count; i++) {
+		if (parse_arg(&v, args[i]) < 0)
+			goto fail;
+		switch (v.type) {
+			case BINRPC_T_STR:
+				if ((ret = binrpc_addstr(&req_pkt,  v.u.strval.s,  v.u.strval.len)) < 0) goto fail2;
+				break;
+			case BINRPC_T_INT:
+				if ((ret = binrpc_addint(&req_pkt, v.u.intval)) < 0) goto fail2;
+				break;
+			case BINRPC_T_DOUBLE:
+				if ((ret = binrpc_adddouble(&req_pkt, v.u.fval)) < 0) goto fail2;
+				break;
+			default:
+				break;
+		}
+	}
+
+	if (binrpc_send_command_ex(handle, &req_pkt, resp_handle) < 0) {
+		goto fail;
+	}
+	res = 0;
+fail:	
+	if (req_buf) binrpc_free(req_buf);
+	return res;	
+fail2:
+	snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+		"send_command: error when preparing params: %s", binrpc_error(ret));
+	goto fail;
+}
+
+void binrpc_release_response(struct binrpc_response_handle *resp_handle) {
+	if (resp_handle->reply_buf) {
+		binrpc_free(resp_handle->reply_buf);
+		resp_handle->reply_buf = NULL;
+	}
+}
+
+int binrpc_get_response_type(struct binrpc_response_handle *resp_handle)
+{
+	switch(resp_handle->in_pkt.type) {
+		case BINRPC_FAULT:
+			return 1;
+			break;
+		case BINRPC_REPL:
+			return 0;
+			break;
+		default:
+			snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+				"BUG: get_response_type: not a reply");
+			return FATAL_ERROR;
+	}
+}
+
+/* parses strings like "bla bla %v 10%% %v\n test=%v",
+ * and stops at each %v,  returning  a pointer after the %v, setting *size
+ * to the string length (not including %v) and *type to the corresponding
+ * BINRPC type (for now only BINRPC_T_ALL).
+ * To escape a '%', use "%%", and check for type==-1 (which means skip an call
+ *  again parse_fmt).
+ * Usage:
+ *        n="test: %v,%v,%v\n";
+ *        while(*n){
+ *          s=n;
+ *          n=parse_fmt(n, &type, &size);
+ *          printf("%.*s", size, s);
+ *          if (type==-1)
+ *            continue;
+ *          else 
+ *             printf("now we should get & print an object of type %d\n", type)
+ *        }
+ */
+static char* parse_fmt(char* fmt, int* type, int* size)
+{
+	char* s;
+
+	s=fmt;
+	do{
+		for(;*fmt && *fmt!='%'; fmt++);
+		if (*fmt=='%'){
+			switch(*(fmt+1)){
+				case 'v':
+					*type=BINRPC_T_ALL;
+					*size=(int)(fmt-s);
+					return (fmt+2);
+					break;
+				case '%':
+					/* escaped % */
+					*size=(int)(fmt-s)+1;
+					*type=-1; /* skip */
+					return (fmt+2);
+					break;
+			}
+		}
+	}while(*fmt);
+	*type=-1; /* no value */
+	*size=(fmt-s);
+	return fmt;
+}
+
+static void print_binrpc_val(struct binrpc_val* v, int ident)
+{
+	int r;
+
+	if ((v->type==BINRPC_T_STRUCT) && !v->u.end)
+		ident--; /* fix to have strut beg. idented differently */
+	for (r=0; r<ident; r++) putchar('	');
+	if (v->name.s){
+		printf("%.*s: ", v->name.len, v->name.s);
+	}
+	switch(v->type){
+		case BINRPC_T_INT:
+			printf("%d", v->u.intval);
+			break;
+		case BINRPC_T_STR:
+		case BINRPC_T_BYTES:
+			printf("%.*s", v->u.strval.len, v->u.strval.s);
+			break;
+		case BINRPC_T_ARRAY:
+			printf("%c", (v->u.end)?']':'[');
+			break;
+		case BINRPC_T_STRUCT:
+			printf("%c", (v->u.end)?'}':'{');
+			break;
+			default:
+				printf("ERROR: unknown type %d\n", v->type);
+	}
+}
+
+int binrpc_print_response(struct binrpc_response_handle *resp_handle, char* fmt)
+{
+	unsigned char* p;
+	unsigned char* end;
+	struct binrpc_val val;
+	int ret;
+	int rec;
+	char *f;
+	char* s;
+	int f_size;
+	int fmt_has_values;
+	
+	if (!resp_handle) {
+		goto error;
+	}
+	resp_handle->in_pkt.offset = resp_handle->in_pkt.in_struct = resp_handle->in_pkt.in_array = 0;
+
+	p=resp_handle->reply_buf;
+	end=p+resp_handle->in_pkt.tlen;
+	rec=0;
+	f=fmt;
+	fmt_has_values=0;
+	/* read body */
+	while(p<end){
+		if (f){
+					
+			do{
+				if (*f==0)
+					f=fmt; /* reset */
+				s=f;
+				f=parse_fmt(f, &val.type, &f_size);
+				printf("%.*s", f_size, s);
+				if (val.type!=-1){
+					fmt_has_values=1;
+					goto read_value;
+				}
+			}while(*f || fmt_has_values);
+			val.type=BINRPC_T_ALL;
+		}else{
+			val.type=BINRPC_T_ALL;
+		}
+read_value:
+		val.name.s=0;
+		val.name.len=0;
+		p=binrpc_read_record(&resp_handle->in_pkt, p, end, &val, &ret);
+		if (ret<0){
+			if (fmt)
+				putchar('\n');
+			/*if (ret==E_BINRPC_MORE_DATA)
+				goto error_read_again;*/
+			if (ret==E_BINRPC_EOP){
+				printf("end of message detected\n");
+				break;
+			}
+			snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+					"error while parsing the record %d,"
+					" @%d: %02x : %s", rec,
+					resp_handle->in_pkt.offset, *p, binrpc_error(ret));
+			goto error;
+		}
+		rec++;
+		if (fmt){
+			print_binrpc_val(&val, 0);
+		}else{
+			print_binrpc_val(&val, resp_handle->in_pkt.in_struct+resp_handle->in_pkt.in_array);
+			putchar('\n');
+		}
+	}
+	if (fmt && *f){
+		/* print the rest, with empty values */
+		while(*f){
+			s=f;
+			f=parse_fmt(f, &val.type, &f_size);
+			printf("%.*s", f_size, s);
+		}
+	}
+	return 0;
+error:
+	return FATAL_ERROR;
+/*error_read_again:
+	snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+		"ERROR: more data needed");
+	return -2;
+	*/
+}
+
+#define VAL_ARRAY_CHUNK 100
+int binrpc_parse_response(struct binrpc_val** vals, int* val_count,
+	struct binrpc_response_handle *resp_handle)
+{
+	struct binrpc_val val;
+	unsigned char *p, *end;
+	int ret, i;
+
+	resp_handle->in_pkt.offset = resp_handle->in_pkt.in_struct = resp_handle->in_pkt.in_array = 0;
+
+	if (*val_count==0){
+		*val_count=VAL_ARRAY_CHUNK; /* start with a reasonable size */
+	}
+	*vals = (struct binrpc_val*) binrpc_malloc(*val_count*sizeof(**vals));
+	if (*vals == 0)
+		goto error_mem;
+	p = resp_handle->reply_buf;
+	end = p + resp_handle->in_pkt.tlen;
+	i=0;
+	
+	/* read body */
+	while(p < end){
+		val.type = BINRPC_T_ALL;
+		val.name.s = 0;
+		val.name.len = 0;
+		p = binrpc_read_record(&resp_handle->in_pkt, p, end, &val, &ret);
+		if (ret<0){
+			if (ret==E_BINRPC_EOP){
+				break;
+			}
+			snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+					"ERROR while parsing the record %d,"
+					" @%d: %02x : %s", i,
+					resp_handle->in_pkt.offset, *p, binrpc_error(ret));
+			goto error;
+		}
+		if (i >= *val_count){
+			struct binrpc_val *t;
+			t= (struct binrpc_val*) binrpc_realloc(*vals, (VAL_ARRAY_CHUNK+(*val_count))*sizeof(**vals));
+			if (t==0)
+				goto error_mem;
+			*vals = t;
+			*val_count += VAL_ARRAY_CHUNK;
+		}
+		(*vals)[i] = val;
+		i++;
+	}
+	if (i == 0) {
+		binrpc_free(*vals);
+		*vals = NULL;
+	}
+	else if (i<*val_count){
+/*		do not try to save memory because it causes fragmentation when used ser mem utils and "regualar" memory leak
+		struct binrpc_val *t;
+		t = (struct binrpc_val*) binrpc_realloc(*vals, i*sizeof(**vals));
+		if (t) *vals = t;
+*/
+	}
+	*val_count = i;
+	return 0;
+error_mem:
+	snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+		"parse_response: out of memory");
+error:
+	if (*vals){
+		binrpc_free(*vals);
+		*vals = NULL;
+	}
+	*val_count=0;
+	return FATAL_ERROR;
+}
+
+int binrpc_parse_error_response(
+	struct binrpc_response_handle *resp_handle,
+	int *err_no,
+	char **err) 
+{
+	struct binrpc_val val;
+	unsigned char *p, *end;
+	int ret;
+
+	resp_handle->in_pkt.offset = resp_handle->in_pkt.in_struct = resp_handle->in_pkt.in_array = 0;
+	p = resp_handle->reply_buf;
+	end = p+resp_handle->in_pkt.tlen;
+	
+	val.type=BINRPC_T_INT;
+	val.name.s=0;
+	val.name.len=0;
+	p = binrpc_read_record(&resp_handle->in_pkt, p, end, &val, &ret);
+	if (ret < 0) {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"parse_error_response: error when parsing reply (code): %s", binrpc_error(ret)
+		);
+		return FATAL_ERROR;
+	}
+	*err_no = val.u.intval;
+
+	val.type=BINRPC_T_STR;
+	p = binrpc_read_record(&resp_handle->in_pkt, p, end, &val, &ret);
+	if (ret < 0) {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"parse_error_response: error when parsing reply (str): %s", binrpc_error(ret)
+		);
+		return FATAL_ERROR;
+	}																																		
+	*err = val.u.strval.s;  /* it's null terminated */
+	return 0;
+}
+
+/* returns a pointer to a static buffer containing l in asciiz & sets len */
+static inline char* int2str(unsigned int l, int* len)
+{
+	static char r[INT2STR_MAX_LEN];
+	int i;
+	
+	i=INT2STR_MAX_LEN-2;
+	r[INT2STR_MAX_LEN-1]=0; /* null terminate */
+	do{
+		r[i]=l%10+'0';
+		i--;
+		l/=10;
+	}while(l && (i>=0));
+	if (l && (i<0)){
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"BUG: int2str: overflow");
+	}
+	if (len) *len=(INT2STR_MAX_LEN-2)-i;
+	return &r[i+1];
+}
+
+static int realloc_buf(unsigned char** buf, int* buf_len, int req_len)
+{ 
+	unsigned char*	tmp_buf;
+	int orig_len;
+	
+	orig_len = (*buf == NULL) ? 0 : strlen((char *) *buf);
+	*buf_len += (TEXT_BUFF_ALLOC_CHUNK < req_len) ? TEXT_BUFF_ALLOC_CHUNK + req_len : TEXT_BUFF_ALLOC_CHUNK;
+	
+	if (*buf == NULL)
+		tmp_buf = (unsigned char *) binrpc_malloc(orig_len + *buf_len);
+	else
+		tmp_buf = (unsigned char *) binrpc_realloc(*buf, orig_len + *buf_len);
+	if (tmp_buf == 0) {
+		snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+			"ERROR: out of memory");
+		return FATAL_ERROR;
+	}
+	
+	*buf = tmp_buf;
+	(*buf)[orig_len] = '\0';
+
+	return 0;
+}
+
+static inline int str2buffer(unsigned char** buf, int* buf_len, int* pos, 
+			     char* data, int data_len)
+{
+	if (*buf_len < data_len) {
+		if(realloc_buf(buf, buf_len, data_len) != 0) {
+			return FATAL_ERROR;
+		}
+	}
+	
+	memcpy(&(*buf)[*pos], data, data_len);
+	*pos += data_len;
+	*buf_len -= data_len;
+	
+	return 0;
+}
+
+static inline int char2buffer(unsigned char** buf, int* buf_len, int* pos, 
+			      char data)
+{
+	if (*buf_len < 1) {
+		if(realloc_buf(buf, buf_len, 1) != 0) {
+			return FATAL_ERROR;
+		}
+	}
+	
+	(*buf)[*pos] = data;
+	++(*pos);
+	--(*buf_len);
+	
+	return 0;
+}
+
+static int val2buffer(struct binrpc_val* v, unsigned char** buf, 
+			     int *buf_len, int* pos)
+{
+	char *number;
+	int num_len; 
+	 
+	if (v->name.s){
+		if(str2buffer(buf, buf_len, pos, v->name.s, v->name.len) != 0) {
+			return FATAL_ERROR;
+		}
+		if(str2buffer(buf, buf_len, pos, ": ", strlen(": ")) != 0) {  /* TODO: common format */
+			return FATAL_ERROR;
+		}
+		
+	}
+	
+	switch(v->type){
+		case BINRPC_T_INT:
+			num_len = 0;
+			number = NULL;
+			number = int2str(v->u.intval, &num_len);
+			if (number == NULL) {
+				printf("ERROR: Conversion of %d into string failed.\n", v->type);
+				return FATAL_ERROR;
+			}
+			
+			if(str2buffer(buf, buf_len, pos, number, num_len) != 0) {
+				return FATAL_ERROR;
+			}
+			break;
+		case BINRPC_T_STR:
+		case BINRPC_T_BYTES:
+			if(str2buffer(buf, buf_len, pos, v->u.strval.s, v->u.strval.len) != 0) {
+				return FATAL_ERROR;
+			}
+			break;
+		case BINRPC_T_ARRAY:
+			if(char2buffer(buf, buf_len, pos, (v->u.end) ? ']' : '[') != 0) {
+				return FATAL_ERROR;
+			}
+			break;
+		case BINRPC_T_STRUCT:
+			if(char2buffer(buf, buf_len, pos, (v->u.end) ? '}' : '{') != 0) {
+				return FATAL_ERROR;
+			}
+			break;
+		default:
+			printf("ERROR: unknown type %d\n", v->type);
+			return FATAL_ERROR;
+	};
+	
+	return 0;
+}
+
+int binrpc_response_to_text(
+	struct binrpc_response_handle *resp_handle,
+	unsigned char** txt_rsp, int* txt_rsp_len, char delimiter)
+{
+	unsigned char* p;
+	unsigned char* end;
+	struct binrpc_val val;
+	int ret;
+	int rec;
+	int pos;
+	
+	pos = 0;
+	
+	if (!resp_handle) {
+		goto error;
+	}
+
+	resp_handle->in_pkt.offset = resp_handle->in_pkt.in_struct = resp_handle->in_pkt.in_array = 0;
+	
+	p=resp_handle->reply_buf;
+	end=p+resp_handle->in_pkt.tlen;
+	rec=0;
+	
+	if (*txt_rsp == NULL) {
+		*txt_rsp_len = 0;
+		if (realloc_buf(txt_rsp, txt_rsp_len, 0) != 0) {
+			goto error;
+		} 
+	}
+	
+	/* read body */
+	while(p<end){
+		val.type=BINRPC_T_ALL;
+		val.name.s=0;
+		val.name.len=0;
+		p = binrpc_read_record(&resp_handle->in_pkt, p, end, &val, &ret);
+		if (ret < 0) {
+			if (ret == E_BINRPC_EOP) {
+				printf("end of message detected\n");
+				break;
+			}
+			snprintf(binrpc_last_errs, sizeof(binrpc_last_errs)-1,
+					"ERROR while parsing the record %d,"
+					" @%d: %02x : %s", rec,	resp_handle->in_pkt.offset, *p, binrpc_error(ret));
+			goto error;
+		}
+		rec++;
+		if (val2buffer(&val, txt_rsp, txt_rsp_len, &pos) != 0) {
+			goto error;
+		}
+		
+		if(char2buffer(txt_rsp, txt_rsp_len, &pos, delimiter) != 0) {
+			goto error;
+		}
+	}
+	
+	/* rewrite last char - we don't need delimiter there */
+	(*txt_rsp)[pos-1] = '\0';   
+	/*
+	if(char2buffer(txt_rsp, txt_rsp_len, &pos, '\0') != 0) {
+		goto error;
+	}
+	*/
+	return 0;
+error:
+	return FATAL_ERROR;
+}
+
+int main(int argc, char** argv)
+{
+	struct binrpc_response_handle resp_handle;
+	unsigned char* txt_rsp = NULL;
+	int txt_rsp_len = 0;
+	struct binrpc_handle handle;
+	struct binrpc_val *vals = NULL;
+	int cnt, i, err_no;
+	char *errs;
+	
+	if (argc < 2) goto err;
+	
+	if (binrpc_open_connection_url(&handle, argv[1]) < 0) goto err2;
+	if (binrpc_send_command(&handle, argv[2], argv+3, argc-3, &resp_handle) < 0) {
+		binrpc_close_connection(&handle);
+		goto err2;
+	}
+	binrpc_close_connection(&handle);
+
+	if (binrpc_response_to_text(&resp_handle, &txt_rsp, &txt_rsp_len, '\n') < 0) goto err3;
+	fprintf(stdout, "binrpc_response_to_text():\n--------------------------\n%s\n", txt_rsp);
+	
+	fprintf(stdout, "\nbinrpc_print_response():\n------------------------\n");
+	binrpc_print_response(&resp_handle, NULL);
+	
+	fprintf(stdout, "\nbinrpc_parse_response():\n------------------------\n");
+	cnt = 0;
+	switch (binrpc_get_response_type(&resp_handle)) {
+		case 0:
+			if (binrpc_parse_response(&vals, &cnt, &resp_handle) < 0) goto err3;
+			fprintf(stdout, "#Records: %d\n", cnt);
+			for (i = 0; i < cnt; i++) {
+				fprintf(stdout, "#%.2d: type:%d name:%.*s\n", i, vals[i].type, vals[i].name.len, vals[i].name.s);
+			}
+			break;
+		case 1:
+			if (binrpc_parse_error_response(&resp_handle, &err_no, &errs) <0) goto err3;
+			fprintf(stdout, "%d %s\n", err_no, errs);
+			break;
+		default:
+			fprintf(stdout, "Unknown response type: %d\n", binrpc_get_response_type(&resp_handle));	
+			break;
+	}
+
+	if (vals != NULL) {
+		binrpc_free(vals);
+	}	
+	if (txt_rsp != NULL) {
+		binrpc_free(txt_rsp);
+	}
+	binrpc_release_response(&resp_handle);
+	
+	return 0;
+err:
+	fprintf(stderr, "Usage: %s url mathod [params]\n", NAME);
+	return -1;
+err3:
+	if (vals != NULL) {
+		binrpc_free(vals);
+	}	
+	if (txt_rsp) {
+		binrpc_free(txt_rsp);
+	}
+	binrpc_release_response(&resp_handle);
+err2:
+	fprintf(stderr, "ERROR: %s\n", binrpc_get_last_errs());
+	return -2;
+}
+

+ 291 - 0
lib/binrpc/binrpc_api.h

@@ -0,0 +1,291 @@
+/*
+ * $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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * History:
+ * --------
+ *  2006-11-09  created (vlada)
+ */
+
+#ifndef BINRPC_API_H_
+#define BINRPC_API_H_
+
+#include "../../modules/ctl/binrpc.h"
+
+struct binrpc_handle {
+	int socket;
+	int proto;
+	int sock_type;
+	unsigned char* buf;
+	int buf_size;
+};
+
+struct binrpc_response_handle {
+	unsigned char *reply_buf;
+	struct binrpc_parse_ctx in_pkt;
+};
+
+/**
+ * Function: binrpc_open_connection
+ * 
+ * Description:
+ *   The function open_connection ensures opening of appropriate device for 
+ *   future communication. It can create unix socket or TCP/UDP connection 
+ *   depending on input parameteres.
+ * 
+ * @param handler [in]: handler that will be used for saving of obtained socket;
+ *   if this function succeed, this handler must be freed via calling of 
+ *   binrpc_close_connection function
+ * @param name [in]: host IP address or FQDN or unix socket name
+ * @param port [in]: host port; in case of unix socket the value is omitted
+ * @param proto [in]: type of communication protocol; allowed values are 
+ *   UDP_SOCK, TCP_SOCK, UNIXS_SOCK, UNIXD_SOCK
+ * @param reply_socket [in]: force reply socket name, for the unix datagram 
+ *   socket mode
+ * @param sock_dir [in]: specify directory where the reply socket will be 
+ *   created; if set to NULL, the default value will be used (/tmp)
+ * 
+ * @return 0 on success, -1 on failure.
+ * 
+ * */ 
+int binrpc_open_connection(
+	struct binrpc_handle* handle,
+	char* name, int port, int proto,
+	char* reply_socket, char* sock_dir);
+
+/**
+ * Function: binrpc_open_connection_url
+ * 
+ * Description:
+ *   The function is similar as open_connection but target is specified using url.
+ * 
+ * @param handle [in]: handler that will be used for saving of obtained socket;
+ *   if this function succeed, this handle must be freed via calling of 
+ *   binrpc_close_connection function
+ * @param url [in]: [tcp|udp|unix|unixs|unixd] ":" host_socket ":" [port | reply_socket]
+ *   Note: unix = unixs
+ * 
+ * @return 0 on success, -1 on failure.
+ * 
+ * */ 
+
+int binrpc_open_connection_url(struct binrpc_handle* handle, char* url);
+                    
+/**
+ * Function: binrpc_close_connection
+ * 
+ * Description:
+ *   The function close_connection ensures freeing of active socket that is 
+ *   represent by handler
+ *
+ * @param handle [in]: active connection descriptor
+ * 
+ * @return 0 on success, -1 on failure.
+ *   none
+ * */
+void binrpc_close_connection(struct binrpc_handle* handle);
+
+/**
+ * Function: binrpc_send_command
+ * 
+ * Description:
+ *   The function send_command provides interface for communication with server 
+ *   application via binary rpc protocol. It sends request over unix socket or 
+ *   TCP/UDPto the host and reads a respons.
+ * 
+ * @param handle [in]: a descriptor of connection
+ * @param method [in]: string value of XMLRPC method (e.g. system.listMethods)
+ * @param args [in]: two dimension array of method's attributes
+ * @param arg_count [in]: number of method's attributes
+ * @param resp_handle [out]: structure for holding binary form of response, must be deallocated using binrpc_release_response
+ * 
+ * @return 0 on success, -1 on failure. 
+ * 
+ * */
+int binrpc_send_command(
+	struct binrpc_handle* handle, 
+	char* method, char** args, int arg_count,
+	struct binrpc_response_handle* resp_handle);
+
+/**
+ * Function: binrpc_send_command_ex
+ *
+ * Description:
+ *   The function send_command_ex is equivalent of send_command and in addition
+ *   provides possibility to pass already prepared input values.
+ *
+ * @param handle [in]: a descriptor of connection
+ * @param cmd [in]: method and method attributes
+ * @param resp_handle [out]: structure for holding binary form of response, must be deallocated using binrpc_release_response
+ *
+ * @return 0 on success, -1 on failure.
+ *
+ * */
+int binrpc_send_command_ex(
+	struct binrpc_handle* handle, struct binrpc_pkt* pkt,
+	struct binrpc_response_handle *resp_handle);
+
+/**
+ * Function: binrpc_release_response
+ *
+ * Description:
+ *   The function releases response handle created in binrpc_send_command
+ *
+ * @param resp_handle [in]: structure for holding binary form of response to be released
+ *
+ * @return 0 on success, -1 on failure.
+ *
+ * */
+void binrpc_release_response(
+	struct binrpc_response_handle *resp_handle
+);
+
+/**
+ * Function: binrpc_get_response_type
+ * 
+ * Description:
+ *   The function get_response_type provides information about type of response.
+ * 
+ * @return 1 on valid failure response, 0 on valid successfull, -1 on failure.
+ * 
+ * */                
+int binrpc_get_response_type(struct binrpc_response_handle *resp_handle);
+
+/* 
+ * Function: binrpc_parse_response
+ * 
+ * Description:
+ *   parse the body into a malloc allocated, binrpc_val array. Ensure that caller and callee are using the same structure alignment!
+ * 
+ * @param vals [out]: array of values allocated via (binrpc)malloc; it must be freed "manually"
+ * @param val_count [in/out]: number of records in a list
+ * @param resp_handle [in]: structure for holding binary form of response
+ * 
+ * @return -1 failure.
+ * 
+ * */
+int binrpc_parse_response(
+	struct binrpc_val** vals,
+	int* val_count,
+	struct binrpc_response_handle *resp_handle
+);
+					
+/* 
+ * Function: binrpc_parse_error_response
+ * 
+ * Description:
+ *   parse the error response
+ * 
+ * @param resp_handle [in]: structure for holding binary form of response
+ * @param err_no [out]: error code
+ * @param err [out]: error stringt
+ * 
+ * @return -1 failure.
+ * 
+ * */
+int binrpc_parse_error_response(
+	struct binrpc_response_handle *resp_handle,
+	int *err_no,
+	char **err
+);
+
+/**
+ * Function: binrpc_print_response
+ * 
+ * Description:
+ *   The function print_response prints binrpc response to the standard output in 
+ *   readable format.
+ * 
+ * @param resp_handle [in]: structure for holding binary form of response
+ * @param fmt [in]: output format that will be used during printing response to 
+ *   the standard output
+ * 
+ * @return 0 on success, -1 on failure
+ *  
+ * */ 
+int binrpc_print_response(struct binrpc_response_handle *resp_handle, char* fmt);
+
+/**
+ * Function: binrpc_response_to_text
+ * 
+ * Description: 
+ *   The function binrpc_response_to_text provides functionality to convert result from 
+ *   binary form into null terminated text buffer. Records from response are 
+ *   separated with character provided in parameter delimiter.
+ * 
+ * @param resp_handle [in]: structure for holding binary form of response
+ * @param txt_rsp [out]: buffer that will be used for text form of result; it 
+ *   can be passed into function as a NULL and in this case the function will 
+ *   alloc some memory that must be freed by user! This function can also 
+ *   realloc txt_rsp buffer if there is not enough space for whole response
+ * @param txt_rsp_len [in/out]: this parameter specify number of allocated but 
+ *   empty characters in txt_rsp buffer; value of this parameter can be modify
+ *   in case that the reallocation will be necessary
+ * @param delimiter [in]: a character that will be used for separation of  
+ *    records; if no value is provided, the character for new line is used ('\n')
+ *
+ * @return 0 on success, -1 on failure
+ * 
+ * */
+int binrpc_response_to_text(
+	struct binrpc_response_handle *resp_handle, 
+        unsigned char** txt_rsp, int* txt_rsp_len, 
+	char delimiter);
+
+/**
+ * Function: binrpc_set_mallocs
+ * 
+ * Description: 
+ *   The function binrpc_set_mallocs allows to programmer use its own function 
+ *   for memory handling.
+ * 
+ * @param _malloc [in]:   pointer to function that ensures memory allocation
+ * @param _realloc [in]:  pointer to function that ensures memory reallocation
+ * @param _free [in]:     pointer to function that ensures memory deallocation
+ * 
+ * */
+void binrpc_set_mallocs(void* _malloc, void* _realloc, void* _free);
+
+/**
+ * Function: binrpc_get_last_errs
+ * 
+ * Description: 
+ *   The function returns last error that occured when function returned FATAL_ERROR
+ * 
+ * */
+char *binrpc_get_last_errs();
+
+/**
+ * Function: binrpc_clear_last_err
+ *
+ * Description:
+ *   The function clears binrpc_last_errs buffer
+ *
+ * */
+void binrpc_clear_last_err();
+
+#endif /*BINRPC_API_H_*/