Static analyzer bug fixes
[silc.git] / lib / silccore / silcargument.c
1 /*
2
3   silcargument.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2007 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 /* Implementation of Argument Payload routines */
20 /* $Id$ */
21
22 #include "silc.h"
23 #include "silcargument.h"
24
25 /*************************** Argument Payload *******************************/
26
27 struct SilcArgumentPayloadStruct {
28   SilcUInt32 argc;
29   unsigned char **argv;
30   SilcUInt32 *argv_lens;
31   SilcUInt32 *argv_types;
32   SilcUInt32 pos;
33 };
34
35 /* Parses arguments and returns them into Argument Payload structure. */
36
37 SilcArgumentPayload silc_argument_payload_parse(const unsigned char *payload,
38                                                 SilcUInt32 payload_len,
39                                                 SilcUInt32 argc)
40 {
41   SilcBufferStruct buffer;
42   SilcArgumentPayload newp;
43   SilcUInt16 p_len = 0;
44   unsigned char arg_type = 0;
45   SilcUInt32 pull_len = 0;
46   int i = 0, ret;
47
48   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
49   newp = silc_calloc(1, sizeof(*newp));
50   if (!newp)
51     return NULL;
52   newp->argv = silc_calloc(argc, sizeof(unsigned char *));
53   if (!newp->argv)
54     goto err;
55   newp->argv_lens = silc_calloc(argc, sizeof(SilcUInt32));
56   if (!newp->argv_lens)
57     goto err;
58   newp->argv_types = silc_calloc(argc, sizeof(SilcUInt32));
59   if (!newp->argv_types)
60     goto err;
61
62   /* Get arguments */
63   for (i = 0; i < argc; i++) {
64     ret = silc_buffer_unformat(&buffer,
65                                SILC_STR_UI_SHORT(&p_len),
66                                SILC_STR_UI_CHAR(&arg_type),
67                                SILC_STR_END);
68     if (ret == -1 || p_len > silc_buffer_len(&buffer) - 3) {
69       SILC_LOG_DEBUG(("Malformed argument payload"));
70       goto err;
71     }
72
73     newp->argv_lens[i] = p_len;
74     newp->argv_types[i] = arg_type;
75
76     /* Get argument data */
77     silc_buffer_pull(&buffer, 3);
78     ret = silc_buffer_unformat(&buffer,
79                                SILC_STR_UI_XNSTRING_ALLOC(&newp->argv[i],
80                                                           p_len),
81                                SILC_STR_END);
82     if (ret == -1) {
83       SILC_LOG_DEBUG(("Malformed argument payload"));
84       goto err;
85     }
86
87     silc_buffer_pull(&buffer, p_len);
88     pull_len += 3 + p_len;
89   }
90
91   if (silc_buffer_len(&buffer) != 0) {
92     SILC_LOG_DEBUG(("Malformed argument payload"));
93     goto err;
94   }
95
96   newp->argc = argc;
97   newp->pos = 0;
98
99   silc_buffer_push(&buffer, pull_len);
100
101   return newp;
102
103  err:
104   SILC_LOG_DEBUG(("Error parsing argument payload"));
105   if (i)
106     for (ret = 0; ret < i; ret++)
107       silc_free(newp->argv[ret]);
108
109   silc_free(newp->argv);
110   silc_free(newp->argv_lens);
111   silc_free(newp->argv_types);
112   silc_free(newp);
113
114   return NULL;
115 }
116
117 /* Encodes arguments in to Argument Paylods returning them to SilcBuffer. */
118
119 SilcBuffer silc_argument_payload_encode(SilcUInt32 argc,
120                                         unsigned char **argv,
121                                         SilcUInt32 *argv_lens,
122                                         SilcUInt32 *argv_types)
123 {
124   SilcBuffer buffer;
125   SilcUInt32 len;
126   int i;
127
128   len = 0;
129   for (i = 0; i < argc; i++)
130     len += 3 + (SilcUInt16)argv_lens[i];
131
132   buffer = silc_buffer_alloc_size(len);
133   if (!buffer)
134     return NULL;
135
136   /* Put arguments */
137   for (i = 0; i < argc; i++) {
138     silc_buffer_format(buffer,
139                        SILC_STR_UI_SHORT(argv_lens[i]),
140                        SILC_STR_UI_CHAR(argv_types[i]),
141                        SILC_STR_UI_XNSTRING(argv[i], (SilcUInt16)argv_lens[i]),
142                        SILC_STR_END);
143     silc_buffer_pull(buffer, 3 + (SilcUInt16)argv_lens[i]);
144   }
145
146   silc_buffer_push(buffer, len);
147
148   return buffer;
149 }
150
151 /* Encode one argument to buffer */
152
153 SilcBuffer silc_argument_payload_encode_one(SilcBuffer args,
154                                             unsigned char *arg,
155                                             SilcUInt32 arg_len,
156                                             SilcUInt32 arg_type)
157 {
158   SilcBuffer buffer = args;
159   SilcUInt32 len;
160
161   len = 3 + (SilcUInt16)arg_len;
162   buffer = silc_buffer_realloc(buffer,
163                                (buffer ? silc_buffer_truelen(buffer) +
164                                 len : len));
165   if (!buffer)
166     return NULL;
167   silc_buffer_pull(buffer, silc_buffer_len(buffer));
168   silc_buffer_pull_tail(buffer, len);
169   silc_buffer_format(buffer,
170                      SILC_STR_UI_SHORT(arg_len),
171                      SILC_STR_UI_CHAR(arg_type),
172                      SILC_STR_UI_XNSTRING(arg, (SilcUInt16)arg_len),
173                      SILC_STR_END);
174   silc_buffer_push(buffer, buffer->data - buffer->head);
175
176   return buffer;
177 }
178
179 /* Same as above but encode the buffer from SilcArgumentPayload structure
180    instead of raw data. */
181
182 SilcBuffer silc_argument_payload_encode_payload(SilcArgumentPayload payload)
183 {
184   SilcBuffer buffer;
185   SilcUInt32 len;
186   int i;
187
188   len = 0;
189   for (i = 0; i < payload->argc; i++)
190     len += 3 + payload->argv_lens[i];
191
192   buffer = silc_buffer_alloc_size(len);
193   if (!buffer)
194     return NULL;
195
196   /* Put arguments */
197   for (i = 0; i < payload->argc; i++) {
198     silc_buffer_format(buffer,
199                        SILC_STR_UI_SHORT(payload->argv_lens[i]),
200                        SILC_STR_UI_CHAR(payload->argv_types[i]),
201                        SILC_STR_UI_XNSTRING(payload->argv[i],
202                                             payload->argv_lens[i]),
203                        SILC_STR_END);
204     silc_buffer_pull(buffer, 3 + payload->argv_lens[i]);
205   }
206
207   silc_buffer_push(buffer, len);
208
209   return buffer;
210 }
211
212 /* Frees Argument Payload */
213
214 void silc_argument_payload_free(SilcArgumentPayload payload)
215 {
216   int i;
217
218   if (payload) {
219     for (i = 0; i < payload->argc; i++)
220       silc_free(payload->argv[i]);
221
222     silc_free(payload->argv);
223     silc_free(payload->argv_lens);
224     silc_free(payload->argv_types);
225     silc_free(payload);
226   }
227 }
228
229 /* Returns number of arguments in payload */
230
231 SilcUInt32 silc_argument_get_arg_num(SilcArgumentPayload payload)
232 {
233   return payload ? payload->argc : 0;
234 }
235
236 /* Returns first argument from payload. */
237
238 unsigned char *silc_argument_get_first_arg(SilcArgumentPayload payload,
239                                            SilcUInt32 *type,
240                                            SilcUInt32 *ret_len)
241 {
242   if (!payload)
243     return NULL;
244
245   payload->pos = 0;
246
247   if (type)
248     *type = payload->argv_types[payload->pos];
249   if (ret_len)
250     *ret_len = payload->argv_lens[payload->pos];
251
252   return payload->argv[payload->pos++];
253 }
254
255 /* Returns next argument from payload or NULL if no more arguments. */
256
257 unsigned char *silc_argument_get_next_arg(SilcArgumentPayload payload,
258                                           SilcUInt32 *type,
259                                           SilcUInt32 *ret_len)
260 {
261   if (!payload)
262     return NULL;
263
264   if (payload->pos >= payload->argc)
265     return NULL;
266
267   if (type)
268     *type = payload->argv_types[payload->pos];
269   if (ret_len)
270     *ret_len = payload->argv_lens[payload->pos];
271
272   return payload->argv[payload->pos++];
273 }
274
275 /* Returns argument which type is `type'. */
276
277 unsigned char *silc_argument_get_arg_type(SilcArgumentPayload payload,
278                                           SilcUInt32 type,
279                                           SilcUInt32 *ret_len)
280 {
281   int i;
282
283   if (!payload)
284     return NULL;
285
286   for (i = 0; i < payload->argc; i++)
287     if (payload->argv_types[i] == type)
288       break;
289
290   if (i >= payload->argc)
291     return NULL;
292
293   if (ret_len)
294     *ret_len = payload->argv_lens[i];
295
296   return payload->argv[i];
297 }
298
299 /* Return argument already decoded */
300
301 static SilcBool silc_argument_decode(unsigned char *data,
302                                      SilcUInt32 data_len,
303                                      SilcArgumentDecodeType dec_type,
304                                      void *ret_arg,
305                                      void **ret_arg_alloc)
306 {
307   switch (dec_type) {
308
309   case SILC_ARGUMENT_ID:
310     if (ret_arg)
311       if (!silc_id_payload_parse_id(data, data_len, (SilcID *)ret_arg))
312         return FALSE;
313
314     if (ret_arg_alloc) {
315       SilcID id;
316       if (!silc_id_payload_parse_id(data, data_len, &id))
317         return FALSE;
318       *ret_arg_alloc = silc_memdup(&id, sizeof(id));
319     }
320     break;
321
322   case SILC_ARGUMENT_PUBLIC_KEY:
323     {
324       SilcPublicKey public_key;
325
326       if (!ret_arg_alloc)
327         return FALSE;
328
329       if (!silc_public_key_payload_decode(data, data_len, &public_key))
330         return FALSE;
331
332       *ret_arg_alloc = public_key;
333     }
334     break;
335
336   case SILC_ARGUMENT_ATTRIBUTES:
337     if (!ret_arg_alloc)
338       return FALSE;
339
340     *ret_arg_alloc = silc_attribute_payload_parse(data, data_len);
341     break;
342
343   case SILC_ARGUMENT_UINT32:
344     if (data_len != 4)
345       return FALSE;
346
347     if (ret_arg) {
348       SilcUInt32 *i = ret_arg;
349       SILC_GET32_MSB(*i, data);
350     }
351
352     if (ret_arg_alloc) {
353       SilcUInt32 i;
354       SILC_GET32_MSB(i, data);
355       *ret_arg_alloc = silc_memdup(&i, sizeof(i));
356     }
357     break;
358
359   case SILC_ARGUMENT_BOOL:
360     if (data_len != sizeof(SilcBool))
361       return FALSE;
362
363     if (ret_arg) {
364       SilcBool *b = ret_arg;
365       *b = (data[0] == 0x01 ? TRUE : FALSE);
366     }
367
368     if (ret_arg_alloc) {
369       SilcBool b;
370       b = (data[0] == 0x01 ? TRUE : FALSE);
371       *ret_arg_alloc = silc_memdup(&b, sizeof(b));
372     }
373     break;
374
375   default:
376     return FALSE;
377   }
378
379   return TRUE;
380 }
381
382 /* Return argument already decoded */
383
384 SilcBool silc_argument_get_decoded(SilcArgumentPayload payload,
385                                    SilcUInt32 type,
386                                    SilcArgumentDecodeType dec_type,
387                                    void *ret_arg,
388                                    void **ret_arg_alloc)
389 {
390   unsigned char *tmp;
391   SilcUInt32 tmp_len;
392
393   tmp = silc_argument_get_arg_type(payload, type, &tmp_len);
394   if (!tmp)
395     return FALSE;
396
397   return silc_argument_decode(tmp, tmp_len, dec_type, ret_arg, ret_arg_alloc);
398 }
399
400 /************************* Argument List Payload ****************************/
401
402 /* Parses argument payload list */
403
404 SilcArgumentPayload
405 silc_argument_list_parse(const unsigned char *payload,
406                          SilcUInt32 payload_len)
407 {
408   SilcArgumentPayload arg;
409   SilcUInt16 argc;
410
411   if (payload_len < 5)
412     return NULL;
413
414   SILC_GET16_MSB(argc, payload);
415
416   arg = silc_argument_payload_parse(payload + 2, payload_len - 2, argc);
417
418   return arg;
419 }
420
421 /* Parses argument payload list of specific argument types */
422
423 SilcDList
424 silc_argument_list_parse_decoded(const unsigned char *payload,
425                                  SilcUInt32 payload_len,
426                                  SilcArgumentDecodeType dec_type)
427 {
428   SilcArgumentPayload arg;
429   SilcArgumentDecodedList dec;
430   unsigned char *data;
431   SilcUInt32 data_len, type;
432   SilcDList list;
433
434   arg = silc_argument_list_parse(payload, payload_len);
435   if (!arg)
436     return NULL;
437
438   list = silc_dlist_init();
439   if (!list) {
440     silc_argument_payload_free(arg);
441     return NULL;
442   }
443
444   data = silc_argument_get_first_arg(arg, &type, &data_len);
445   while (data) {
446     dec = silc_calloc(1, sizeof(*dec));
447     if (!dec)
448       continue;
449     dec->arg_type = type;
450     if (silc_argument_decode(data, data_len, dec_type, NULL, &dec->argument))
451       silc_dlist_add(list, dec);
452     else
453       silc_free(dec);
454     data = silc_argument_get_next_arg(arg, &type, &data_len);
455   }
456
457   silc_argument_payload_free(arg);
458
459   silc_dlist_start(list);
460
461   return list;
462 }
463
464 /* Free decoded argument payload list */
465
466 void silc_argument_list_free(SilcDList list, SilcArgumentDecodeType dec_type)
467 {
468   SilcArgumentDecodedList dec;
469
470   if (!list)
471     return;
472
473   silc_dlist_start(list);
474   while ((dec = silc_dlist_get(list))) {
475     switch (dec_type) {
476
477     case SILC_ARGUMENT_ID:
478     case SILC_ARGUMENT_UINT32:
479     case SILC_ARGUMENT_BOOL:
480       silc_free(dec->argument);
481       break;
482
483     case SILC_ARGUMENT_PUBLIC_KEY:
484       silc_pkcs_public_key_free(dec->argument);
485       break;
486
487     case SILC_ARGUMENT_ATTRIBUTES:
488       silc_attribute_payload_free(dec->argument);
489       break;
490
491     default:
492       break;
493     }
494
495     silc_free(dec);
496   }
497
498   silc_dlist_uninit(list);
499 }