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