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