Added SILC Server library.
[silc.git] / lib / silcutil / silcbuffmt.c
1 /*
2
3   silcbuffmt.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2005 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22
23 /* Macros to check whether there is enough free space to add the
24    required amount of data. For unformatting this means that there must
25    be the data that is to be extracted. */
26 #define FORMAT_HAS_SPACE(__x__, __req__)        \
27   do {                                          \
28     if (__req__ > silc_buffer_len((__x__)))     \
29       goto fail;                                \
30   } while(0)
31 #define UNFORMAT_HAS_SPACE(__x__, __req__)      \
32   do {                                          \
33     if (__req__ > silc_buffer_len((__x__)))     \
34       goto fail;                                \
35     if ((__req__ + 1) <= 0)                     \
36       goto fail;                                \
37   } while(0)
38
39 /* Formats the arguments sent and puts them into the buffer sent as
40    argument. The buffer must be initialized beforehand and it must have
41    enough free space to include the formatted data. If this function
42    fails caller should not trust the buffer anymore and should free it.
43    This function is used, for example, to create packets to send over
44    network. */
45
46 int silc_buffer_format(SilcBuffer dst, ...)
47 {
48   va_list ap;
49   int ret;
50
51   va_start(ap, dst);
52   ret = silc_buffer_format_vp(dst, ap);
53   va_end(ap);
54
55   return ret;
56 }
57
58 int silc_buffer_format_vp(SilcBuffer dst, va_list ap)
59 {
60   SilcBufferParamType fmt;
61   unsigned char *start_ptr = dst->data;
62   int len;
63
64   /* Parse the arguments by formatting type. */
65   while (1) {
66     fmt = va_arg(ap, SilcBufferParamType);
67
68     switch(fmt) {
69     case SILC_BUFFER_PARAM_OFFSET:
70       {
71         int offst = va_arg(ap, int);
72         if (!offst)
73           break;
74         if (offst > 1) {
75           FORMAT_HAS_SPACE(dst, offst);
76           silc_buffer_pull(dst, offst);
77         } else {
78           silc_buffer_push(dst, -(offst));
79         }
80         break;
81       }
82     case SILC_BUFFER_PARAM_SI8_CHAR:
83       {
84         char x = (char)va_arg(ap, int);
85         FORMAT_HAS_SPACE(dst, 1);
86         silc_buffer_put(dst, &x, 1);
87         silc_buffer_pull(dst, 1);
88         break;
89       }
90     case SILC_BUFFER_PARAM_UI8_CHAR:
91       {
92         unsigned char x = (unsigned char)va_arg(ap, int);
93         FORMAT_HAS_SPACE(dst, 1);
94         silc_buffer_put(dst, &x, 1);
95         silc_buffer_pull(dst, 1);
96         break;
97       }
98     case SILC_BUFFER_PARAM_SI16_SHORT:
99       {
100         unsigned char xf[2];
101         SilcInt16 x = (SilcInt16)va_arg(ap, int);
102         FORMAT_HAS_SPACE(dst, 2);
103         SILC_PUT16_MSB(x, xf);
104         silc_buffer_put(dst, xf, 2);
105         silc_buffer_pull(dst, 2);
106         break;
107       }
108     case SILC_BUFFER_PARAM_UI16_SHORT:
109       {
110         unsigned char xf[2];
111         SilcUInt16 x = (SilcUInt16)va_arg(ap, int);
112         FORMAT_HAS_SPACE(dst, 2);
113         SILC_PUT16_MSB(x, xf);
114         silc_buffer_put(dst, xf, 2);
115         silc_buffer_pull(dst, 2);
116         break;
117       }
118     case SILC_BUFFER_PARAM_SI32_INT:
119       {
120         unsigned char xf[4];
121         SilcInt32 x = va_arg(ap, SilcInt32);
122         FORMAT_HAS_SPACE(dst, 4);
123         SILC_PUT32_MSB(x, xf);
124         silc_buffer_put(dst, xf, 4);
125         silc_buffer_pull(dst, 4);
126         break;
127       }
128     case SILC_BUFFER_PARAM_UI32_INT:
129       {
130         unsigned char xf[4];
131         SilcUInt32 x = va_arg(ap, SilcUInt32);
132         FORMAT_HAS_SPACE(dst, 4);
133         SILC_PUT32_MSB(x, xf);
134         silc_buffer_put(dst, xf, 4);
135         silc_buffer_pull(dst, 4);
136         break;
137       }
138     case SILC_BUFFER_PARAM_SI64_INT:
139       {
140         unsigned char xf[8];
141         SilcInt64 x = va_arg(ap, SilcInt64);
142         FORMAT_HAS_SPACE(dst, sizeof(SilcInt64));
143         SILC_PUT64_MSB(x, xf);
144         silc_buffer_put(dst, xf, sizeof(SilcInt64));
145         silc_buffer_pull(dst, sizeof(SilcInt64));
146         break;
147       }
148     case SILC_BUFFER_PARAM_UI64_INT:
149       {
150         unsigned char xf[8];
151         SilcUInt64 x = va_arg(ap, SilcUInt64);
152         FORMAT_HAS_SPACE(dst, sizeof(SilcUInt64));
153         SILC_PUT64_MSB(x, xf);
154         silc_buffer_put(dst, xf, sizeof(SilcUInt64));
155         silc_buffer_pull(dst, sizeof(SilcUInt64));
156         break;
157       }
158     case SILC_BUFFER_PARAM_UI8_STRING:
159     case SILC_BUFFER_PARAM_UI16_STRING:
160     case SILC_BUFFER_PARAM_UI32_STRING:
161     case SILC_BUFFER_PARAM_UI8_STRING_ALLOC:
162     case SILC_BUFFER_PARAM_UI16_STRING_ALLOC:
163     case SILC_BUFFER_PARAM_UI32_STRING_ALLOC:
164       {
165         unsigned char *x = va_arg(ap, unsigned char *);
166         SilcUInt32 tmp_len = strlen(x);
167         FORMAT_HAS_SPACE(dst, tmp_len);
168         silc_buffer_put(dst, x, tmp_len);
169         silc_buffer_pull(dst, tmp_len);
170         break;
171       }
172     case SILC_BUFFER_PARAM_UI8_NSTRING:
173     case SILC_BUFFER_PARAM_UI16_NSTRING:
174     case SILC_BUFFER_PARAM_UI32_NSTRING:
175     case SILC_BUFFER_PARAM_UI_XNSTRING:
176     case SILC_BUFFER_PARAM_UI8_NSTRING_ALLOC:
177     case SILC_BUFFER_PARAM_UI16_NSTRING_ALLOC:
178     case SILC_BUFFER_PARAM_UI32_NSTRING_ALLOC:
179     case SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC:
180       {
181         unsigned char *x = va_arg(ap, unsigned char *);
182         SilcUInt32 len = va_arg(ap, SilcUInt32);
183         if (x && len) {
184           FORMAT_HAS_SPACE(dst, len);
185           silc_buffer_put(dst, x, len);
186           silc_buffer_pull(dst, len);
187         }
188         break;
189       }
190     case SILC_BUFFER_PARAM_END:
191       goto ok;
192       break;
193     default:
194       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
195                       "format the data.", fmt));
196       goto fail;
197       break;
198     }
199   }
200
201  fail:
202   SILC_LOG_DEBUG(("Error occured while formatting data"));
203   len = dst->data - start_ptr;
204   silc_buffer_push(dst, len);
205   return -1;
206
207  ok:
208   /* Push the buffer back to where it belongs. */
209   len = dst->data - start_ptr;
210   silc_buffer_push(dst, len);
211   return len;
212 }
213
214 /* Unformats the buffer sent as argument. The unformatted data is returned
215    to the variable argument list of pointers. The buffer must point to the
216    start of the data area to be unformatted. Buffer maybe be safely free'd
217    after this returns succesfully. */
218
219 int silc_buffer_unformat(SilcBuffer src, ...)
220 {
221   va_list ap;
222   int ret;
223
224   va_start(ap, src);
225   ret = silc_buffer_unformat_vp(src, ap);
226   va_end(ap);
227
228   return ret;
229 }
230
231 int silc_buffer_unformat_vp(SilcBuffer src, va_list ap)
232 {
233   SilcBufferParamType fmt;
234   unsigned char *start_ptr = src->data;
235   int len = 0;
236
237   /* Parse the arguments by formatting type. */
238   while(1) {
239     fmt = va_arg(ap, SilcBufferParamType);
240
241     switch(fmt) {
242     case SILC_BUFFER_PARAM_OFFSET:
243       {
244         int offst = va_arg(ap, int);
245         if (!offst)
246           break;
247         if (offst > 1) {
248           UNFORMAT_HAS_SPACE(src, offst);
249           silc_buffer_pull(src, offst);
250         } else {
251           silc_buffer_push(src, -(offst));
252         }
253         break;
254       }
255     case SILC_BUFFER_PARAM_SI8_CHAR:
256       {
257         char *x = va_arg(ap, char *);
258         UNFORMAT_HAS_SPACE(src, 1);
259         if (x)
260           *x = src->data[0];
261         silc_buffer_pull(src, 1);
262         break;
263       }
264     case SILC_BUFFER_PARAM_UI8_CHAR:
265       {
266         unsigned char *x = va_arg(ap, unsigned char *);
267         UNFORMAT_HAS_SPACE(src, 1);
268         if (x)
269           *x = src->data[0];
270         silc_buffer_pull(src, 1);
271         break;
272       }
273     case SILC_BUFFER_PARAM_SI16_SHORT:
274       {
275         SilcInt16 *x = va_arg(ap, SilcInt16 *);
276         UNFORMAT_HAS_SPACE(src, 2);
277         if (x)
278           SILC_GET16_MSB(*x, src->data);
279         silc_buffer_pull(src, 2);
280         break;
281       }
282     case SILC_BUFFER_PARAM_UI16_SHORT:
283       {
284         SilcUInt16 *x = va_arg(ap, SilcUInt16 *);
285         UNFORMAT_HAS_SPACE(src, 2);
286         if (x)
287           SILC_GET16_MSB(*x, src->data);
288         silc_buffer_pull(src, 2);
289         break;
290       }
291     case SILC_BUFFER_PARAM_SI32_INT:
292       {
293         SilcInt32 *x = va_arg(ap, SilcInt32 *);
294         UNFORMAT_HAS_SPACE(src, 4);
295         if (x)
296           SILC_GET32_MSB(*x, src->data);
297         silc_buffer_pull(src, 4);
298         break;
299       }
300     case SILC_BUFFER_PARAM_UI32_INT:
301       {
302         SilcUInt32 *x = va_arg(ap, SilcUInt32 *);
303         UNFORMAT_HAS_SPACE(src, 4);
304         if (x)
305           SILC_GET32_MSB(*x, src->data);
306         silc_buffer_pull(src, 4);
307         break;
308       }
309     case SILC_BUFFER_PARAM_SI64_INT:
310       {
311         SilcInt64 *x = va_arg(ap, SilcInt64 *);
312         UNFORMAT_HAS_SPACE(src, sizeof(SilcInt64));
313         if (x)
314           SILC_GET64_MSB(*x, src->data);
315         silc_buffer_pull(src, sizeof(SilcInt64));
316         break;
317       }
318     case SILC_BUFFER_PARAM_UI64_INT:
319       {
320         SilcUInt64 *x = va_arg(ap, SilcUInt64 *);
321         UNFORMAT_HAS_SPACE(src, sizeof(SilcUInt64));
322         if (x)
323           SILC_GET64_MSB(*x, src->data);
324         silc_buffer_pull(src, sizeof(SilcUInt64));
325         break;
326       }
327     case SILC_BUFFER_PARAM_UI8_STRING:
328       {
329         SilcUInt8 len2;
330         unsigned char **x = va_arg(ap, unsigned char **);
331         UNFORMAT_HAS_SPACE(src, 1);
332         len2 = (SilcUInt8)src->data[0];
333         silc_buffer_pull(src, 1);
334         UNFORMAT_HAS_SPACE(src, len2);
335         if (x)
336           *x = src->data;
337         silc_buffer_pull(src, len2);
338         break;
339       }
340     case SILC_BUFFER_PARAM_UI16_STRING:
341       {
342         SilcUInt16 len2;
343         unsigned char **x = va_arg(ap, unsigned char **);
344         UNFORMAT_HAS_SPACE(src, 2);
345         SILC_GET16_MSB(len2, src->data);
346         silc_buffer_pull(src, 2);
347         UNFORMAT_HAS_SPACE(src, len2);
348         if (x)
349           *x = src->data;
350         silc_buffer_pull(src, len2);
351         break;
352       }
353     case SILC_BUFFER_PARAM_UI8_STRING_ALLOC:
354       {
355         SilcUInt8 len2;
356         unsigned char **x = va_arg(ap, unsigned char **);
357         UNFORMAT_HAS_SPACE(src, 1);
358         len2 = (SilcUInt8)src->data[0];
359         silc_buffer_pull(src, 1);
360         UNFORMAT_HAS_SPACE(src, len2);
361         if (x && len2) {
362           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
363           memcpy(*x, src->data, len2);
364         }
365         silc_buffer_pull(src, len2);
366         break;
367       }
368     case SILC_BUFFER_PARAM_UI16_STRING_ALLOC:
369       {
370         SilcUInt16 len2;
371         unsigned char **x = va_arg(ap, unsigned char **);
372         UNFORMAT_HAS_SPACE(src, 2);
373         SILC_GET16_MSB(len2, src->data);
374         silc_buffer_pull(src, 2);
375         UNFORMAT_HAS_SPACE(src, len2);
376         if (x && len2) {
377           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
378           memcpy(*x, src->data, len2);
379         }
380         silc_buffer_pull(src, len2);
381         break;
382       }
383     case SILC_BUFFER_PARAM_UI32_STRING:
384       {
385         SilcUInt32 len2;
386         unsigned char **x = va_arg(ap, unsigned char **);
387         UNFORMAT_HAS_SPACE(src, 4);
388         SILC_GET32_MSB(len2, src->data);
389         silc_buffer_pull(src, 4);
390         UNFORMAT_HAS_SPACE(src, len2);
391         if (x)
392           *x = src->data;
393         silc_buffer_pull(src, len2);
394         break;
395       }
396     case SILC_BUFFER_PARAM_UI32_STRING_ALLOC:
397       {
398         SilcUInt32 len2;
399         unsigned char **x = va_arg(ap, unsigned char **);
400         UNFORMAT_HAS_SPACE(src, 4);
401         SILC_GET32_MSB(len2, src->data);
402         silc_buffer_pull(src, 4);
403         UNFORMAT_HAS_SPACE(src, len2);
404         if (x && len2) {
405           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
406           memcpy(*x, src->data, len2);
407         }
408         silc_buffer_pull(src, len2);
409         break;
410       }
411     case SILC_BUFFER_PARAM_UI8_NSTRING:
412       {
413         SilcUInt8 len2;
414         unsigned char **x = va_arg(ap, unsigned char **);
415         SilcUInt8 *len = va_arg(ap, SilcUInt8 *);
416         UNFORMAT_HAS_SPACE(src, 1);
417         len2 = (SilcUInt8)src->data[0];
418         silc_buffer_pull(src, 1);
419         UNFORMAT_HAS_SPACE(src, len2);
420         if (len)
421           *len = len2;
422         if (x)
423           *x = src->data;
424         silc_buffer_pull(src, len2);
425         break;
426       }
427     case SILC_BUFFER_PARAM_UI16_NSTRING:
428       {
429         SilcUInt16 len2;
430         unsigned char **x = va_arg(ap, unsigned char **);
431         SilcUInt16 *len = va_arg(ap, SilcUInt16 *);
432         UNFORMAT_HAS_SPACE(src, 2);
433         SILC_GET16_MSB(len2, src->data);
434         silc_buffer_pull(src, 2);
435         UNFORMAT_HAS_SPACE(src, len2);
436         if (len)
437           *len = len2;
438         if (x)
439           *x = src->data;
440         silc_buffer_pull(src, len2);
441         break;
442       }
443     case SILC_BUFFER_PARAM_UI8_NSTRING_ALLOC:
444       {
445         SilcUInt8 len2;
446         unsigned char **x = va_arg(ap, unsigned char **);
447         SilcUInt8 *len = va_arg(ap, SilcUInt8 *);
448         UNFORMAT_HAS_SPACE(src, 1);
449         len2 = (SilcUInt8)src->data[0];
450         silc_buffer_pull(src, 1);
451         UNFORMAT_HAS_SPACE(src, len2);
452         if (len)
453           *len = len2;
454         if (x && len2) {
455           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
456           memcpy(*x, src->data, len2);
457         }
458         silc_buffer_pull(src, len2);
459         break;
460       }
461     case SILC_BUFFER_PARAM_UI16_NSTRING_ALLOC:
462       {
463         SilcUInt16 len2;
464         unsigned char **x = va_arg(ap, unsigned char **);
465         SilcUInt16 *len = va_arg(ap, SilcUInt16 *);
466         UNFORMAT_HAS_SPACE(src, 2);
467         SILC_GET16_MSB(len2, src->data);
468         silc_buffer_pull(src, 2);
469         UNFORMAT_HAS_SPACE(src, len2);
470         if (len)
471           *len = len2;
472         if (x && len2) {
473           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
474           memcpy(*x, src->data, len2);
475         }
476         silc_buffer_pull(src, len2);
477         break;
478       }
479     case SILC_BUFFER_PARAM_UI32_NSTRING:
480       {
481         SilcUInt32 len2;
482         unsigned char **x = va_arg(ap, unsigned char **);
483         SilcUInt32 *len = va_arg(ap, SilcUInt32 *);
484         UNFORMAT_HAS_SPACE(src, 4);
485         SILC_GET32_MSB(len2, src->data);
486         silc_buffer_pull(src, 4);
487         UNFORMAT_HAS_SPACE(src, len2);
488         if (len)
489           *len = len2;
490         if (x)
491           *x = src->data;
492         silc_buffer_pull(src, len2);
493         break;
494       }
495     case SILC_BUFFER_PARAM_UI_XNSTRING:
496       {
497         unsigned char **x = va_arg(ap, unsigned char **);
498         SilcUInt32 len = va_arg(ap, SilcUInt32);
499         UNFORMAT_HAS_SPACE(src, len);
500         if (len && x)
501           *x = src->data;
502         silc_buffer_pull(src, len);
503         break;
504       }
505     case SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC:
506       {
507         unsigned char **x = va_arg(ap, unsigned char **);
508         SilcUInt32 len = va_arg(ap, SilcUInt32);
509         UNFORMAT_HAS_SPACE(src, len);
510         if (len && x) {
511           *x = silc_calloc(len + 1, sizeof(unsigned char));
512           memcpy(*x, src->data, len);
513         }
514         silc_buffer_pull(src, len);
515         break;
516       }
517     case SILC_BUFFER_PARAM_END:
518       goto ok;
519       break;
520     default:
521       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
522                       "format the data.", fmt));
523       goto fail;
524       break;
525     }
526   }
527
528  fail:
529   SILC_LOG_DEBUG(("Error occured while unformatting buffer"));
530   len = src->data - start_ptr;
531   silc_buffer_push(src, len);
532   return -1;
533
534  ok:
535   /* Push the buffer back to the start. */
536   len = src->data - start_ptr;
537   silc_buffer_push(src, len);
538   return len;
539 }
540
541 /* Formats strings into a buffer */
542
543 int silc_buffer_strformat(SilcBuffer dst, ...)
544 {
545   int len = silc_buffer_truelen(dst);
546   va_list va;
547
548   va_start(va, dst);
549
550   /* Parse the arguments by formatting type. */
551   while(1) {
552     char *string = va_arg(va, char *);
553     unsigned char *d;
554     SilcInt32 slen;
555
556     if (!string)
557       continue;
558     if (string == (char *)SILC_BUFFER_PARAM_END)
559       goto ok;
560
561     slen = strlen(string);
562     d = silc_realloc(dst->head, sizeof(*dst->head) * (slen + len + 1));
563     if (!d)
564       return -1;
565     dst->head = d;
566     memcpy(dst->head + len, string, slen);
567     len += slen;
568     dst->head[len] = '\0';
569   }
570
571   SILC_LOG_DEBUG(("Error occured while formatting buffer"));
572   va_end(va);
573   return -1;
574
575  ok:
576   dst->end = dst->head + len;
577   dst->data = dst->head;
578   dst->tail = dst->end;
579
580   va_end(va);
581   return len;
582 }
583
584 /* Formats strings into a buffer.  Allocates memory from SilcStack. */
585
586 int silc_buffer_sstrformat(SilcStack stack, SilcBuffer dst, ...)
587 {
588   int len = silc_buffer_truelen(dst);
589   va_list va;
590
591   va_start(va, dst);
592
593   /* Parse the arguments by formatting type. */
594   while(1) {
595     char *string = va_arg(va, char *);
596     unsigned char *d;
597     SilcInt32 slen;
598
599     if (!string)
600       continue;
601     if (string == (char *)SILC_BUFFER_PARAM_END)
602       goto ok;
603
604     slen = strlen(string);
605     d = silc_srealloc_ua(stack, len, dst->head,
606                          sizeof(*dst->head) * (slen + len + 1));
607     if (!d)
608       return -1;
609     dst->head = d;
610     memcpy(dst->head + len, string, slen);
611     len += slen;
612     dst->head[len] = '\0';
613   }
614
615   SILC_LOG_DEBUG(("Error occured while formatting buffer"));
616   va_end(va);
617   return -1;
618
619  ok:
620   dst->end = dst->head + len;
621   dst->data = dst->head;
622   dst->tail = dst->end;
623
624   va_end(va);
625   return len;
626 }