Fix two more coverity constant expression result findings.
[asterisk/asterisk.git] / pbx / dundi-parser.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Distributed Universal Number Discovery (DUNDi)
22  *
23  */
24
25 #include "asterisk.h"
26
27 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
28
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32
33 #include "asterisk/frame.h"
34 #include "asterisk/utils.h"
35 #include "asterisk/dundi.h"
36 #include "dundi-parser.h"
37
38 static void internaloutput(const char *str)
39 {
40         fputs(str, stdout);
41 }
42
43 static void internalerror(const char *str)
44 {
45         fprintf(stderr, "WARNING: %s", str);
46 }
47
48 static void (*outputf)(const char *str) = internaloutput;
49 static void (*errorf)(const char *str) = internalerror;
50
51 char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid)
52 {
53         int x;
54         char *os = s;
55         if (maxlen < 13) {
56                 if (s && (maxlen > 0))
57                         *s = '\0';
58         } else {
59                 for (x=0;x<6;x++) {
60                         sprintf(s, "%02X", eid->eid[x]);
61                         s += 2;
62                 }
63         }
64         return os;
65 }
66
67 int dundi_str_short_to_eid(dundi_eid *eid, const char *s)
68 {
69         unsigned int eid_int[6];
70         int x;
71         if (sscanf(s, "%2x%2x%2x%2x%2x%2x", &eid_int[0], &eid_int[1], &eid_int[2],
72                  &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
73                         return -1;
74         for (x = 0; x < 6; x++)
75                 eid->eid[x] = eid_int[x];
76         return 0;
77 }
78
79 int dundi_eid_zero(dundi_eid *eid)
80 {
81         int x;
82         for (x = 0; x < ARRAY_LEN(eid->eid); x++)
83                 if (eid->eid[x]) return 0;
84         return 1;
85 }
86
87 static void dump_string(char *output, int maxlen, void *value, int len)
88 {
89         if (maxlen > len + 1)
90                 maxlen = len + 1;
91
92         snprintf(output, maxlen, "%s", (char *) value);
93 }
94
95 static void dump_cbypass(char *output, int maxlen, void *value, int len)
96 {
97         snprintf(output, maxlen, "Bypass Caches");
98 }
99
100 static void dump_eid(char *output, int maxlen, void *value, int len)
101 {
102         if (len == 6)
103                 ast_eid_to_str(output, maxlen, (dundi_eid *)value);
104         else
105                 snprintf(output, maxlen, "Invalid EID len %d", len);
106 }
107
108 char *dundi_hint2str(char *buf, int bufsiz, int flags)
109 {
110         strcpy(buf, "");
111         buf[bufsiz-1] = '\0';
112         if (flags & DUNDI_HINT_TTL_EXPIRED) {
113                 strncat(buf, "TTLEXPIRED|", bufsiz - strlen(buf) - 1);
114         }
115         if (flags & DUNDI_HINT_DONT_ASK) {
116                 strncat(buf, "DONTASK|", bufsiz - strlen(buf) - 1);
117         }
118         if (flags & DUNDI_HINT_UNAFFECTED) {
119                 strncat(buf, "UNAFFECTED|", bufsiz - strlen(buf) - 1);
120         }
121         /* Get rid of trailing | */
122         if (ast_strlen_zero(buf))
123                 strcpy(buf, "NONE|");
124         buf[strlen(buf)-1] = '\0';
125         return buf;
126 }
127
128 static void dump_hint(char *output, int maxlen, void *value, int len)
129 {
130         char tmp2[256];
131         char tmp3[256];
132         int datalen;
133         struct dundi_hint *hint;
134         if (len < sizeof(*hint)) {
135                 snprintf(output, maxlen, "<invalid contents>");
136                 return;
137         }
138
139         hint = (struct dundi_hint *) value;;
140
141         datalen = len - offsetof(struct dundi_hint, data);
142         if (datalen > sizeof(tmp3) - 1)
143                 datalen = sizeof(tmp3) - 1;
144
145         memcpy(tmp3, hint->data, datalen);
146         tmp3[datalen] = '\0';
147
148         dundi_hint2str(tmp2, sizeof(tmp2), ntohs(hint->flags));
149
150         if (ast_strlen_zero(tmp3))
151                 snprintf(output, maxlen, "[%s]", tmp2);
152         else
153                 snprintf(output, maxlen, "[%s] %s", tmp2, tmp3);
154 }
155
156 static void dump_cause(char *output, int maxlen, void *value, int len)
157 {
158         static const char * const causes[] = {
159                 "SUCCESS",
160                 "GENERAL",
161                 "DYNAMIC",
162                 "NOAUTH" ,
163                 };
164         char tmp2[256];
165         struct dundi_cause *cause;
166         int datalen;
167         int causecode;
168
169         if (len < sizeof(*cause)) {
170                 snprintf(output, maxlen, "<invalid contents>");
171                 return;
172         }
173
174         cause = (struct dundi_cause*) value;
175         causecode = cause->causecode;
176
177         datalen = len - offsetof(struct dundi_cause, desc);
178         if (datalen > sizeof(tmp2) - 1)
179                 datalen = sizeof(tmp2) - 1;
180
181         memcpy(tmp2, cause->desc, datalen);
182         tmp2[datalen] = '\0';
183
184         if (causecode < ARRAY_LEN(causes)) {
185                 if (ast_strlen_zero(tmp2))
186                         snprintf(output, maxlen, "%s", causes[causecode]);
187                 else
188                         snprintf(output, maxlen, "%s: %s", causes[causecode], tmp2);
189         } else {
190                 if (ast_strlen_zero(tmp2))
191                         snprintf(output, maxlen, "%d", causecode);
192                 else
193                         snprintf(output, maxlen, "%d: %s", causecode, tmp2);
194         }
195 }
196
197 static void dump_int(char *output, int maxlen, void *value, int len)
198 {
199         if (len == (int)sizeof(unsigned int))
200                 snprintf(output, maxlen, "%lu", (unsigned long)ntohl(*((unsigned int *)value)));
201         else
202                 ast_copy_string(output, "Invalid INT", maxlen);
203 }
204
205 static void dump_short(char *output, int maxlen, void *value, int len)
206 {
207         if (len == (int)sizeof(unsigned short))
208                 snprintf(output, maxlen, "%d", ntohs(*((unsigned short *)value)));
209         else
210                 ast_copy_string(output, "Invalid SHORT", maxlen);
211 }
212
213 static void dump_byte(char *output, int maxlen, void *value, int len)
214 {
215         if (len == (int)sizeof(unsigned char))
216                 snprintf(output, maxlen, "%d", *((unsigned char *)value));
217         else
218                 ast_copy_string(output, "Invalid BYTE", maxlen);
219 }
220
221 static char *proto2str(int proto, char *buf, int bufsiz)
222 {       
223         switch(proto) {
224         case DUNDI_PROTO_NONE:
225                 strncpy(buf, "None", bufsiz - 1);
226                 break;
227         case DUNDI_PROTO_IAX:
228                 strncpy(buf, "IAX", bufsiz - 1);
229                 break;
230         case DUNDI_PROTO_SIP:
231                 strncpy(buf, "SIP", bufsiz - 1);
232                 break;
233         case DUNDI_PROTO_H323:
234                 strncpy(buf, "H.323", bufsiz - 1);
235                 break;
236         default:
237                 snprintf(buf, bufsiz, "Unknown Proto(%d)", proto);
238         }
239         buf[bufsiz-1] = '\0';
240         return buf;
241 }
242
243 char *dundi_flags2str(char *buf, int bufsiz, int flags)
244 {
245         strcpy(buf, "");
246         buf[bufsiz-1] = '\0';
247         if (flags & DUNDI_FLAG_EXISTS) {
248                 strncat(buf, "EXISTS|", bufsiz - strlen(buf) - 1);
249         }
250         if (flags & DUNDI_FLAG_MATCHMORE) {
251                 strncat(buf, "MATCHMORE|", bufsiz - strlen(buf) - 1);
252         }
253         if (flags & DUNDI_FLAG_CANMATCH) {
254                 strncat(buf, "CANMATCH|", bufsiz - strlen(buf) - 1);
255         }
256         if (flags & DUNDI_FLAG_IGNOREPAT) {
257                 strncat(buf, "IGNOREPAT|", bufsiz - strlen(buf) - 1);
258         }
259         if (flags & DUNDI_FLAG_RESIDENTIAL) {
260                 strncat(buf, "RESIDENCE|", bufsiz - strlen(buf) - 1);
261         }
262         if (flags & DUNDI_FLAG_COMMERCIAL) {
263                 strncat(buf, "COMMERCIAL|", bufsiz - strlen(buf) - 1);
264         }
265         if (flags & DUNDI_FLAG_MOBILE) {
266                 strncat(buf, "MOBILE", bufsiz - strlen(buf) - 1);
267         }
268         if (flags & DUNDI_FLAG_NOUNSOLICITED) {
269                 strncat(buf, "NOUNSLCTD|", bufsiz - strlen(buf) - 1);
270         }
271         if (flags & DUNDI_FLAG_NOCOMUNSOLICIT) {
272                 strncat(buf, "NOCOMUNSLTD|", bufsiz - strlen(buf) - 1);
273         }
274         /* Get rid of trailing | */
275         if (ast_strlen_zero(buf))
276                 strcpy(buf, "NONE|");
277         buf[strlen(buf)-1] = '\0';
278         return buf;
279 }
280
281 static void dump_answer(char *output, int maxlen, void *value, int len)
282 {
283         struct dundi_answer *answer;
284         char proto[40];
285         char flags[40];
286         char eid_str[40];
287         char tmp[512]="";
288         int datalen;
289
290         if (len < sizeof(*answer)) {
291                 snprintf(output, maxlen, "Invalid Answer");
292                 return;
293         }
294
295         answer = (struct dundi_answer *)(value);
296
297         datalen = len - offsetof(struct dundi_answer, data);
298         if (datalen > sizeof(tmp) - 1)
299                 datalen = sizeof(tmp) - 1;
300
301         memcpy(tmp, answer->data, datalen);
302         tmp[datalen] = '\0';
303
304         ast_eid_to_str(eid_str, sizeof(eid_str), &answer->eid);
305         snprintf(output, maxlen, "[%s] %d <%s/%s> from [%s]", 
306                 dundi_flags2str(flags, sizeof(flags), ntohs(answer->flags)), 
307                 ntohs(answer->weight),
308                 proto2str(answer->protocol, proto, sizeof(proto)), 
309                         tmp, eid_str);
310 }
311
312 static void dump_encrypted(char *output, int maxlen, void *value, int len)
313 {
314         char iv[33];
315         int x;
316         if ((len > 16) && !(len % 16)) {
317                 /* Build up IV */
318                 for (x=0;x<16;x++) {
319                         snprintf(iv + (x << 1), 3, "%02x", ((unsigned char *)value)[x]);
320                 }
321                 snprintf(output, maxlen, "[IV %s] %d encrypted blocks\n", iv, len / 16);
322         } else
323                 snprintf(output, maxlen, "Invalid Encrypted Datalen %d", len);
324 }
325
326 static void dump_raw(char *output, int maxlen, void *value, int len)
327 {
328         int x;
329         unsigned char *u = value;
330         output[maxlen - 1] = '\0';
331         strcpy(output, "[ ");
332         for (x=0;x<len;x++) {
333                 snprintf(output + strlen(output), maxlen - strlen(output) - 1, "%02x ", u[x]);
334         }
335         strncat(output + strlen(output), "]", maxlen - strlen(output) - 1);
336 }
337
338 static struct dundi_ie {
339         int ie;
340         char *name;
341         void (*dump)(char *output, int maxlen, void *value, int len);
342 } infoelts[] = {
343         { DUNDI_IE_EID, "ENTITY IDENT", dump_eid },
344         { DUNDI_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string },
345         { DUNDI_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string },
346         { DUNDI_IE_EID_DIRECT, "DIRECT EID", dump_eid },
347         { DUNDI_IE_ANSWER, "ANSWER", dump_answer },
348         { DUNDI_IE_TTL, "TTL", dump_short },
349         { DUNDI_IE_VERSION, "VERSION", dump_short },
350         { DUNDI_IE_EXPIRATION, "EXPIRATION", dump_short },
351         { DUNDI_IE_UNKNOWN, "UKWN DUNDI CMD", dump_byte },
352         { DUNDI_IE_CAUSE, "CAUSE", dump_cause },
353         { DUNDI_IE_REQEID, "REQUEST EID", dump_eid },
354         { DUNDI_IE_ENCDATA, "ENCDATA", dump_encrypted },
355         { DUNDI_IE_SHAREDKEY, "SHAREDKEY", dump_raw },
356         { DUNDI_IE_SIGNATURE, "SIGNATURE", dump_raw },
357         { DUNDI_IE_KEYCRC32, "KEYCRC32", dump_int },
358         { DUNDI_IE_HINT, "HINT", dump_hint },
359         { DUNDI_IE_DEPARTMENT, "DEPARTMENT", dump_string },
360         { DUNDI_IE_ORGANIZATION, "ORGANIZTN", dump_string },
361         { DUNDI_IE_LOCALITY, "LOCALITY", dump_string },
362         { DUNDI_IE_STATE_PROV, "STATEPROV", dump_string },
363         { DUNDI_IE_COUNTRY, "COUNTRY", dump_string },
364         { DUNDI_IE_EMAIL, "EMAIL", dump_string },
365         { DUNDI_IE_PHONE, "PHONE", dump_string },
366         { DUNDI_IE_IPADDR, "ADDRESS", dump_string },
367         { DUNDI_IE_CACHEBYPASS, "CBYPASS", dump_cbypass },
368 };
369
370 const char *dundi_ie2str(int ie)
371 {
372         int x;
373         for (x = 0; x < ARRAY_LEN(infoelts); x++) {
374                 if (infoelts[x].ie == ie)
375                         return infoelts[x].name;
376         }
377         return "Unknown IE";
378 }
379
380 static void dump_ies(unsigned char *iedata, int spaces, int len)
381 {
382         int ielen;
383         int ie;
384         int x;
385         int found;
386         char interp[1024];
387         char tmp[1024];
388         if (len < 2)
389                 return;
390         while(len >= 2) {
391                 ie = iedata[0];
392                 ielen = iedata[1];
393                 /* Encrypted data is the remainder */
394                 if (ie == DUNDI_IE_ENCDATA)
395                         ielen = len - 2;
396                 if (ielen + 2> len) {
397                         snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len);
398                         outputf(tmp);
399                         return;
400                 }
401                 found = 0;
402                 for (x = 0; x < ARRAY_LEN(infoelts); x++) {
403                         if (infoelts[x].ie == ie) {
404                                 if (infoelts[x].dump) {
405                                         infoelts[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen);
406                                         snprintf(tmp, (int)sizeof(tmp), "   %s%-15.15s : %s\n", (spaces ? "     " : "" ), infoelts[x].name, interp);
407                                         outputf(tmp);
408                                 } else {
409                                         if (ielen)
410                                                 snprintf(interp, (int)sizeof(interp), "%d bytes", ielen);
411                                         else
412                                                 strcpy(interp, "Present");
413                                         snprintf(tmp, (int)sizeof(tmp), "   %s%-15.15s : %s\n", (spaces ? "     " : "" ), infoelts[x].name, interp);
414                                         outputf(tmp);
415                                 }
416                                 found++;
417                         }
418                 }
419                 if (!found) {
420                         snprintf(tmp, (int)sizeof(tmp), "   %sUnknown IE %03d  : Present\n", (spaces ? "     " : "" ), ie);
421                         outputf(tmp);
422                 }
423                 iedata += (2 + ielen);
424                 len -= (2 + ielen);
425         }
426         outputf("\n");
427 }
428
429 void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen)
430 {
431         char *pref[] = {
432                 "Tx",
433                 "Rx",
434                 "    ETx",
435                 "    Erx" };
436         char *commands[] = {
437                 "ACK         ",
438                 "DPDISCOVER  ",
439                 "DPRESPONSE  ",
440                 "EIDQUERY    ",
441                 "EIDRESPONSE ",
442                 "PRECACHERQ  ",
443                 "PRECACHERP  ",
444                 "INVALID     ",
445                 "UNKNOWN CMD ",
446                 "NULL        ",
447                 "REQREQ      ",
448                 "REGRESPONSE ",
449                 "CANCEL      ",
450                 "ENCRYPT     ",
451                 "ENCREJ      " };
452         char class2[20];
453         char *class;
454         char subclass2[20];
455         char *subclass;
456         char tmp[256];
457         if ((fhi->cmdresp & 0x3f) > (int)sizeof(commands)/(int)sizeof(char *)) {
458                 snprintf(class2, (int)sizeof(class2), "(%d?)", fhi->cmdresp);
459                 class = class2;
460         } else {
461                 class = commands[(int)(fhi->cmdresp & 0x3f)];
462         }
463         snprintf(subclass2, (int)sizeof(subclass2), "%02x", fhi->cmdflags);
464         subclass = subclass2;
465         snprintf(tmp, (int)sizeof(tmp), 
466                 "%s-Frame -- OSeqno: %3.3d ISeqno: %3.3d Type: %s (%s)\n",
467                 pref[rx],
468                 fhi->oseqno, fhi->iseqno, class, fhi->cmdresp & 0x40 ? "Response" : "Command");
469         outputf(tmp);
470         snprintf(tmp, (int)sizeof(tmp), 
471                 "%s     Flags: %s STrans: %5.5d  DTrans: %5.5d [%s:%d]%s\n", (rx > 1) ? "     " : "",
472                 subclass, ntohs(fhi->strans) & ~DUNDI_FLAG_RESERVED, ntohs(fhi->dtrans) & ~DUNDI_FLAG_RETRANS,
473                 ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
474                 fhi->cmdresp & 0x80 ? " (Final)" : "");
475         outputf(tmp);
476         dump_ies(fhi->ies, rx > 1, datalen);
477 }
478
479 int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen)
480 {
481         char tmp[256];
482         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
483                 snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
484                 errorf(tmp);
485                 return -1;
486         }
487         ied->buf[ied->pos++] = ie;
488         ied->buf[ied->pos++] = datalen;
489         memcpy(ied->buf + ied->pos, data, datalen);
490         ied->pos += datalen;
491         return 0;
492 }
493
494 int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, char *data)
495 {
496         char tmp[256];
497         int datalen = data ? strlen(data) + 1 : 1;
498         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
499                 snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
500                 errorf(tmp);
501                 return -1;
502         }
503         ied->buf[ied->pos++] = ie;
504         ied->buf[ied->pos++] = datalen;
505         ied->buf[ied->pos++] = cause;
506         if (data) {
507                 memcpy(ied->buf + ied->pos, data, datalen-1);
508                 ied->pos += datalen-1;
509         }
510         return 0;
511 }
512
513 int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, char *data)
514 {
515         char tmp[256];
516         int datalen = data ? strlen(data) + 2 : 2;
517         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
518                 snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
519                 errorf(tmp);
520                 return -1;
521         }
522         ied->buf[ied->pos++] = ie;
523         ied->buf[ied->pos++] = datalen;
524         flags = htons(flags);
525         memcpy(ied->buf + ied->pos, &flags, sizeof(flags));
526         ied->pos += 2;
527         if (data) {
528                 memcpy(ied->buf + ied->pos, data, datalen-2);
529                 ied->pos += datalen-2;
530         }
531         return 0;
532 }
533
534 int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen)
535 {
536         char tmp[256];
537         datalen += 16;
538         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
539                 snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
540                 errorf(tmp);
541                 return -1;
542         }
543         ied->buf[ied->pos++] = ie;
544         ied->buf[ied->pos++] = datalen;
545         memcpy(ied->buf + ied->pos, iv, 16);
546         ied->pos += 16;
547         if (data) {
548                 memcpy(ied->buf + ied->pos, data, datalen-16);
549                 ied->pos += datalen-16;
550         }
551         return 0;
552 }
553
554 int dundi_ie_append_answer(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid, unsigned char protocol, unsigned short flags, unsigned short weight, char *data)
555 {
556         char tmp[256];
557         int datalen = data ? strlen(data) + 11 : 11;
558         int x;
559         unsigned short myw;
560         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
561                 snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
562                 errorf(tmp);
563                 return -1;
564         }
565         ied->buf[ied->pos++] = ie;
566         ied->buf[ied->pos++] = datalen;
567         for (x=0;x<6;x++)
568                 ied->buf[ied->pos++] = eid->eid[x];
569         ied->buf[ied->pos++] = protocol;
570         myw = htons(flags);
571         memcpy(ied->buf + ied->pos, &myw, 2);
572         ied->pos += 2;
573         myw = htons(weight);
574         memcpy(ied->buf + ied->pos, &myw, 2);
575         ied->pos += 2;
576         memcpy(ied->buf + ied->pos, data, datalen-11);
577         ied->pos += datalen-11;
578         return 0;
579 }
580
581 int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin)
582 {
583         return dundi_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in));
584 }
585
586 int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value) 
587 {
588         unsigned int newval;
589         newval = htonl(value);
590         return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
591 }
592
593 int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value) 
594 {
595         unsigned short newval;
596         newval = htons(value);
597         return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
598 }
599
600 int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, char *str)
601 {
602         return dundi_ie_append_raw(ied, ie, str, strlen(str));
603 }
604
605 int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid)
606 {
607         return dundi_ie_append_raw(ied, ie, (unsigned char *)eid, sizeof(dundi_eid));
608 }
609
610 int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat)
611 {
612         return dundi_ie_append_raw(ied, ie, &dat, 1);
613 }
614
615 int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie) 
616 {
617         return dundi_ie_append_raw(ied, ie, NULL, 0);
618 }
619
620 void dundi_set_output(void (*func)(const char *))
621 {
622         outputf = func;
623 }
624
625 void dundi_set_error(void (*func)(const char *))
626 {
627         errorf = func;
628 }
629
630 int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen)
631 {
632         /* Parse data into information elements */
633         int len;
634         int ie;
635         char tmp[256];
636         memset(ies, 0, (int)sizeof(struct dundi_ies));
637         ies->ttl = -1;
638         ies->expiration = -1;
639         ies->unknowncmd = -1;
640         ies->cause = -1;
641         while(datalen >= 2) {
642                 ie = data[0];
643                 len = data[1];
644                 if (len > datalen - 2) {
645                         errorf("Information element length exceeds message size\n");
646                         return -1;
647                 }
648                 switch(ie) {
649                 case DUNDI_IE_EID:
650                 case DUNDI_IE_EID_DIRECT:
651                         if (len != (int)sizeof(dundi_eid)) {
652                                 errorf("Improper entity identifer, expecting 6 bytes!\n");
653                         } else if (ies->eidcount < DUNDI_MAX_STACK) {
654                                 ies->eids[ies->eidcount] = (dundi_eid *)(data + 2);
655                                 ies->eid_direct[ies->eidcount] = (ie == DUNDI_IE_EID_DIRECT);
656                                 ies->eidcount++;
657                         } else
658                                 errorf("Too many entities in stack!\n");
659                         break;
660                 case DUNDI_IE_REQEID:
661                         if (len != (int)sizeof(dundi_eid)) {
662                                 errorf("Improper requested entity identifer, expecting 6 bytes!\n");
663                         } else
664                                 ies->reqeid = (dundi_eid *)(data + 2);
665                         break;
666                 case DUNDI_IE_CALLED_CONTEXT:
667                         ies->called_context = (char *)data + 2;
668                         break;
669                 case DUNDI_IE_CALLED_NUMBER:
670                         ies->called_number = (char *)data + 2;
671                         break;
672                 case DUNDI_IE_ANSWER:
673                         if (len < sizeof(struct dundi_answer)) {
674                                 snprintf(tmp, (int)sizeof(tmp), "Answer expected to be >=%d bytes long but was %d\n", (int)sizeof(struct dundi_answer), len);
675                                 errorf(tmp);
676                         } else {
677                                 if (ies->anscount < DUNDI_MAX_ANSWERS)
678                                         ies->answers[ies->anscount++]= (struct dundi_answer *)(data + 2);
679                                 else 
680                                         errorf("Ignoring extra answers!\n");
681                         }
682                         break;
683                 case DUNDI_IE_TTL:
684                         if (len != (int)sizeof(unsigned short)) {
685                                 snprintf(tmp, (int)sizeof(tmp), "Expecting ttl to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
686                                 errorf(tmp);
687                         } else
688                                 ies->ttl = ntohs(*((unsigned short *)(data + 2)));
689                         break;
690                 case DUNDI_IE_VERSION:
691                         if (len != (int)sizeof(unsigned short)) {
692                                 snprintf(tmp, (int)sizeof(tmp),  "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
693                                 errorf(tmp);
694                         } else
695                                 ies->version = ntohs(*((unsigned short *)(data + 2)));
696                         break;
697                 case DUNDI_IE_EXPIRATION:
698                         if (len != (int)sizeof(unsigned short)) {
699                                 snprintf(tmp, (int)sizeof(tmp),  "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
700                                 errorf(tmp);
701                         } else
702                                 ies->expiration = ntohs(*((unsigned short *)(data + 2)));
703                         break;
704                 case DUNDI_IE_KEYCRC32:
705                         if (len != (int)sizeof(unsigned int)) {
706                                 snprintf(tmp, (int)sizeof(tmp),  "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
707                                 errorf(tmp);
708                         } else
709                                 ies->keycrc32 = ntohl(*((unsigned int *)(data + 2)));
710                         break;
711                 case DUNDI_IE_UNKNOWN:
712                         if (len == 1)
713                                 ies->unknowncmd = data[2];
714                         else {
715                                 snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len);
716                                 errorf(tmp);
717                         }
718                         break;
719                 case DUNDI_IE_CAUSE:
720                         if (len >= 1) {
721                                 ies->cause = data[2];
722                                 ies->causestr = (char *)data + 3;
723                         } else {
724                                 snprintf(tmp, (int)sizeof(tmp), "Expected at least one byte cause, but was %d long\n", len);
725                                 errorf(tmp);
726                         }
727                         break;
728                 case DUNDI_IE_HINT:
729                         if (len >= 2) {
730                                 ies->hint = (struct dundi_hint *)(data + 2);
731                         } else {
732                                 snprintf(tmp, (int)sizeof(tmp), "Expected at least two byte hint, but was %d long\n", len);
733                                 errorf(tmp);
734                         }
735                         break;
736                 case DUNDI_IE_DEPARTMENT:
737                         ies->q_dept = (char *)data + 2;
738                         break;
739                 case DUNDI_IE_ORGANIZATION:
740                         ies->q_org = (char *)data + 2;
741                         break;
742                 case DUNDI_IE_LOCALITY:
743                         ies->q_locality = (char *)data + 2;
744                         break;
745                 case DUNDI_IE_STATE_PROV:
746                         ies->q_stateprov = (char *)data + 2;
747                         break;
748                 case DUNDI_IE_COUNTRY:
749                         ies->q_country = (char *)data + 2;
750                         break;
751                 case DUNDI_IE_EMAIL:
752                         ies->q_email = (char *)data + 2;
753                         break;
754                 case DUNDI_IE_PHONE:
755                         ies->q_phone = (char *)data + 2;
756                         break;
757                 case DUNDI_IE_IPADDR:
758                         ies->q_ipaddr = (char *)data + 2;
759                         break;
760                 case DUNDI_IE_ENCDATA:
761                         /* Recalculate len as the remainder of the message, regardless of
762                            theoretical length */
763                         len = datalen - 2;
764                         if ((len > 16) && !(len % 16)) {
765                                 ies->encblock = (struct dundi_encblock *)(data + 2);
766                                 ies->enclen = len - 16;
767                         } else {
768                                 snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted data length %d\n", len);
769                                 errorf(tmp);
770                         }
771                         break;
772                 case DUNDI_IE_SHAREDKEY:
773                         if (len == 128) {
774                                 ies->encsharedkey = (unsigned char *)(data + 2);
775                         } else {
776                                 snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted shared key length %d\n", len);
777                                 errorf(tmp);
778                         }
779                         break;
780                 case DUNDI_IE_SIGNATURE:
781                         if (len == 128) {
782                                 ies->encsig = (unsigned char *)(data + 2);
783                         } else {
784                                 snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted signature length %d\n", len);
785                                 errorf(tmp);
786                         }
787                         break;
788                 case DUNDI_IE_CACHEBYPASS:
789                         ies->cbypass = 1;
790                         break;
791                 default:
792                         snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", dundi_ie2str(ie), ie, len);
793                         outputf(tmp);
794                 }
795                 /* Overwrite information element with 0, to null terminate previous portion */
796                 data[0] = 0;
797                 datalen -= (len + 2);
798                 data += (len + 2);
799         }
800         /* Null-terminate last field */
801         *data = '\0';
802         if (datalen) {
803                 errorf("Invalid information element contents, strange boundary\n");
804                 return -1;
805         }
806         return 0;
807 }