Preliminary "PRECACHE" / push support...
[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
26 static void internaloutput(const char *str)
27 {
28         fputs(str, stdout);
29 }
30
31 static void internalerror(const char *str)
32 {
33         fprintf(stderr, "WARNING: %s", str);
34 }
35
36 static void (*outputf)(const char *str) = internaloutput;
37 static void (*errorf)(const char *str) = internalerror;
38
39 char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid)
40 {
41         int x;
42         char *os = s;
43         if (maxlen < 18) {
44                 if (s && (maxlen > 0))
45                         *s = '\0';
46         } else {
47                 for (x=0;x<5;x++) {
48                         sprintf(s, "%02x:", eid->eid[x]);
49                         s += 3;
50                 }
51                 sprintf(s, "%02x", eid->eid[5]);
52         }
53         return os;
54 }
55
56 char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid)
57 {
58         int x;
59         char *os = s;
60         if (maxlen < 13) {
61                 if (s && (maxlen > 0))
62                         *s = '\0';
63         } else {
64                 for (x=0;x<6;x++) {
65                         sprintf(s, "%02X", eid->eid[x]);
66                         s += 2;
67                 }
68         }
69         return os;
70 }
71
72 int dundi_str_to_eid(dundi_eid *eid, char *s)
73 {
74         unsigned int eid_int[6];
75         int x;
76         if (sscanf(s, "%x:%x:%x:%x:%x:%x", &eid_int[0], &eid_int[1], &eid_int[2],
77                  &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
78                         return -1;
79         for (x=0;x<6;x++)
80                 eid->eid[x] = eid_int[x];
81         return 0;
82 }
83
84 int dundi_str_short_to_eid(dundi_eid *eid, char *s)
85 {
86         unsigned int eid_int[6];
87         int x;
88         if (sscanf(s, "%2x%2x%2x%2x%2x%2x", &eid_int[0], &eid_int[1], &eid_int[2],
89                  &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
90                         return -1;
91         for (x=0;x<6;x++)
92                 eid->eid[x] = eid_int[x];
93         return 0;
94 }
95
96 int dundi_eid_zero(dundi_eid *eid)
97 {
98         int x;
99         for (x=0;x<sizeof(eid->eid) / sizeof(eid->eid[0]);x++)
100                 if (eid->eid[x]) return 0;
101         return 1;
102 }
103
104 int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2)
105 {
106         return memcmp(eid1, eid2, sizeof(dundi_eid));
107 }
108
109 static void dump_string(char *output, int maxlen, void *value, int len)
110 {
111         maxlen--;
112         if (maxlen > len)
113                 maxlen = len;
114         strncpy(output,value, maxlen);
115         output[maxlen] = '\0';
116 }
117
118 static void dump_cbypass(char *output, int maxlen, void *value, int len)
119 {
120         strncpy(output, "Bypass Caches", maxlen);
121         output[maxlen] = '\0';
122 }
123
124 static void dump_eid(char *output, int maxlen, void *value, int len)
125 {
126         if (len == 6)
127                 dundi_eid_to_str(output, maxlen, (dundi_eid *)value);
128         else
129                 snprintf(output, maxlen, "Invalid EID len %d", len);
130 }
131
132 char *dundi_hint2str(char *buf, int bufsiz, int flags)
133 {
134         strcpy(buf, "");
135         buf[bufsiz-1] = '\0';
136         if (flags & DUNDI_HINT_TTL_EXPIRED) {
137                 strncat(buf, "TTLEXPIRED|", bufsiz - strlen(buf) - 1);
138         }
139         if (flags & DUNDI_HINT_DONT_ASK) {
140                 strncat(buf, "DONTASK|", bufsiz - strlen(buf) - 1);
141         }
142         if (flags & DUNDI_HINT_UNAFFECTED) {
143                 strncat(buf, "UNAFFECTED|", bufsiz - strlen(buf) - 1);
144         }
145         /* Get rid of trailing | */
146         if (ast_strlen_zero(buf))
147                 strcpy(buf, "NONE|");
148         buf[strlen(buf)-1] = '\0';
149         return buf;
150 }
151
152 static void dump_hint(char *output, int maxlen, void *value, int len)
153 {
154         unsigned short flags;
155         char tmp[512];
156         char tmp2[256];
157         if (len < 2) {
158                 strncpy(output, "<invalid contents>", maxlen);
159                 return;
160         }
161         memcpy(&flags, value, sizeof(flags));
162         flags = ntohs(flags);
163         memset(tmp, 0, sizeof(tmp));
164         dundi_hint2str(tmp2, sizeof(tmp2), flags);
165         snprintf(tmp, sizeof(tmp), "[%s] ", tmp2);
166         memcpy(tmp + strlen(tmp), value + 2, len - 2);
167         strncpy(output, tmp, maxlen - 1);
168 }
169
170 static void dump_cause(char *output, int maxlen, void *value, int len)
171 {
172         static char *causes[] = {
173                 "SUCCESS",
174                 "GENERAL",
175                 "DYNAMIC",
176                 "NOAUTH" ,
177                 };
178         char tmp[256];
179         char tmp2[256];
180         int mlen;
181         unsigned char cause;
182         if (len < 1) {
183                 strncpy(output, "<invalid contents>", maxlen);
184                 return;
185         }
186         cause = *((unsigned char *)value);
187         memset(tmp2, 0, sizeof(tmp2));
188         mlen = len - 1;
189         if (mlen > 255)
190                 mlen = 255;
191         memcpy(tmp2, value + 1, mlen);
192         if (cause < sizeof(causes) / sizeof(causes[0])) {
193                 if (len > 1)
194                         snprintf(tmp, sizeof(tmp), "%s: %s", causes[cause], tmp2);
195                 else
196                         snprintf(tmp, sizeof(tmp), "%s", causes[cause]);
197         } else {
198                 if (len > 1)
199                         snprintf(tmp, sizeof(tmp), "%d: %s", cause, tmp2);
200                 else
201                         snprintf(tmp, sizeof(tmp), "%d", cause);
202         }
203         
204         strncpy(output,tmp, maxlen);
205         output[maxlen] = '\0';
206 }
207
208 static void dump_int(char *output, int maxlen, void *value, int len)
209 {
210         if (len == (int)sizeof(unsigned int))
211                 snprintf(output, maxlen, "%lu", (unsigned long)ntohl(*((unsigned int *)value)));
212         else
213                 snprintf(output, maxlen, "Invalid INT");
214 }
215
216 static void dump_short(char *output, int maxlen, void *value, int len)
217 {
218         if (len == (int)sizeof(unsigned short))
219                 snprintf(output, maxlen, "%d", ntohs(*((unsigned short *)value)));
220         else
221                 snprintf(output, maxlen, "Invalid SHORT");
222 }
223
224 static void dump_byte(char *output, int maxlen, void *value, int len)
225 {
226         if (len == (int)sizeof(unsigned char))
227                 snprintf(output, maxlen, "%d", *((unsigned char *)value));
228         else
229                 snprintf(output, maxlen, "Invalid BYTE");
230 }
231
232 static char *proto2str(int proto, char *buf, int bufsiz)
233 {       
234         switch(proto) {
235         case DUNDI_PROTO_NONE:
236                 strncpy(buf, "None", bufsiz - 1);
237                 break;
238         case DUNDI_PROTO_IAX:
239                 strncpy(buf, "IAX", bufsiz - 1);
240                 break;
241         case DUNDI_PROTO_SIP:
242                 strncpy(buf, "SIP", bufsiz - 1);
243                 break;
244         case DUNDI_PROTO_H323:
245                 strncpy(buf, "H.323", bufsiz - 1);
246                 break;
247         default:
248                 snprintf(buf, bufsiz, "Unknown Proto(%d)", proto);
249         }
250         buf[bufsiz-1] = '\0';
251         return buf;
252 }
253
254 char *dundi_flags2str(char *buf, int bufsiz, int flags)
255 {
256         strcpy(buf, "");
257         buf[bufsiz-1] = '\0';
258         if (flags & DUNDI_FLAG_EXISTS) {
259                 strncat(buf, "EXISTS|", bufsiz - strlen(buf) - 1);
260         }
261         if (flags & DUNDI_FLAG_MATCHMORE) {
262                 strncat(buf, "MATCHMORE|", bufsiz - strlen(buf) - 1);
263         }
264         if (flags & DUNDI_FLAG_CANMATCH) {
265                 strncat(buf, "CANMATCH|", bufsiz - strlen(buf) - 1);
266         }
267         if (flags & DUNDI_FLAG_IGNOREPAT) {
268                 strncat(buf, "IGNOREPAT|", bufsiz - strlen(buf) - 1);
269         }
270         if (flags & DUNDI_FLAG_RESIDENTIAL) {
271                 strncat(buf, "RESIDENCE|", bufsiz - strlen(buf) - 1);
272         }
273         if (flags & DUNDI_FLAG_COMMERCIAL) {
274                 strncat(buf, "COMMERCIAL|", bufsiz - strlen(buf) - 1);
275         }
276         if (flags & DUNDI_FLAG_MOBILE) {
277                 strncat(buf, "MOBILE", bufsiz - strlen(buf) - 1);
278         }
279         if (flags & DUNDI_FLAG_NOUNSOLICITED) {
280                 strncat(buf, "NOUNSLCTD|", bufsiz - strlen(buf) - 1);
281         }
282         if (flags & DUNDI_FLAG_NOCOMUNSOLICIT) {
283                 strncat(buf, "NOCOMUNSLTD|", bufsiz - strlen(buf) - 1);
284         }
285         /* Get rid of trailing | */
286         if (ast_strlen_zero(buf))
287                 strcpy(buf, "NONE|");
288         buf[strlen(buf)-1] = '\0';
289         return buf;
290 }
291
292 static void dump_answer(char *output, int maxlen, void *value, int len)
293 {
294         struct dundi_answer *answer;
295         char proto[40];
296         char flags[40];
297         char eid_str[40];
298         char tmp[512]="";
299         if (len >= 10) {
300                 answer = (struct dundi_answer *)(value);
301                 memcpy(tmp, answer->data, (len >= 500) ? 500 : len - 10);
302                 dundi_eid_to_str(eid_str, sizeof(eid_str), &answer->eid);
303                 snprintf(output, maxlen, "[%s] %d <%s/%s> from [%s]", 
304                         dundi_flags2str(flags, sizeof(flags), ntohs(answer->flags)), 
305                         ntohs(answer->weight),
306                         proto2str(answer->protocol, proto, sizeof(proto)), 
307                                 tmp, eid_str);
308         } else
309                 strncpy(output, "Invalid Answer", maxlen - 1);
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 } ies[] = {
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<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
374                 if (ies[x].ie == ie)
375                         return ies[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<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
403                         if (ies[x].ie == ie) {
404                                 if (ies[x].dump) {
405                                         ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen);
406                                         snprintf(tmp, (int)sizeof(tmp), "   %s%-15.15s : %s\n", (spaces ? "     " : "" ), ies[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 ? "     " : "" ), ies[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         char retries[20];
458         char iabuf[INET_ADDRSTRLEN];
459         if (ntohs(fhi->dtrans) & DUNDI_FLAG_RETRANS)
460                 strcpy(retries, "Yes");
461         else
462                 strcpy(retries, "No");
463         if ((ntohs(fhi->strans) & DUNDI_FLAG_RESERVED)) {
464                 /* Ignore frames with high bit set to 1 */
465                 return;
466         }
467         if ((fhi->cmdresp & 0x3f) > (int)sizeof(commands)/(int)sizeof(char *)) {
468                 snprintf(class2, (int)sizeof(class2), "(%d?)", fhi->cmdresp);
469                 class = class2;
470         } else {
471                 class = commands[(int)(fhi->cmdresp & 0x3f)];
472         }
473         snprintf(subclass2, (int)sizeof(subclass2), "%02x", fhi->cmdflags);
474         subclass = subclass2;
475         snprintf(tmp, (int)sizeof(tmp), 
476                 "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s (%s)\n",
477                 pref[rx],
478                 retries, fhi->oseqno, fhi->iseqno, class, fhi->cmdresp & 0x40 ? "Response" : "Command");
479         outputf(tmp);
480         snprintf(tmp, (int)sizeof(tmp), 
481                 "%s     Flags: %s STrans: %5.5d  DTrans: %5.5d [%s:%d]%s\n", (rx > 1) ? "     " : "",
482                 subclass, ntohs(fhi->strans) & ~DUNDI_FLAG_RESERVED, ntohs(fhi->dtrans) & ~DUNDI_FLAG_RETRANS,
483                 ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port),
484                 fhi->cmdresp & 0x80 ? " (Final)" : "");
485         outputf(tmp);
486         dump_ies(fhi->ies, rx > 1, datalen);
487 }
488
489 int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen)
490 {
491         char tmp[256];
492         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
493                 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);
494                 errorf(tmp);
495                 return -1;
496         }
497         ied->buf[ied->pos++] = ie;
498         ied->buf[ied->pos++] = datalen;
499         memcpy(ied->buf + ied->pos, data, datalen);
500         ied->pos += datalen;
501         return 0;
502 }
503
504 int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, unsigned char *data)
505 {
506         char tmp[256];
507         int datalen = data ? strlen(data) + 1 : 1;
508         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
509                 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);
510                 errorf(tmp);
511                 return -1;
512         }
513         ied->buf[ied->pos++] = ie;
514         ied->buf[ied->pos++] = datalen;
515         ied->buf[ied->pos++] = cause;
516         memcpy(ied->buf + ied->pos, data, datalen-1);
517         ied->pos += datalen-1;
518         return 0;
519 }
520
521 int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, unsigned char *data)
522 {
523         char tmp[256];
524         int datalen = data ? strlen(data) + 2 : 2;
525         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
526                 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);
527                 errorf(tmp);
528                 return -1;
529         }
530         ied->buf[ied->pos++] = ie;
531         ied->buf[ied->pos++] = datalen;
532         flags = htons(flags);
533         memcpy(ied->buf + ied->pos, &flags, sizeof(flags));
534         ied->pos += 2;
535         memcpy(ied->buf + ied->pos, data, datalen-1);
536         ied->pos += datalen-2;
537         return 0;
538 }
539
540 int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen)
541 {
542         char tmp[256];
543         datalen += 16;
544         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
545                 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);
546                 errorf(tmp);
547                 return -1;
548         }
549         ied->buf[ied->pos++] = ie;
550         ied->buf[ied->pos++] = datalen;
551         memcpy(ied->buf + ied->pos, iv, 16);
552         ied->pos += 16;
553         if (data) {
554                 memcpy(ied->buf + ied->pos, data, datalen-16);
555                 ied->pos += datalen-16;
556         }
557         return 0;
558 }
559
560 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)
561 {
562         char tmp[256];
563         int datalen = data ? strlen(data) + 11 : 11;
564         int x;
565         unsigned short myw;
566         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
567                 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);
568                 errorf(tmp);
569                 return -1;
570         }
571         ied->buf[ied->pos++] = ie;
572         ied->buf[ied->pos++] = datalen;
573         for (x=0;x<6;x++)
574                 ied->buf[ied->pos++] = eid->eid[x];
575         ied->buf[ied->pos++] = protocol;
576         myw = htons(flags);
577         memcpy(ied->buf + ied->pos, &myw, 2);
578         ied->pos += 2;
579         myw = htons(weight);
580         memcpy(ied->buf + ied->pos, &myw, 2);
581         ied->pos += 2;
582         memcpy(ied->buf + ied->pos, data, datalen-11);
583         ied->pos += datalen-11;
584         return 0;
585 }
586
587 int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin)
588 {
589         return dundi_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in));
590 }
591
592 int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value) 
593 {
594         unsigned int newval;
595         newval = htonl(value);
596         return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
597 }
598
599 int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value) 
600 {
601         unsigned short newval;
602         newval = htons(value);
603         return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
604 }
605
606 int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, unsigned char *str)
607 {
608         return dundi_ie_append_raw(ied, ie, str, strlen(str));
609 }
610
611 int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid)
612 {
613         return dundi_ie_append_raw(ied, ie, (unsigned char *)eid, sizeof(dundi_eid));
614 }
615
616 int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat)
617 {
618         return dundi_ie_append_raw(ied, ie, &dat, 1);
619 }
620
621 int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie) 
622 {
623         return dundi_ie_append_raw(ied, ie, NULL, 0);
624 }
625
626 void dundi_set_output(void (*func)(const char *))
627 {
628         outputf = func;
629 }
630
631 void dundi_set_error(void (*func)(const char *))
632 {
633         errorf = func;
634 }
635
636 int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen)
637 {
638         /* Parse data into information elements */
639         int len;
640         int ie;
641         char tmp[256];
642         memset(ies, 0, (int)sizeof(struct dundi_ies));
643         ies->ttl = -1;
644         ies->expiration = -1;
645         ies->unknowncmd = -1;
646         ies->cause = -1;
647         while(datalen >= 2) {
648                 ie = data[0];
649                 len = data[1];
650                 if (len > datalen - 2) {
651                         errorf("Information element length exceeds message size\n");
652                         return -1;
653                 }
654                 switch(ie) {
655                 case DUNDI_IE_EID:
656                 case DUNDI_IE_EID_DIRECT:
657                         if (len != (int)sizeof(dundi_eid)) {
658                                 errorf("Improper entity identifer, expecting 6 bytes!\n");
659                         } else if (ies->eidcount < DUNDI_MAX_STACK) {
660                                 ies->eids[ies->eidcount] = (dundi_eid *)(data + 2);
661                                 ies->eid_direct[ies->eidcount] = (ie == DUNDI_IE_EID_DIRECT);
662                                 ies->eidcount++;
663                         } else
664                                 errorf("Too many entities in stack!\n");
665                         break;
666                 case DUNDI_IE_REQEID:
667                         if (len != (int)sizeof(dundi_eid)) {
668                                 errorf("Improper requested entity identifer, expecting 6 bytes!\n");
669                         } else
670                                 ies->reqeid = (dundi_eid *)(data + 2);
671                         break;
672                 case DUNDI_IE_CALLED_CONTEXT:
673                         ies->called_context = data + 2;
674                         break;
675                 case DUNDI_IE_CALLED_NUMBER:
676                         ies->called_number = data + 2;
677                         break;
678                 case DUNDI_IE_ANSWER:
679                         if (len < sizeof(struct dundi_answer)) {
680                                 snprintf(tmp, (int)sizeof(tmp), "Answer expected to be >=%d bytes long but was %d\n", (int)sizeof(struct dundi_answer), len);
681                                 errorf(tmp);
682                         } else {
683                                 if (ies->anscount < DUNDI_MAX_ANSWERS)
684                                         ies->answers[ies->anscount++]= (struct dundi_answer *)(data + 2);
685                                 else 
686                                         errorf("Ignoring extra answers!\n");
687                         }
688                         break;
689                 case DUNDI_IE_TTL:
690                         if (len != (int)sizeof(unsigned short)) {
691                                 snprintf(tmp, (int)sizeof(tmp), "Expecting ttl to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
692                                 errorf(tmp);
693                         } else
694                                 ies->ttl = ntohs(*((unsigned short *)(data + 2)));
695                         break;
696                 case DUNDI_IE_VERSION:
697                         if (len != (int)sizeof(unsigned short)) {
698                                 snprintf(tmp, (int)sizeof(tmp),  "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
699                                 errorf(tmp);
700                         } else
701                                 ies->version = ntohs(*((unsigned short *)(data + 2)));
702                         break;
703                 case DUNDI_IE_EXPIRATION:
704                         if (len != (int)sizeof(unsigned short)) {
705                                 snprintf(tmp, (int)sizeof(tmp),  "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
706                                 errorf(tmp);
707                         } else
708                                 ies->expiration = ntohs(*((unsigned short *)(data + 2)));
709                         break;
710                 case DUNDI_IE_KEYCRC32:
711                         if (len != (int)sizeof(unsigned int)) {
712                                 snprintf(tmp, (int)sizeof(tmp),  "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
713                                 errorf(tmp);
714                         } else
715                                 ies->keycrc32 = ntohl(*((unsigned int *)(data + 2)));
716                         break;
717                 case DUNDI_IE_UNKNOWN:
718                         if (len == 1)
719                                 ies->unknowncmd = data[2];
720                         else {
721                                 snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len);
722                                 errorf(tmp);
723                         }
724                         break;
725                 case DUNDI_IE_CAUSE:
726                         if (len >= 1) {
727                                 ies->cause = data[2];
728                                 ies->causestr = data + 3;
729                         } else {
730                                 snprintf(tmp, (int)sizeof(tmp), "Expected at least one byte cause, but was %d long\n", len);
731                                 errorf(tmp);
732                         }
733                         break;
734                 case DUNDI_IE_HINT:
735                         if (len >= 2) {
736                                 ies->hint = (struct dundi_hint *)(data + 2);
737                         } else {
738                                 snprintf(tmp, (int)sizeof(tmp), "Expected at least two byte hint, but was %d long\n", len);
739                                 errorf(tmp);
740                         }
741                         break;
742                 case DUNDI_IE_DEPARTMENT:
743                         ies->q_dept = data + 2;
744                         break;
745                 case DUNDI_IE_ORGANIZATION:
746                         ies->q_org = data + 2;
747                         break;
748                 case DUNDI_IE_LOCALITY:
749                         ies->q_locality = data + 2;
750                         break;
751                 case DUNDI_IE_STATE_PROV:
752                         ies->q_stateprov = data + 2;
753                         break;
754                 case DUNDI_IE_COUNTRY:
755                         ies->q_country = data + 2;
756                         break;
757                 case DUNDI_IE_EMAIL:
758                         ies->q_email = data + 2;
759                         break;
760                 case DUNDI_IE_PHONE:
761                         ies->q_phone = data + 2;
762                         break;
763                 case DUNDI_IE_IPADDR:
764                         ies->q_ipaddr = data + 2;
765                         break;
766                 case DUNDI_IE_ENCDATA:
767                         /* Recalculate len as the remainder of the message, regardless of
768                            theoretical length */
769                         len = datalen - 2;
770                         if ((len > 16) && !(len % 16)) {
771                                 ies->encblock = (struct dundi_encblock *)(data + 2);
772                                 ies->enclen = len - 16;
773                         } else {
774                                 snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted data length %d\n", len);
775                                 errorf(tmp);
776                         }
777                         break;
778                 case DUNDI_IE_SHAREDKEY:
779                         if (len == 128) {
780                                 ies->encsharedkey = (unsigned char *)(data + 2);
781                         } else {
782                                 snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted shared key length %d\n", len);
783                                 errorf(tmp);
784                         }
785                         break;
786                 case DUNDI_IE_SIGNATURE:
787                         if (len == 128) {
788                                 ies->encsig = (unsigned char *)(data + 2);
789                         } else {
790                                 snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted signature length %d\n", len);
791                                 errorf(tmp);
792                         }
793                         break;
794                 case DUNDI_IE_CACHEBYPASS:
795                         ies->cbypass = 1;
796                         break;
797                 default:
798                         snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", dundi_ie2str(ie), ie, len);
799                         outputf(tmp);
800                 }
801                 /* Overwrite information element with 0, to null terminate previous portion */
802                 data[0] = 0;
803                 datalen -= (len + 2);
804                 data += (len + 2);
805         }
806         /* Null-terminate last field */
807         *data = '\0';
808         if (datalen) {
809                 errorf("Invalid information element contents, strange boundary\n");
810                 return -1;
811         }
812         return 0;
813 }