1 /*
2  * Copyright (C) 2001-2003 FhG Fokus
3  * Copyright (C) 2005 iptelorg GmbH
4  *
5  * This file is part of Kamailio, a free SIP server.
6  *
7  * Kamailio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version
11  *
12  * Kamailio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22 
23 /*! \file
24  * \brief ctl module
25  * \ingroup ctl
26  *
27  */
28 
29 /*! \defgroup ctl Control binrpc socket
30  *
31  * Fifo server is a very powerful tool used to access easily
32  * ser's internals via textual interface, similarly to
33  * how internals of many operating systems are accessible
34  * via the proc file system. This might be used for
35  * making ser do things for you (such as initiating new
36  * transaction from webpages) or inspect server's health.
37  *
38  * FIFO server allows new functionality to be registered
39  * with it -- thats what register_fifo_cmd is good for.
40  * Remember, the initialization must take place before
41  * forking; best in init_module functions. When a function
42  * is registered, it can be always evoked by sending its
43  * name prefixed by colon to the FIFO.
44  *
45  * There are few commands already implemented in core.
46  * These are 'uptime' for looking at how long the server
47  * is alive and 'print' for debugging purposes.
48  *
49  * Every command sent to FIFO must be sent atomically to
50  * avoid intermixing with other commands and MUST be
51  * terminated by empty line so that the server is to able
52  * to find its end if it does not understand the command.
53  *
54  * File test/transaction.fifo illustrates example of use
55  * of t_uac command (part of TM module).
56  *
57  */
58 
59 #ifdef USE_FIFO
60 
61 #include <limits.h>
62 #include <stdlib.h>
63 #include <sys/types.h>
64 #include <unistd.h>
65 #include <stdio.h>
66 #include <errno.h>
67 #include <sys/stat.h>
68 #include <fcntl.h>
69 #include <signal.h>
70 #include <string.h>
71 #include <time.h>
72 #include <stdarg.h>
73 #ifdef USE_TCP
74 #include <sys/socket.h>
75 #endif
76 
77 #include "../../core/dprint.h"
78 #include "../../core/ut.h"
79 #include "../../core/error.h"
80 #include "../../core/config.h"
81 #include "../../core/globals.h"
82 #include "../../core/mem/mem.h"
83 #include "../../core/mem/shm_mem.h"
84 #include "../../core/sr_module.h"
85 #include "../../core/pt.h"
86 #include "../../core/rpc.h"
87 #include "../../core/tsend.h"
88 #include "fifo_server.h"
89 #include "io_listener.h"
90 #include "ctl.h"
91 
92 
93 #define MAX_FIFO_COMMAND        128    /* Maximum length of a FIFO server command */
94 #define MAX_CONSUME_BUFFER     1024    /* Buffer dimensions for FIFO server */
95 #define MAX_LINE_BUFFER        2048    /* Maximum parameter line length */
96 #define DEFAULT_REPLY_RETRIES     4    /* Default number of reply write attempts */
97 #define DEFAULT_REPLY_WAIT    80000    /* How long we should wait for the client, in micro seconds */
98 #define DEFAULT_FIFO_DIR    "/tmp/"    /* Where reply pipes may be opened */
99 #define MAX_MSG_CHUNKS        1024     /* maximum message pieces */
100 #define FIFO_TX_TIMEOUT        200     /* maximum time to block writing to
101                                           the fifo */
102 
103 /* readline from a buffer helper */
104 struct readline_handle{
105 	char* s;    /* buffer start */
106 	char* end;  /* end */
107 	char* crt;  /* crt. pos */
108 };
109 
110 enum text_flags {
111 	CHUNK_SEEN         = (1 << 0),
112 	CHUNK_POSITIONAL   = (1 << 1), /* Positinal parameter, should be followed by \n */
113 	CHUNK_MEMBER_NAME  = (1 << 2), /* Struct member name, should be followed by : */
114 	CHUNK_MEMBER_VALUE = (1 << 3)  /* Struct member value, should be followed by , if
115 					* there is another member name and \n if not */
116 };
117 
118 
119 /*
120  * Generit text chunk. Flags attribute contains arbitrary flags
121  */
122 struct text_chunk {
123 	unsigned char flags;
124 	str s;
125 	struct text_chunk* next;
126 	void *ctx; /* context, which must be passed along */
127 };
128 
129 
130 /*
131  * This is the parameter if rpc_struct_add
132  */
133 struct rpc_struct_out {
134 	struct rpc_context* ctx;
135 	struct text_chunk* line;
136 };
137 
138 
139 struct rpc_struct {
140 	struct rpc_context* ctx;
141 	struct text_chunk* names;  /* Names of elements */
142 	struct text_chunk* values; /* Element values as strings */
143 	struct rpc_struct* next;
144 };
145 
146 
147 /*
148  * Context structure containing state of processing
149  */
150 typedef struct rpc_context {
151 	char* method;               /* Request method name */
152 	char* reply_file;           /* Full path and name to the reply FIFO file */
153 	int reply_sent;             /* This flag ensures that we do not send a reply twice */
154 	int code;                   /* Reply code */
155 	char* reason;               /* Reason phrase */
156 	struct text_chunk* body;    /* First line to be appended as reply body */
157 	struct text_chunk* last;    /* Last body line */
158 	struct text_chunk* strs;    /* Strings to be collected at the end of processing */
159 	struct rpc_struct* structs; /* Structures to be collected at the end of processing */
160 	struct readline_handle read_h;
161 	struct send_handle* send_h;
162 	int line_no;
163 } rpc_ctx_t;
164 
165 
166 
167 
168 char* fifo_dir           = DEFAULT_FIFO_DIR;       /* dir where reply fifos are
169 													  allowed */
170 int   fifo_reply_retries = DEFAULT_REPLY_RETRIES;
171 int   fifo_reply_wait    = DEFAULT_REPLY_WAIT;
172 
173 
174 static rpc_t     func_param;        /* Pointers to implementation of RPC funtions */
175 
176 static int  rpc_send         (rpc_ctx_t* ctx);                                 /* Send the reply to the client */
177 static void rpc_fault        (rpc_ctx_t* ctx,       int code, char* fmt, ...); /* Signal a failure to the client */
178 static int  rpc_add          (rpc_ctx_t* ctx,       char* fmt, ...);           /* Add a new piece of data to the result */
179 static int  rpc_scan         (rpc_ctx_t* ctx,       char* fmt, ...);           /* Retrieve request parameters */
180 static int  rpc_rpl_printf   (rpc_ctx_t* ctx,       char* fmt, ...);           /* Add printf-like formated data to the result set */
181 static int  rpc_struct_add   (struct text_chunk* s, char* fmt, ...);           /* Create a new structure */
182 static int  rpc_struct_scan  (struct rpc_struct* s, char* fmt, ...);           /* Scan attributes of a structure */
183 static int  rpc_struct_printf(struct text_chunk* s, char* name, char* fmt, ...);
184 
185 
186 /*
187  * Escape string in buffer 'r' of length len. Write
188  * the escaped string in buffer dst. The destination
189  * buffer must exist and must be twice as big as the
190  * input buffer.
191  *
192  * Parameter all controls the set of characters to be
193  * escaped. If set to 1 then all characters, including
194  * structure delimiters, will be escaped. If set to
195  * 0 then only line delimiters, tab and zero will be
196  * escaped.
197  */
escape(str * dst,char * r,int len,int all)198 static void escape(str* dst, char* r, int len, int all)
199 {
200 	int i;
201 	char* w;
202 	if (!len) {
203 		dst->len = 0;
204 		return;
205 	}
206 
207 	w = dst->s;
208 	for(i = 0; i < len; i++) {
209 		switch(r[i]) {
210 		case '\n': *w++ = '\\'; *w++ = 'n';  break;
211 		case '\r': *w++ = '\\'; *w++ = 'r';  break;
212 		case '\t': *w++ = '\\'; *w++ = 't';  break;
213 		case '\\': *w++ = '\\'; *w++ = '\\'; break;
214 		case '\0': *w++ = '\\'; *w++ = '0';  break;
215 		case ':':
216 			if (all) {
217 				*w++ = '\\';
218 				*w++ = 'o';
219 			} else *w++ = r[i];
220 			break;
221 
222 		case ',':
223 			if (all) {
224 				*w++ = '\\';
225 				*w++ = 'c';
226 			} else *w++ = r[i];
227 			break;
228 
229 		default:
230 			*w++ = r[i];
231 			break;
232 		}
233 	}
234 	dst->len = w - dst->s;
235 }
236 
237 
238 /*
239  * Unescape the string in buffer 'r' of length len.
240  * The resulting string will be stored in buffer dst
241  * which must exist and must be at least as big as
242  * the source buffer. The function will update dst->len
243  * to the length of the resulting string.
244  *
245  * Return value 0 indicates success, -1 indicates
246  * formatting error.
247  */
unescape(str * dst,char * r,int len)248 static int unescape(str* dst, char* r, int len)
249 {
250 	char* w;
251 	int i;
252 
253 	if (!len) {
254 		dst->len = 0;
255 		return 0;
256 	}
257 
258 	w = dst->s;
259 	for(i = 0; i < len; i++) {
260 		switch(*r) {
261 		case '\\':
262 			r++;
263 			i++;
264 			switch(*r++) {
265 			case '\\': *w++ = '\\'; break;
266 			case 'n':  *w++ = '\n'; break;
267 			case 'r':  *w++ = '\r'; break;
268 			case 't':  *w++ = '\t'; break;
269 			case '0':  *w++ = '\0'; break;
270 			case 'c':  *w++ = ':';  break; /* Structure delimiter */
271 			case 'o':  *w++ = ',';  break; /* Structure delimiter */
272 			default:   return -1;
273 			}
274 			break;
275 
276 		default: *w++ = *r++; break;
277 		}
278 	}
279 	dst->len = w - dst->s;
280 	return 0;
281 }
282 
283 
284 /*
285  * Create a new text chunk, the input text will
286  * be escaped.
287  */
new_chunk_escape(str * src,int escape_all)288 struct text_chunk* new_chunk_escape(str* src, int escape_all)
289 {
290 	struct text_chunk* l;
291 	if (!src) return 0;
292 
293         l = ctl_malloc(sizeof(struct text_chunk));
294 	if (!l) {
295 		ERR("No Memory Left\n");
296 		return 0;
297 	}
298 	l->s.s = ctl_malloc(src->len * 2 + 1);
299 	if (!l->s.s) {
300 		ERR("No Memory Left\n");
301 		ctl_free(l);
302 		return 0;
303 	}
304 	l->next = 0;
305 	l->flags = 0;
306 	escape(&l->s, src->s, src->len, escape_all);
307 	l->s.s[l->s.len] = '\0';
308 	return l;
309 }
310 
311 /*
312  * Create a new text chunk, the input text
313  * will not be escaped. The function returns
314  * 0 on an error
315  */
new_chunk(str * src)316 struct text_chunk* new_chunk(str* src)
317 {
318 	struct text_chunk* l;
319 	if (!src) return 0;
320 
321         l = ctl_malloc(sizeof(struct text_chunk));
322 	if (!l) {
323 		ERR("No Memory Left\n");
324 		return 0;
325 	}
326 	l->s.s = ctl_malloc(src->len + 1);
327 	if (!l->s.s) {
328 		ERR("No Memory Left\n");
329 		ctl_free(l);
330 		return 0;
331 	}
332 	l->next = 0;
333 	l->flags = 0;
334 	memcpy(l->s.s, src->s, src->len);
335 	l->s.len = src->len;
336 	l->s.s[l->s.len] = '\0';
337 	return l;
338 }
339 
340 
341 /*
342  * Create a new text chunk, the input text
343  * will be unescaped first.
344  */
new_chunk_unescape(str * src)345 struct text_chunk* new_chunk_unescape(str* src)
346 {
347 	struct text_chunk* l;
348 	if (!src) return 0;
349 
350         l = ctl_malloc(sizeof(struct text_chunk));
351 	if (!l) {
352 		ERR("No Memory Left\n");
353 		return 0;
354 	}
355 	l->s.s = ctl_malloc(src->len + 1);
356 	if (!l->s.s) {
357 		ERR("No Memory Left\n");
358 		ctl_free(l);
359 		return 0;
360 	}
361 	l->next = 0;
362 	l->flags = 0;
363 	if (unescape(&l->s, src->s, src->len) < 0) {
364 		ctl_free(l->s.s);
365 		ctl_free(l);
366 		return 0;
367 	}
368 	l->s.s[l->s.len] = '\0';
369 	return l;
370 }
371 
372 
free_chunk(struct text_chunk * c)373 static void free_chunk(struct text_chunk* c)
374 {
375 	if (c && c->s.s) ctl_free(c->s.s);
376 	if (c) ctl_free(c);
377 }
378 
379 
free_struct(struct rpc_struct * s)380 static void free_struct(struct rpc_struct* s)
381 {
382 	struct text_chunk* c;
383 
384 	if (!s) return;
385 	while(s->names) {
386 		c = s->names;
387 		s->names = s->names->next;
388 		free_chunk(c);
389 	}
390 
391 	while(s->values) {
392 		c = s->values;
393 		s->values = s->values->next;
394 		free_chunk(c);
395 	}
396 
397 	ctl_free(s);
398 }
399 
400 
401 /*
402  * Parse a structure
403  */
new_struct(rpc_ctx_t * ctx,str * line)404 static struct rpc_struct* new_struct(rpc_ctx_t* ctx, str* line)
405 {
406 	char* comma, *colon;
407 	struct rpc_struct* s;
408 	str left, right = STR_NULL, name, value;
409 	struct text_chunk* n, *v;
410 
411 	if (!line->len) {
412 		rpc_fault(ctx, 400, "Line %d Empty - Structure Expected",
413 					ctx->line_no);
414 		return 0;
415 	}
416 
417 	s = (struct rpc_struct*)ctl_malloc(sizeof(struct rpc_struct));
418 	if (!s) {
419 		rpc_fault(ctx, 500, "Internal Server Error (No Memory Left)");
420 		return 0;
421 	}
422 	memset(s, 0, sizeof(struct rpc_struct));
423 	s->ctx = ctx;
424 
425 	left = *line;
426 	do {
427 		comma = q_memchr(left.s, ',', left.len);
428 		if (comma) {
429 			right.s = comma + 1;
430 			right.len = left.len - (comma - left.s) - 1;
431 			left.len = comma - left.s;
432 		}
433 
434 		     /* Split the record to name and value */
435 		colon = q_memchr(left.s, ':', left.len);
436 		if (!colon) {
437 			rpc_fault(ctx, 400, "Colon missing in struct on line %d",
438 							ctx->line_no);
439 			goto err;;
440 		}
441 		name.s = left.s;
442 		name.len = colon - name.s;
443 		value.s = colon + 1;
444 		value.len = left.len - (colon - left.s) - 1;
445 
446 		     /* Create name chunk */
447 		n = new_chunk_unescape(&name);
448 		if (!n) {
449 			rpc_fault(ctx, 400, "Error while processing struct member '%.*s' "
450 						"on line %d", name.len, ZSW(name.s), ctx->line_no);
451 			goto err;
452 		}
453 		n->next = s->names;
454 		s->names = n;
455 
456 		     /* Create value chunk */
457 		v = new_chunk_unescape(&value);
458 		if (!v) {
459 			rpc_fault(ctx, 400, "Error while processing struct membeer '%.*s'"
460 						" on line %d", name.len, ZSW(name.s), ctx->line_no);
461 			goto err;
462 		}
463 		v->next = s->values;
464 		s->values = v;
465 
466 		left = right;
467 	} while(comma);
468 
469 	return s;
470  err:
471 	if (s) free_struct(s);
472 	return 0;
473 }
474 
475 
476 /*
477  * Read a line from FIFO file and store a pointer to the data in buffer 'b'
478  * and the legnth of the line in variable 'read'
479  *
480  * Returns -1 on error, 0 on success
481  */
read_line(char ** b,int * read,struct readline_handle * rh)482 static int read_line(char** b, int* read, struct readline_handle* rh)
483 {
484 	char* eol;
485 	char* trim;
486 
487 	if (rh->crt>=rh->end){
488 		/* end, nothing more to read */
489 		return -1;
490 	}
491 	for(eol=rh->crt; (eol<rh->end) && (*eol!='\n'); eol++);
492 	*eol=0;
493 	trim=eol;
494 	/* trim spaces at the end */
495 	for(trim=eol;(trim>rh->crt) &&
496 			((*trim=='\r')||(*trim==' ')||(*trim=='\t')); trim--){
497 		*trim=0;
498 	}
499 	*b=rh->crt;
500 	*read = (int)(trim-rh->crt);
501 	rh->crt=eol+1;
502 	return 0;
503 }
504 
505 
506 /*
507  * Remove directory path from filename and replace it
508  * with the path configured through a module parameter.
509  *
510  * The result is allocated using ctl_malloc and thus
511  * has to be freed using ctl_free
512  */
trim_filename(char * file)513 static char *trim_filename(char * file)
514 {
515 	int prefix_len, fn_len;
516 	char *new_fn;
517 
518 	/* we only allow files in "/tmp" -- any directory
519 	 * changes are not welcome
520 	 */
521 	if (strchr(file, '.') || strchr(file, '/')
522 	    || strchr(file, '\\')) {
523 		ERR("Forbidden filename: %s\n"
524 		    , file);
525 		return 0;
526 	}
527 	prefix_len = strlen(fifo_dir); fn_len = strlen(file);
528 	new_fn = ctl_malloc(prefix_len + fn_len + 1);
529 	if (new_fn == 0) {
530 		ERR("No memory left\n");
531 		return 0;
532 	}
533 
534 	memcpy(new_fn, fifo_dir, prefix_len);
535 	memcpy(new_fn + prefix_len, file, fn_len);
536 	new_fn[prefix_len + fn_len] = 0;
537 	return new_fn;
538 }
539 
540 
541 
542 /* reply fifo security checks:
543  * checks if fd is a fifo, is not hardlinked and it's not a softlink
544  * opened file descriptor + file name (for soft link check)
545  * returns 0 if ok, <0 if not
546  */
fifo_check(int fd,char * fname)547 static int fifo_check(int fd, char* fname)
548 {
549 	struct stat fst;
550 	struct stat lst;
551 
552 	if (fstat(fd, &fst) < 0) {
553 		ERR("fstat failed: %s\n",
554 		    strerror(errno));
555 		return -1;
556 	}
557 	     /* check if fifo */
558 	if (!S_ISFIFO(fst.st_mode)){
559 		ERR("%s is not a fifo\n", fname);
560 		return -1;
561 	}
562 	     /* check if hard-linked */
563 	if (fst.st_nlink > 1) {
564 		ERR("%s is hard-linked %d times\n",
565 		    fname, (unsigned)fst.st_nlink);
566 		return -1;
567 	}
568 
569 	     /* lstat to check for soft links */
570 	if (lstat(fname, &lst) < 0) {
571 		ERR("lstat failed: %s\n",
572 		    strerror(errno));
573 		return -1;
574 	}
575 	if (S_ISLNK(lst.st_mode)) {
576 		ERR("%s is a soft link\n", fname);
577 		return -1;
578 	}
579 	     /* if this is not a symbolic link, check to see if the inode didn't
580 	      * change to avoid possible sym.link, rm sym.link & replace w/ fifo race
581 	      */
582 	if ((lst.st_dev != fst.st_dev) || (lst.st_ino != fst.st_ino)) {
583 		ERR("inode/dev number differ : %d %d (%s)\n",
584 		    (int)fst.st_ino, (int)lst.st_ino, fname);
585 		return -1;
586 	}
587 	     /* success */
588 	return 0;
589 }
590 
591 
592 /*
593  * Open the FIFO reply file
594  * returns fs no on success, -1 on error
595  */
open_reply_pipe(char * pipe_name)596 static int open_reply_pipe(char *pipe_name)
597 {
598 
599 	int fifofd;
600 	int flags;
601 
602 	int retries = fifo_reply_retries;
603 
604 	fifofd=-1;
605 	if (!pipe_name || *pipe_name == 0) {
606 		DBG("No file to write to about missing cmd\n");
607 		goto error;
608 	}
609 
610  tryagain:
611 	     /* open non-blocking to make sure that a broken client will not
612 	      * block the FIFO server forever */
613 	fifofd = open(pipe_name, O_WRONLY | O_NONBLOCK);
614 	if (fifofd == -1) {
615 		     /* retry several times if client is not yet ready for getting
616 		      * feedback via a reply pipe
617 		      */
618 		if (errno == ENXIO) {
619 			     /* give up on the client - we can't afford server blocking */
620 			if (retries == 0) {
621 				ERR("No client at %s\n", pipe_name);
622 				goto error;
623 			}
624 			     /* don't be noisy on the very first try */
625 			if (retries != fifo_reply_retries) {
626 				DBG("Retry countdown: %d\n", retries);
627 			}
628 			sleep_us(fifo_reply_wait);
629 			retries--;
630 			goto tryagain;
631 		}
632 		     /* some other opening error */
633 		ERR("Open error (%s): %s\n",
634 		    pipe_name, strerror(errno));
635 		goto error;
636 	}
637 	     /* security checks: is this really a fifo?, is
638 	      * it hardlinked? is it a soft link? */
639 	if (fifo_check(fifofd, pipe_name) < 0) goto error;
640 
641 	     /* we want server blocking for big writes */
642 	if ((flags = fcntl(fifofd, F_GETFL, 0)) < 0) {
643 		ERR("(%s): getfl failed: %s\n", pipe_name, strerror(errno));
644 		goto error;
645 	}
646 	flags &= ~O_NONBLOCK;
647 	if (fcntl(fifofd, F_SETFL, flags) < 0) {
648 		ERR("(%s): setfl cntl failed: %s\n",
649 		    pipe_name, strerror(errno));
650 		goto error;
651 	}
652 
653 	return fifofd;
654  error:
655 	if (fifofd!=-1) close(fifofd);
656 	return -1;
657 }
658 
659 
660 
661 
662 /*
663  * Man FIFO routine running in the FIFO
664  * processes requests received
665  * through the FIFO file repeatedly
666  */
fifo_process(char * msg_buf,int size,int * bytes_needed,void * sh,void ** saved_state)667 int fifo_process(char* msg_buf, int size, int* bytes_needed, void *sh,
668 					void** saved_state)
669 {
670 	rpc_export_t* exp;
671 	char* buf;
672 	int line_len;
673 	char *file_sep;
674 	struct text_chunk* p;
675 	struct rpc_struct* s;
676 	int r;
677 	int req_size;
678 	static rpc_ctx_t context;
679 
680 	DBG("process_fifo: called with %d bytes, offset %d: %.*s\n",
681 			size, (int)(long)*saved_state, size, msg_buf);
682 	/* search for the end of the request (\n\r) */
683 	if (size < 6){ /* min fifo request */
684 		*bytes_needed=6-size;
685 		return 0; /* we want more bytes, nothing processed */
686 	}
687 	for (r=1+(int)(long)*saved_state;r<size;r++){
688 		if ((msg_buf[r]=='\n' || msg_buf[r]=='\r') &&
689 			(msg_buf[r-1]=='\n'|| msg_buf[r-1]=='\r')){
690 			/* found double cr, or double lf => end of request */
691 			req_size=r;
692 			goto process;
693 		}
694 	}
695 	/* no end of request found => ask for more bytes */
696 	*bytes_needed=1;
697 	/* save current offset, to optimize search */
698 	*saved_state=(void*)(long)(r-1);
699 	return 0; /* we want again the whole buffer */
700 process:
701 
702 	DBG("process_fifo  %d bytes request: %.*s\n",
703 			req_size, req_size, msg_buf);
704 	file_sep = 0;
705 	context.method = 0;
706 	context.reply_file = 0;
707 	context.body = 0;
708 	context.code = 200;
709 	context.reason = "OK";
710 	context.reply_sent = 0;
711 	context.last = 0;
712 	context.line_no = 0;
713 	context.read_h.s=msg_buf;
714 	context.read_h.end=msg_buf+size;
715 	context.read_h.crt=msg_buf;
716 	context.send_h=(struct send_handle*)sh;
717 		     /* commands must look this way ':<command>:[filename]' */
718 		if (read_line(&buf, &line_len, &context.read_h) < 0) {
719 			     /* line breaking must have failed -- consume the rest
720 			      * and proceed to a new request
721 			      */
722 			ERR("Command expected\n");
723 			goto consume;
724 		}
725 		context.line_no++;
726 		if (line_len == 0) {
727 			DBG("Empty command received\n");
728 			goto consume;
729 		}
730 		if (line_len < 3) {
731 			ERR("Command must have at least 3 chars\n");
732 			goto consume;
733 		}
734 		if (*buf != CMD_SEPARATOR) {
735 			ERR("Command must begin with %c: %.*s\n",
736 			    CMD_SEPARATOR, line_len, buf);
737 			goto consume;
738 		}
739 
740 		context.method = buf + 1;
741 		file_sep = strchr(context.method, CMD_SEPARATOR);
742 		if (file_sep == NULL) {
743 			ERR("File separator missing\n");
744 			goto consume;
745 		}
746 		if (file_sep == context.method) {
747 			ERR("Empty command\n");
748 			goto consume;
749 		}
750 		if (*(file_sep + 1) == 0) context.reply_file = NULL;
751 		else {
752 			context.reply_file = file_sep + 1;
753 			context.reply_file = trim_filename(context.reply_file);
754 			if (context.reply_file == 0) {
755 				ERR("Trimming filename\n");
756 				goto consume;
757 			}
758 		}
759 		     /* make command zero-terminated */
760 		*file_sep = 0;
761 
762 		exp = find_rpc_export(context.method, 0);
763 		if (!exp || !exp->function) {
764 			DBG("Command %s not found\n", context.method);
765 			rpc_fault(&context, 500, "Command '%s' not found", context.method);
766 			goto consume;
767 		}
768 
769 		exp->function(&func_param, &context);
770 
771 	consume:
772 		if (!context.reply_sent) {
773 			rpc_send(&context);
774 		}
775 
776 		if (context.reply_file) {
777 			ctl_free(context.reply_file);
778 			context.reply_file = 0;
779 		}
780 
781 		     /* Collect garbage (unescaped strings and structures) */
782 		while(context.strs) {
783 			p = context.strs;
784 			context.strs = context.strs->next;
785 			free_chunk(p);
786 		}
787 
788 		while(context.structs) {
789 			s = context.structs;
790 			context.structs = context.structs->next;
791 			free_struct(s);
792 		}
793 
794 		*bytes_needed=0;
795 		DBG("Command consumed\n");
796 		DBG("process_fifo: returning %d, bytes_needed 0\n", req_size+1);
797 		return req_size+1; /* all was processed (including terminating \n)*/
798 
799 }
800 
801 
802 /*
803  * Initialze the FIFO fd
804  * Make sure that we can create and open the FIFO file and
805  * make it secure. This function must be executed from mod_init.
806  * This ensures that it has sufficient privileges.
807  */
init_fifo_fd(char * fifo,int fifo_mode,int fifo_uid,int fifo_gid,int * fifo_write)808 int init_fifo_fd(char* fifo, int fifo_mode, int fifo_uid, int fifo_gid,
809 					int* fifo_write)
810 {
811 	struct stat filestat;
812 	int n;
813 	long opt;
814 	int fifo_read = -1;
815 
816 	if (fifo == NULL) {
817 		ERR("null fifo: no fifo will be opened\n");
818 		/* error null fifo */
819 		return -1;
820 	}
821 	if (strlen(fifo) == 0) {
822 		ERR("empty fifo: fifo disabled\n");
823 		return -1;
824 	}
825 
826 	DBG("Opening fifo...\n");
827 	n = stat(fifo, &filestat);
828 	if (n == 0) {
829 		/* FIFO exist, delete it (safer) */
830 		if (unlink(fifo) < 0) {
831 			ERR("Cannot delete old fifo (%s):"
832 			    " %s\n", fifo, strerror(errno));
833 			return -1;
834 		}
835 	} else if (n < 0 && errno != ENOENT) {
836 		ERR("FIFO stat failed: %s\n",
837 		    strerror(errno));
838 	}
839 	/* create FIFO ... */
840 	if ((mkfifo(fifo, fifo_mode) < 0)) {
841 		ERR("Can't create FIFO: "
842 		    "%s (mode=%d)\n",
843 		    strerror(errno), fifo_mode);
844 		return -1;
845 	}
846 	DBG("FIFO created @ %s\n", fifo );
847 	if ((chmod(fifo, fifo_mode) < 0)) {
848 		ERR("Can't chmod FIFO: %s (mode=%d)\n",
849 		    strerror(errno), fifo_mode);
850 		return -1;
851 	}
852 	if ((fifo_uid != -1) || (fifo_gid != -1)) {
853 		if (chown(fifo, fifo_uid, fifo_gid) < 0) {
854 			ERR("Failed to change the owner/group for %s  to %d.%d; %s[%d]\n",
855 			    fifo, fifo_uid, fifo_gid, strerror(errno), errno);
856 			return -1;
857 		}
858 	}
859 
860 	DBG("fifo %s opened, mode=%d\n", fifo, fifo_mode);
861 
862 	fifo_read = open(fifo, O_RDONLY | O_NONBLOCK, 0);
863 	if (fifo_read < 0) {
864 		ERR("fifo_read did not open: %s\n", strerror(errno));
865 		return -1;
866 	}
867 	/* make sure the read fifo will not close */
868 	*fifo_write = open(fifo, O_WRONLY | O_NONBLOCK, 0);
869 	if (*fifo_write < 0) {
870 		ERR("fifo_write did not open: %s\n", strerror(errno));
871 		close(fifo_read);
872 		return -1;
873 	}
874 	/* set read fifo blocking mode */
875 	if ((opt = fcntl(fifo_read, F_GETFL)) == -1) {
876 		ERR("fcntl(F_GETFL) failed: %s [%d]\n", strerror(errno), errno);
877 		close(fifo_read);
878 		close(*fifo_write);
879 		*fifo_write = -1;
880 		return -1;
881 	}
882 	if (fcntl(fifo_read, F_SETFL, opt & (~O_NONBLOCK)) == -1) {
883 		ERR("fcntl(F_SETFL) failed: %s [%d]\n", strerror(errno), errno);
884 		close(fifo_read);
885 		close(*fifo_write);
886 		*fifo_write = -1;
887 		return -1;
888 	}
889 	return fifo_read;
890 }
891 
892 
893 
fifo_rpc_init()894 int fifo_rpc_init()
895 {
896 	memset(&func_param, 0, sizeof(func_param));
897 	func_param.send = (rpc_send_f)rpc_send;
898 	func_param.fault = (rpc_fault_f)rpc_fault;
899 	func_param.add = (rpc_add_f)rpc_add;
900 	func_param.scan = (rpc_scan_f)rpc_scan;
901 	func_param.rpl_printf = (rpc_rpl_printf_f)rpc_rpl_printf;
902 	func_param.struct_add = (rpc_struct_add_f)rpc_struct_add;
903 	/* use rpc_struct_add for array_add */
904 	func_param.array_add = (rpc_array_add_f)rpc_struct_add;
905 	func_param.struct_scan = (rpc_struct_scan_f)rpc_struct_scan;
906 	func_param.struct_printf = (rpc_struct_printf_f)rpc_struct_printf;
907 	return 0;
908 }
909 
910 
911 
912 /*
913  * Close and unlink the FIFO file
914  */
destroy_fifo(int read_fd,int w_fd,char * fname)915 void destroy_fifo(int read_fd, int w_fd, char* fname)
916 {
917 	if (read_fd!=-1)
918 		close(read_fd);
919 	if(w_fd!=-1)
920 		close(w_fd);
921 	/* if  FIFO was created, delete it */
922 	if (fname && strlen(fname)) {
923 		if (unlink(fname) < 0) {
924 			WARN("Cannot delete fifo (%s):"
925 			     " %s\n", fname, strerror(errno));
926 		}
927 	}
928 }
929 
930 
931 
932 #define REASON_BUF_LEN 1024
933 
934 
935 /*
936  * An error occurred, signal it to the client
937  */
rpc_fault(rpc_ctx_t * ctx,int code,char * fmt,...)938 static void rpc_fault(rpc_ctx_t* ctx, int code, char* fmt, ...)
939 {
940 	static char buf[REASON_BUF_LEN];
941 	va_list ap;
942 	ctx->code = code;
943 	va_start(ap, fmt);
944 	vsnprintf(buf, REASON_BUF_LEN, fmt, ap);
945 	va_end(ap);
946 	ctx->reason = buf;
947 }
948 
949 
safe_write(FILE * f,char * fmt,...)950 static inline int safe_write(FILE* f, char* fmt, ...)
951 {
952 	va_list ap;
953 
954 	if (!*fmt) return 0;
955 	va_start(ap, fmt);
956 
957  retry:
958 	     /* First line containing code and reason phrase */
959 	if (vfprintf(f, fmt, ap) <= 0) {
960 		ERR("fifo write error: %s\n", strerror(errno));
961 		if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK)) {
962 			goto retry;
963 		}
964 		va_end(ap);
965 		return -1;
966 	}
967 	va_end(ap);
968 	return 0;
969 }
970 
971 
972 
build_iovec(rpc_ctx_t * ctx,struct iovec * v,int v_size)973 inline static int build_iovec(rpc_ctx_t* ctx, struct iovec* v, int v_size)
974 {
975 	struct text_chunk* p;
976 	int r_c_len;
977 	int r;
978 
979 
980 
981 	/* reason code */
982 	v[0].iov_base=int2str(ctx->code, &r_c_len);
983 	v[0].iov_len=r_c_len;
984 	v[1].iov_base=" ";
985 	v[1].iov_len=1;
986 	/* reason txt */
987 	v[2].iov_base=ctx->reason;
988 	v[2].iov_len=strlen(ctx->reason);
989 	v[3].iov_base="\n";
990 	v[3].iov_len=1;
991 	r=4;
992 	/* Send the body */
993 	while(ctx->body) {
994 		p = ctx->body;
995 		ctx->body = ctx->body->next;
996 		if (p->s.len){
997 			if (r>=v_size) goto error_overflow;
998 			v[r].iov_base=p->s.s;
999 			v[r].iov_len=p->s.len;
1000 			r++;
1001 		}
1002 		if (p->flags & CHUNK_POSITIONAL) {
1003 			if (r>=v_size) goto error_overflow;
1004 			v[r].iov_base="\n";
1005 			v[r].iov_len=1;
1006 			r++;
1007 		} else if (p->flags & CHUNK_MEMBER_NAME) {
1008 			if (r>=v_size) goto error_overflow;
1009 			v[r].iov_base=":";
1010 			v[r].iov_len=1;
1011 			r++;
1012 		} else if (p->flags & CHUNK_MEMBER_VALUE) {
1013 			if (p->next && p->next->flags & CHUNK_MEMBER_NAME) {
1014 				if (r>=MAX_MSG_CHUNKS) goto error_overflow;
1015 				v[r].iov_base=",";
1016 				v[r].iov_len=1;
1017 				r++;
1018 			} else {
1019 				if (r>=v_size) goto error_overflow;
1020 				v[r].iov_base="\n";
1021 				v[r].iov_len=1;
1022 				r++;
1023 			}
1024 		}
1025 		free_chunk(p);
1026 	}
1027 	return r;
1028 error_overflow:
1029 	ERR("too many message chunks, iovec buffer overflow: %d/%d\n", r,
1030 			MAX_MSG_CHUNKS);
1031 	return -1;
1032 }
1033 
1034 
1035 
1036 /*
1037  * Send a reply, either positive or negative, to the client
1038  */
rpc_send(rpc_ctx_t * ctx)1039 static int rpc_send(rpc_ctx_t* ctx)
1040 {
1041 	struct iovec v[MAX_MSG_CHUNKS];
1042 	int f;
1043 	int n;
1044 	int ret;
1045 	/* Send the reply only once */
1046 	if (ctx->reply_sent) return 1;
1047 	else ctx->reply_sent = 1;
1048 
1049 	if ((n=build_iovec(ctx, v, MAX_MSG_CHUNKS))<0)
1050 		goto error;
1051 	if (ctx->send_h->type==S_FIFO){
1052 	/* Open the reply file */
1053 		f = open_reply_pipe(ctx->reply_file);
1054 		if (f == -1) {
1055 			ERR("No reply pipe %s\n", ctx->reply_file);
1056 			return -1;
1057 		}
1058 		ret=tsend_dgram_ev(f, v, n, FIFO_TX_TIMEOUT);
1059 		close(f);
1060 	}else{
1061 		ret=sock_send_v(ctx->send_h, v, n);
1062 	}
1063 	return (ret>=0)?0:-1;
1064 error:
1065 	ERR("rpc_send fifo error\n");
1066 	return -1;
1067 }
1068 
1069 
1070 
1071 /*
1072  * Add a chunk to reply
1073  */
append_chunk(rpc_ctx_t * ctx,struct text_chunk * l)1074 static void append_chunk(rpc_ctx_t* ctx, struct text_chunk* l)
1075 {
1076 	if (!ctx->last) {
1077 		ctx->body = l;
1078 		ctx->last = l;
1079 	} else {
1080 		ctx->last->next = l;
1081 		ctx->last = l;
1082 	}
1083 }
1084 
1085 
1086 /*
1087  * Convert a value to data chunk and add it to reply
1088  */
print_value(rpc_ctx_t * ctx,char fmt,va_list * ap)1089 static int print_value(rpc_ctx_t* ctx, char fmt, va_list* ap)
1090 {
1091 	struct text_chunk* l;
1092 	str str_val;
1093 	str* sp;
1094 	char buf[256];
1095 	str null_value = str_init("<null string>");
1096 
1097 	switch(fmt) {
1098 	case 'd':
1099 	case 't':
1100 		str_val.s = int2str(va_arg(*ap, int), &str_val.len);
1101 		l = new_chunk(&str_val);
1102 		if (!l) {
1103 			rpc_fault(ctx, 500, "Internal server error while processing"
1104 					" line %d", ctx->line_no);
1105 			goto err;
1106 		}
1107 		break;
1108 
1109 	case 'f':
1110 		str_val.s = buf;
1111 		str_val.len = snprintf(buf, 256, "%f", va_arg(*ap, double));
1112 		if (str_val.len < 0) {
1113 			rpc_fault(ctx, 400, "Error While Converting double");
1114 			ERR("Error while converting double\n");
1115 			goto err;
1116 		}
1117 		l = new_chunk(&str_val);
1118 		if (!l) {
1119 			rpc_fault(ctx, 500, "Internal Server Error, line %d",
1120 						ctx->line_no);
1121 			goto err;
1122 		}
1123 		break;
1124 
1125 	case 'b':
1126 		str_val.len = 1;
1127 		str_val.s = ((va_arg(*ap, int) == 0) ? "0" : "1");
1128 		l = new_chunk(&str_val);
1129 		if (!l) {
1130 			rpc_fault(ctx, 500, "Internal Server Error, line %d",
1131 						ctx->line_no);
1132 			goto err;
1133 		}
1134 		break;
1135 
1136 	case 's':
1137 		str_val.s = va_arg(*ap, char*);
1138 		if(str_val.s!=NULL) {
1139 			str_val.len = strlen(str_val.s);
1140 		} else {
1141 			str_val = null_value;
1142 		}
1143 		l = new_chunk_escape(&str_val, 0);
1144 		if (!l) {
1145 			rpc_fault(ctx, 500, "Internal Server Error, line %d",
1146 						ctx->line_no);
1147 			goto err;
1148 		}
1149 		break;
1150 
1151 	case 'S':
1152 		sp = va_arg(*ap, str*);
1153 		if(sp!=NULL && sp->s!=NULL) {
1154 			str_val = *sp;
1155 		} else {
1156 			str_val = null_value;
1157 		}
1158 		l = new_chunk_escape(&str_val, 0);
1159 		if (!l) {
1160 			rpc_fault(ctx, 500, "Internal Server Error, line %d",
1161 							ctx->line_no);
1162 			goto err;
1163 		}
1164 		break;
1165 
1166 	default:
1167 		rpc_fault(ctx, 500, "Bug In SER (Invalid formatting character %c)", fmt);
1168 		ERR("Invalid formatting character\n");
1169 		goto err;
1170 	}
1171 
1172 	l->flags |= CHUNK_POSITIONAL;
1173 	append_chunk(ctx, l);
1174 	return 0;
1175  err:
1176 	return -1;
1177 }
1178 
1179 
rpc_add(rpc_ctx_t * ctx,char * fmt,...)1180 static int rpc_add(rpc_ctx_t* ctx, char* fmt, ...)
1181 {
1182 	void** void_ptr;
1183 	va_list ap;
1184 	str s = {"", 0};
1185 	struct text_chunk* l;
1186 
1187 	va_start(ap, fmt);
1188 	while(*fmt) {
1189 		if (*fmt == '{' || *fmt == '[') {
1190 			void_ptr = va_arg(ap, void**);
1191 			l = new_chunk(&s);
1192 			if (!l) {
1193 				rpc_fault(ctx, 500, "Internal Server Error");
1194 				goto err;
1195 			}
1196 			l->ctx=ctx;
1197 			append_chunk(ctx, l);
1198 			*void_ptr = l;
1199 		} else {
1200 			if (print_value(ctx, *fmt, &ap) < 0) goto err;
1201 		}
1202 		fmt++;
1203 	}
1204 	va_end(ap);
1205 	return 0;
1206  err:
1207 	va_end(ap);
1208 	return -1;
1209 }
1210 
1211 #define RPC_BUF_SIZE 1024
1212 
rpc_struct_printf(struct text_chunk * c,char * name,char * fmt,...)1213 static int rpc_struct_printf(struct text_chunk* c, char* name, char* fmt, ...)
1214 {
1215 	int n, buf_size;
1216 	char* buf;
1217 	char* buf0;
1218 	va_list ap;
1219 	str s, nm;
1220 	struct text_chunk* l, *m;
1221 	rpc_ctx_t* ctx;
1222 
1223 	ctx=(rpc_ctx_t*)c->ctx;
1224 	buf = (char*)ctl_malloc(RPC_BUF_SIZE);
1225 	if (!buf) {
1226 		rpc_fault(ctx,  500, "Internal Server Error (No memory left)");
1227 		ERR("No memory left\n");
1228 		return -1;
1229 	}
1230 
1231 	buf_size = RPC_BUF_SIZE;
1232 	while (1) {
1233 		     /* Try to print in the allocated space. */
1234 		va_start(ap, fmt);
1235 		n = vsnprintf(buf, buf_size, fmt, ap);
1236 		va_end(ap);
1237 		     /* If that worked, return the string. */
1238 		if (n > -1 && n < buf_size) {
1239 			nm.s = name;
1240 			nm.len = strlen(name);
1241 			m = new_chunk_escape(&nm, 1); /* Escape all characters, including : and , */
1242 			if (!m) {
1243 				rpc_fault(ctx, 500, "Internal Server Error");
1244 				goto err;
1245 			}
1246 
1247 			s.s = buf;
1248 			s.len = n;
1249 			l = new_chunk_escape(&s, 1);
1250 			if (!l) {
1251 				rpc_fault(ctx, 500, "Internal Server Error");
1252 				free_chunk(m);
1253 				ERR("Error while creating text_chunk structure");
1254 				goto err;
1255 			}
1256 
1257 			l->flags |= CHUNK_MEMBER_VALUE;
1258 			l->next = c->next;
1259 			c->next = l;
1260 			if (c == ctx->last) ctx->last = l;
1261 
1262 			m->flags |= CHUNK_MEMBER_NAME;
1263 			m->next = c->next;
1264 			c->next = m;
1265 			if (c == ctx->last) ctx->last = m;
1266 			return 0;
1267 		}
1268 		     /* Else try again with more space. */
1269 		if (n > -1) {   /* glibc 2.1 */
1270 			buf_size = n + 1; /* precisely what is needed */
1271 		} else {          /* glibc 2.0 */
1272 			buf_size *= 2;  /* twice the old size */
1273 		}
1274 		if ((buf0 = ctl_realloc(buf, buf_size)) == 0) {
1275 			rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
1276 			ERR("No memory left\n");
1277 			goto err;
1278 		}
1279 		buf = buf0;
1280 	}
1281 	return 0;
1282  err:
1283 	if (buf) ctl_free(buf);
1284 	return -1;
1285 }
1286 
1287 
rpc_rpl_printf(rpc_ctx_t * ctx,char * fmt,...)1288 static int rpc_rpl_printf(rpc_ctx_t* ctx, char* fmt, ...)
1289 {
1290 	int n, buf_size;
1291 	char* buf;
1292 	char* buf0;
1293 	va_list ap;
1294 	str s;
1295 	struct text_chunk* l;
1296 
1297 	buf = (char*)ctl_malloc(RPC_BUF_SIZE);
1298 	if (!buf) {
1299 		rpc_fault(ctx,  500, "Internal Server Error (No memory left)");
1300 		ERR("No memory left\n");
1301 		return -1;
1302 	}
1303 
1304 	buf_size = RPC_BUF_SIZE;
1305 	while (1) {
1306 		     /* Try to print in the allocated space. */
1307 		va_start(ap, fmt);
1308 		n = vsnprintf(buf, buf_size, fmt, ap);
1309 		va_end(ap);
1310 		     /* If that worked, return the string. */
1311 		if (n > -1 && n < buf_size) {
1312 			s.s = buf;
1313 			s.len = n;
1314 			l = new_chunk_escape(&s, 0);
1315 			if (!l) {
1316 				rpc_fault(ctx, 500, "Internal Server Error");
1317 				ERR("Error while creating text_chunk structure");
1318 				goto err;
1319 			}
1320 			append_chunk(ctx, l);
1321 			ctl_free(buf);
1322 			return 0;
1323 		}
1324 		     /* Else try again with more space. */
1325 		if (n > -1) {   /* glibc 2.1 */
1326 			buf_size = n + 1; /* precisely what is needed */
1327 		} else {          /* glibc 2.0 */
1328 			buf_size *= 2;  /* twice the old size */
1329 		}
1330 		if ((buf0 = ctl_realloc(buf, buf_size)) == 0) {
1331 			rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
1332 			ERR("No memory left\n");
1333 			goto err;
1334 		}
1335 		buf = buf0;
1336 	}
1337 	return 0;
1338  err:
1339 	if (buf) ctl_free(buf);
1340 	return -1;
1341 }
1342 
1343 
rpc_scan(rpc_ctx_t * ctx,char * fmt,...)1344 static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...)
1345 {
1346 	struct text_chunk* l;
1347 	struct rpc_struct* s;
1348 	int* int_ptr;
1349 	char** char_ptr;
1350 	str* str_ptr;
1351 	double* double_ptr;
1352 	void** void_ptr;
1353 	int read;
1354 	str line;
1355 	int nofault;
1356 	int modifiers;
1357 
1358 	va_list ap;
1359 	va_start(ap, fmt);
1360 
1361 	nofault = 0;
1362 	read = 0;
1363 	modifiers=0;
1364 	while(*fmt) {
1365 		if (read_line(&line.s, &line.len, &ctx->read_h) < 0) {
1366 			va_end(ap);
1367 			return read;
1368 		}
1369 		ctx->line_no++;
1370 
1371 		switch(*fmt) {
1372 		case '*': /* start of optional parameters */
1373 			nofault = 1;
1374 			modifiers++;
1375 			break;
1376 		case 'b': /* Bool */
1377 		case 't': /* Date and time */
1378 		case 'd': /* Integer */
1379 			if (!line.len) {
1380 				if(nofault==0)
1381 					rpc_fault(ctx, 400, "Invalid parameter value on line %d",
1382 									ctx->line_no);
1383 				goto error;
1384 			}
1385 			int_ptr = va_arg(ap, int*);
1386 			*int_ptr = strtol(line.s, 0, 0);
1387 			break;
1388 
1389 		case 'f': /* double */
1390 			if (!line.len) {
1391 				if(nofault==0)
1392 					rpc_fault(ctx, 400, "Invalid parameter value on line %d",
1393 								ctx->line_no);
1394 				goto error;
1395 			}
1396 			double_ptr = va_arg(ap, double*);
1397 			*double_ptr = strtod(line.s, 0);
1398 			break;
1399 
1400 		case 's': /* zero terminated string */
1401 		case 'S': /* str structure */
1402 			l = new_chunk_unescape(&line);
1403 			if (!l) {
1404 				if(nofault==0) {
1405 					rpc_fault(ctx, 500, "Internal Server Error");
1406 					ERR("Not enough memory\n");
1407 				}
1408 				goto error;
1409 			}
1410 			     /* Make sure it gets released at the end */
1411 			l->next = ctx->strs;
1412 			ctx->strs = l;
1413 
1414 			if (*fmt == 's') {
1415 				char_ptr = va_arg(ap, char**);
1416 				*char_ptr = l->s.s;
1417 			} else {
1418 				str_ptr = va_arg(ap, str*);
1419 				*str_ptr = l->s;
1420 			}
1421 			break;
1422 
1423 		case '{':
1424 			void_ptr = va_arg(ap, void**);
1425 			s = new_struct(ctx, &line);
1426 			if (!s) goto error;
1427 			s->next = ctx->structs;
1428 			ctx->structs = s;
1429 			*void_ptr = s;
1430 			break;
1431 
1432 		default:
1433 			ERR("Invalid parameter type in formatting string: %c\n", *fmt);
1434 			rpc_fault(ctx, 500, "Server Internal Error (Invalid Formatting Character '%c')", *fmt);
1435 			goto error;
1436 		}
1437 		fmt++;
1438 		read++;
1439 	}
1440 	va_end(ap);
1441 	return read-modifiers;
1442 
1443  error:
1444 	va_end(ap);
1445 	return -(read-modifiers);
1446 }
1447 
1448 
rpc_struct_add(struct text_chunk * s,char * fmt,...)1449 static int rpc_struct_add(struct text_chunk* s, char* fmt, ...)
1450 {
1451 	static char buf[MAX_LINE_BUFFER];
1452 	str st, *sp;
1453 	void** void_ptr;
1454 	va_list ap;
1455 	struct text_chunk* m, *c;
1456 	rpc_ctx_t* ctx;
1457 	str null_value = str_init("<null string>");
1458 
1459 	ctx=(rpc_ctx_t*)s->ctx;
1460 	va_start(ap, fmt);
1461 	while(*fmt) {
1462 		     /* Member name escaped */
1463 		st.s = va_arg(ap, char*);
1464 		st.len = strlen(st.s);
1465 		m = new_chunk_escape(&st, 1); /* Escape all characters, including : and , */
1466 		if (!m) {
1467 			rpc_fault(ctx, 500, "Internal Server Error");
1468 			goto err;
1469 		}
1470 		m->flags |= CHUNK_MEMBER_NAME;
1471 
1472 		if(*fmt=='{' || *fmt=='[') {
1473 			void_ptr = va_arg(ap, void**);
1474 			m->ctx=ctx;
1475 			append_chunk(ctx, m);
1476 			*void_ptr = m;
1477 		} else {
1478 			switch(*fmt) {
1479 			case 'd':
1480 			case 't':
1481 				st.s = int2str(va_arg(ap, int), &st.len);
1482 				c = new_chunk(&st);
1483 				break;
1484 
1485 			case 'f':
1486 				st.s = buf;
1487 				st.len = snprintf(buf, 256, "%f", va_arg(ap, double));
1488 				if (st.len < 0) {
1489 					rpc_fault(ctx, 400, "Error While Converting double");
1490 					ERR("Error while converting double\n");
1491 					goto err;
1492 				}
1493 				c = new_chunk(&st);
1494 				break;
1495 
1496 				case 'b':
1497 					st.len = 1;
1498 					st.s = ((va_arg(ap, int) == 0) ? "0" : "1");
1499 					c = new_chunk(&st);
1500 				break;
1501 
1502 			case 's':
1503 				st.s = va_arg(ap, char*);
1504 				if(st.s==NULL) {
1505 					st = null_value;
1506 				} else {
1507 					st.len = strlen(st.s);
1508 				}
1509 				c = new_chunk_escape(&st, 1);
1510 				break;
1511 
1512 			case 'S':
1513 				sp = va_arg(ap, str*);
1514 				if(sp!=NULL && sp->s!=NULL) {
1515 					st = *sp;
1516 				} else {
1517 					st = null_value;
1518 				}
1519 				c = new_chunk_escape(&st, 1);
1520 				break;
1521 
1522 			default:
1523 				rpc_fault(ctx, 500, "Bug In SER (Invalid formatting character %c)",
1524 						*fmt);
1525 				ERR("Invalid formatting character\n");
1526 				goto err;
1527 			}
1528 
1529 			if (!c) {
1530 				rpc_fault(ctx, 500, "Internal Server Error");
1531 				goto err;
1532 			}
1533 			c->flags |= CHUNK_MEMBER_VALUE;
1534 			c->next = s->next;
1535 			s->next = c;
1536 			if (s == ctx->last) ctx->last = c;
1537 
1538 			m->next = s->next;
1539 			s->next = m;
1540 			if (s == ctx->last) ctx->last = m;
1541 		}
1542 		fmt++;
1543 	}
1544 	va_end(ap);
1545 	return 0;
1546  err:
1547 	if (m) free_chunk(m);
1548 	va_end(ap);
1549 	return -1;
1550 }
1551 
1552 
find_member(struct text_chunk ** value,struct rpc_struct * s,str * member_name)1553 static int find_member(struct text_chunk** value, struct rpc_struct* s, str* member_name)
1554 {
1555 	struct text_chunk* n, *v;
1556 
1557 	n = s->names;
1558 	v = s->values;
1559 	while(n) {
1560 		if (member_name->len == n->s.len &&
1561 		    !strncasecmp(member_name->s, n->s.s, n->s.len)) {
1562 			if (n->flags & CHUNK_SEEN) goto skip;
1563 			else {
1564 				*value = v;
1565 				n->flags |= CHUNK_SEEN;
1566 				return 0;
1567 			}
1568 		}
1569 
1570 	skip:
1571 		n = n->next;
1572 		v = v->next;
1573 	}
1574 	return 1;
1575 }
1576 
1577 
rpc_struct_scan(struct rpc_struct * s,char * fmt,...)1578 static int rpc_struct_scan(struct rpc_struct* s, char* fmt, ...)
1579 {
1580 	struct text_chunk* val;
1581 	va_list ap;
1582 	int* int_ptr;
1583 	double* double_ptr;
1584 	char** char_ptr;
1585 	str* str_ptr;
1586 	str member_name;
1587 	int ret, read;
1588 
1589 	read = 0;
1590 	va_start(ap, fmt);
1591 	while(*fmt) {
1592 		member_name.s = va_arg(ap, char*);
1593 		member_name.len = strlen(member_name.s);
1594 		ret = find_member(&val, s, &member_name);
1595 		if (ret > 0) {
1596 			va_end(ap);
1597 			return read;
1598 		}
1599 
1600 		switch(*fmt) {
1601 		case 'b': /* Bool */
1602 		case 't': /* Date and time */
1603 		case 'd': /* Integer */
1604 			int_ptr = va_arg(ap, int*);
1605 			if (!val->s.len) {
1606 				rpc_fault(s->ctx, 400, "Invalid Parameter Value");
1607 				goto error;
1608 			}
1609 			     /* String in text_chunk is always zero terminated */
1610 			*int_ptr = strtol(val->s.s, 0, 0);
1611 			break;
1612 
1613 		case 'f': /* double */
1614 			double_ptr = va_arg(ap, double*);
1615 			if (!val->s.len) {
1616 				rpc_fault(s->ctx, 400, "Invalid Parameter Value");
1617 				goto error;
1618 			}
1619 			     /* String in text_chunk is always zero terminated */
1620 			*double_ptr = strtod(val->s.s, 0);
1621 			break;
1622 
1623 		case 's': /* zero terminated string */
1624 			char_ptr = va_arg(ap, char**);
1625 			     /* String in text_chunk is always zero terminated */
1626 			*char_ptr = val->s.s;
1627 			break;
1628 
1629 		case 'S': /* str structure */
1630 			str_ptr = va_arg(ap, str*);
1631 			*str_ptr = val->s;
1632 			break;
1633 		default:
1634 			rpc_fault(s->ctx, 500, "Invalid character in formatting string '%c'", *fmt);
1635 			ERR("Invalid parameter type in formatting string: %c\n", *fmt);
1636 			goto error;
1637 		}
1638 		fmt++;
1639 		read++;
1640 	}
1641 	va_end(ap);
1642 	return read;
1643  error:
1644 	va_end(ap);
1645 	return -read;
1646 }
1647 
1648 #endif
1649