50c45e08da02cbf58723516c38059ae28b89fabb
[asterisk/asterisk.git] / pbx / dundi-parser.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Distributed Universal Number Discovery (DUNDi)
22  *
23  */
24
25 #include "asterisk.h"
26
27 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
28
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32
33 #include "asterisk/frame.h"
34 #include "asterisk/utils.h"
35 #include "asterisk/dundi.h"
36 #include "dundi-parser.h"
37
38 static void internaloutput(const char *str)
39 {
40         fputs(str, stdout);
41 }
42
43 static void internalerror(const char *str)
44 {
45         fprintf(stderr, "WARNING: %s", str);
46 }
47
48 static void (*outputf)(const char *str) = internaloutput;
49 static void (*errorf)(const char *str) = internalerror;
50
51 char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid)
52 {
53         int x;
54         char *os = s;
55         if (maxlen < 13) {
56                 if (s && (maxlen > 0))
57                         *s = '\0';
58         } else {
59                 for (x=0;x<6;x++) {
60                         sprintf(s, "%02X", eid->eid[x]);
61                         s += 2;
62                 }
63         }
64         return os;
65 }
66
67 int dundi_str_short_to_eid(dundi_eid *eid, const char *s)
68 {
69         unsigned int eid_int[6];
70         int x;
71         if (sscanf(s, "%2x%2x%2x%2x%2x%2x", &eid_int[0], &eid_int[1], &eid_int[2],
72                  &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
73                         return -1;
74         for (x = 0; x < 6; x++)
75                 eid->eid[x] = eid_int[x];
76         return 0;
77 }
78
79 int dundi_eid_zero(dundi_eid *eid)
80 {
81         int x;
82         for (x = 0; x < ARRAY_LEN(eid->eid); x++)
83                 if (eid->eid[x]) return 0;
84         return 1;
85 }
86
87 static void dump_string(char *output, int maxlen, void *value, int len)
88 {
89         if (maxlen > len + 1)
90                 maxlen = len + 1;
91
92         snprintf(output, maxlen, "%s", (char *) value);
93 }
94
95 static void dump_cbypass(char *output, int maxlen, void *value, int len)
96 {
97         snprintf(output, maxlen, "Bypass Caches");
98 }
99
100 static void dump_eid(char *output, int maxlen, void *value, int len)
101 {
102         if (len == 6)
103                 ast_eid_to_str(output, maxlen, (dundi_eid *)value);
104         else
105                 snprintf(output, maxlen, "Invalid EID len %d", len);
106 }
107
108 char *dundi_hint2str(char *buf, int bufsiz, int flags)
109 {
110         strcpy(buf, "");
111         buf[bufsiz-1] = '\0';
112         if (flags & DUNDI_HINT_TTL_EXPIRED) {
113                 strncat(buf, "TTLEXPIRED|", bufsiz - strlen(buf) - 1);
114         }
115         if (flags & DUNDI_HINT_DONT_ASK) {
116                 strncat(buf, "DONTASK|", bufsiz - strlen(buf) - 1);
117         }
118         if (flags & DUNDI_HINT_UNAFFECTED) {
119                 strncat(buf, "UNAFFECTED|", bufsiz - strlen(buf) - 1);
120         }
121         /* Get rid of trailing | */
122         if (ast_strlen_zero(buf))
123                 strcpy(buf, "NONE|");
124         buf[strlen(buf)-1] = '\0';
125         return buf;
126 }
127
128 static void dump_hint(char *output, int maxlen, void *value, int len)
129 {
130         char tmp2[256];
131         char tmp3[256];
132         int datalen;
133         struct dundi_hint *hint;
134         if (len < sizeof(*hint)) {
135                 snprintf(output, maxlen, "<invalid contents>");
136                 return;
137         }
138
139         hint = (struct dundi_hint *) value;;
140
141         datalen = len - offsetof(struct dundi_hint, data);
142         if (datalen > sizeof(tmp3) - 1)
143                 datalen = sizeof(tmp3) - 1;
144
145         memcpy(tmp3, hint->data, datalen);
146         tmp3[datalen] = '\0';
147
148         dundi_hint2str(tmp2, sizeof(tmp2), ntohs(hint->flags));
149
150         if (ast_strlen_zero(tmp3))
151                 snprintf(output, maxlen, "[%s]", tmp2);
152         else
153                 snprintf(output, maxlen, "[%s] %s", tmp2, tmp3);
154 }
155
156 static void dump_cause(char *output, int maxlen, void *value, int len)
157 {
158         static char *causes[] = {
159                 "SUCCESS",
160                 "GENERAL",
161                 "DYNAMIC",
162                 "NOAUTH" ,
163                 };
164         char tmp2[256];
165         struct dundi_cause *cause;
166         int datalen;
167         int causecode;
168
169         if (len < sizeof(*cause)) {
170                 snprintf(output, maxlen, "<invalid contents>");
171                 return;
172         }
173
174         cause = (struct dundi_cause*) value;
175         causecode = cause->causecode;
176
177         datalen = len - offsetof(struct dundi_cause, desc);
178         if (datalen > sizeof(tmp2) - 1)
179                 datalen = sizeof(tmp2) - 1;
180
181         memcpy(tmp2, cause->desc, datalen);
182         tmp2[datalen] = '\0';
183
184         if (causecode < ARRAY_LEN(causes)) {
185                 if (ast_strlen_zero(tmp2))
186                         snprintf(output, maxlen, "%s", causes[causecode]);
187                 else
188                         snprintf(output, maxlen, "%s: %s", causes[causecode], tmp2);
189         } else {
190                 if (ast_strlen_zero(tmp2))
191                         snprintf(output, maxlen, "%d", causecode);
192                 else
193                         snprintf(output, maxlen, "%d: %s", causecode, tmp2);
194         }
195 }
196
197 static void dump_int(char *output, int maxlen, void *value, int len)
198 {
199         if (len == (int)sizeof(unsigned int))
200                 snprintf(output, maxlen, "%lu", (unsigned long)ntohl(*((unsigned int *)value)));
201         else
202                 ast_copy_string(output, "Invalid INT", maxlen);
203 }
204
205 static void dump_short(char *output, int maxlen, void *value, int len)
206 {
207         if (len == (int)sizeof(unsigned short))
208                 snprintf(output, maxlen, "%d", ntohs(*((unsigned short *)value)));
209         else
210                 ast_copy_string(output, "Invalid SHORT", maxlen);
211 }
212
213 static void dump_byte(char *output, int maxlen, void *value, int len)
214 {
215         if (len == (int)sizeof(unsigned char))
216                 snprintf(output, maxlen, "%d", *((unsigned char *)value));
217         else
218                 ast_copy_string(output, "Invalid BYTE", maxlen);
219 }
220
221 static char *proto2str(int proto, char *buf, int bufsiz)
222 {       
223         switch(proto) {
224         case DUNDI_PROTO_NONE:
225                 strncpy(buf, "None", bufsiz - 1);
226                 break;
227         case DUNDI_PROTO_IAX:
228                 strncpy(buf, "IAX", bufsiz - 1);
229                 break;
230         case DUNDI_PROTO_SIP:
231                 strncpy(buf, "SIP", bufsiz - 1);
232                 break;
233         case DUNDI_PROTO_H323:
234                 strncpy(buf, "H.323", bufsiz - 1);
235                 break;
236         default:
237                 snprintf(buf, bufsiz, "Unknown Proto(%d)", proto);
238         }
239         buf[bufsiz-1] = '\0';
240         return buf;
241 }
242
243 char *dundi_flags2str(char *buf, int bufsiz, int flags)
244 {
245         strcpy(buf, "");
246         buf[bufsiz-1] = '\0';
247         if (flags & DUNDI_FLAG_EXISTS) {
248                 strncat(buf, "EXISTS|", bufsiz - strlen(buf) - 1);
249         }
250         if (flags & DUNDI_FLAG_MATCHMORE) {
251                 strncat(buf, "MATCHMORE|", bufsiz - strlen(buf) - 1);
252         }
253         if (flags & DUNDI_FLAG_CANMATCH) {
254                 strncat(buf, "CANMATCH|", bufsiz - strlen(buf) - 1);
255         }
256         if (flags & DUNDI_FLAG_IGNOREPAT) {
257                 strncat(buf, "IGNOREPAT|", bufsiz - strlen(buf) - 1);
258         }
259         if (flags & DUNDI_FLAG_RESIDENTIAL) {
260                 strncat(buf, "RESIDENCE|", bufsiz - strlen(buf) - 1);
261         }
262         if (flags & DUNDI_FLAG_COMMERCIAL) {
263                 strncat(buf, "COMMERCIAL|", bufsiz - strlen(buf) - 1);
264         }
265         if (flags & DUNDI_FLAG_MOBILE) {
266                 strncat(buf, "MOBILE", bufsiz - strlen(buf) - 1);
267         }
268         if (flags & DUNDI_FLAG_NOUNSOLICITED) {
269                 strncat(buf, "NOUNSLCTD|", bufsiz - strlen(buf) - 1);
270         }
271         if (flags & DUNDI_FLAG_NOCOMUNSOLICIT) {
272                 strncat(buf, "NOCOMUNSLTD|", bufsiz - strlen(buf) - 1);
273         }
274         /* Get rid of trailing | */
275         if (ast_strlen_zero(buf))
276                 strcpy(buf, "NONE|");
277         buf[strlen(buf)-1] = '\0';
278         return buf;
279 }
280
281 static void dump_answer(char *output, int maxlen, void *value, int len)
282 {
283         struct dundi_answer *answer;
284         char proto[40];
285         char flags[40];
286         char eid_str[40];
287         char tmp[512]="";
288         int datalen;
289
290         if (len < sizeof(*answer)) {
291                 snprintf(output, maxlen, "Invalid Answer");
292                 return;
293         }
294
295         answer = (struct dundi_answer *)(value);
296
297         datalen = len - offsetof(struct dundi_answer, data);
298         if (datalen > sizeof(tmp) - 1)
299                 datalen = sizeof(tmp) - 1;
300
301         memcpy(tmp, answer->data, datalen);
302         tmp[datalen] = '\0';
303
304         ast_eid_to_str(eid_str, sizeof(eid_str), &answer->eid);
305         snprintf(output, maxlen, "[%s] %d <%s/%s> from [%s]", 
306                 dundi_flags2str(flags, sizeof(flags), ntohs(answer->flags)), 
307                 ntohs(answer->weight),
308                 proto2str(answer->protocol, proto, sizeof(proto)), 
309                         tmp, eid_str);
310 }
311
312 static void dump_encrypted(char *output, int maxlen, void *value, int len)
313 {
314         char iv[33];
315         int x;
316         if ((len > 16) && !(len % 16)) {
317                 /* Build up IV */
318                 for (x=0;x<16;x++) {
319                         snprintf(iv + (x << 1), 3, "%02x", ((unsigned char *)value)[x]);
320                 }
321                 snprintf(output, maxlen, "[IV %s] %d encrypted blocks\n", iv, len / 16);
322         } else
323                 snprintf(output, maxlen, "Invalid Encrypted Datalen %d", len);
324 }
325
326 static void dump_raw(char *output, int maxlen, void *value, int len)
327 {
328         int x;
329         unsigned char *u = value;
330         output[maxlen - 1] = '\0';
331         strcpy(output, "[ ");
332         for (x=0;x<len;x++) {
333                 snprintf(output + strlen(output), maxlen - strlen(output) - 1, "%02x ", u[x]);
334         }
335         strncat(output + strlen(output), "]", maxlen - strlen(output) - 1);
336 }
337
338 static struct dundi_ie {
339         int ie;
340         char *name;
341         void (*dump)(char *output, int maxlen, void *value, int len);
342 } 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         if (ntohs(fhi->dtrans) & DUNDI_FLAG_RETRANS)
459                 strcpy(retries, "Yes");
460         else
461                 strcpy(retries, "No");
462         if ((ntohs(fhi->strans) & DUNDI_FLAG_RESERVED)) {
463                 /* Ignore frames with high bit set to 1 */
464                 return;
465         }
466         if ((fhi->cmdresp & 0x3f) > (int)sizeof(commands)/(int)sizeof(char *)) {
467                 snprintf(class2, (int)sizeof(class2), "(%d?)", fhi->cmdresp);
468                 class = class2;
469         } else {
470                 class = commands[(int)(fhi->cmdresp & 0x3f)];
471         }
472         snprintf(subclass2, (int)sizeof(subclass2), "%02x", fhi->cmdflags);
473         subclass = subclass2;
474         snprintf(tmp, (int)sizeof(tmp), 
475                 "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s (%s)\n",
476                 pref[rx],
477                 retries, fhi->oseqno, fhi->iseqno, class, fhi->cmdresp & 0x40 ? "Response" : "Command");
478         outputf(tmp);
479         snprintf(tmp, (int)sizeof(tmp), 
480                 "%s     Flags: %s STrans: %5.5d  DTrans: %5.5d [%s:%d]%s\n", (rx > 1) ? "     " : "",
481                 subclass, ntohs(fhi->strans) & ~DUNDI_FLAG_RESERVED, ntohs(fhi->dtrans) & ~DUNDI_FLAG_RETRANS,
482                 ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
483                 fhi->cmdresp & 0x80 ? " (Final)" : "");
484         outputf(tmp);
485         dump_ies(fhi->ies, rx > 1, datalen);
486 }
487
488 int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen)
489 {
490         char tmp[256];
491         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
492                 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);
493                 errorf(tmp);
494                 return -1;
495         }
496         ied->buf[ied->pos++] = ie;
497         ied->buf[ied->pos++] = datalen;
498         memcpy(ied->buf + ied->pos, data, datalen);
499         ied->pos += datalen;
500         return 0;
501 }
502
503 int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, char *data)
504 {
505         char tmp[256];
506         int datalen = data ? strlen(data) + 1 : 1;
507         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
508                 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);
509                 errorf(tmp);
510                 return -1;
511         }
512         ied->buf[ied->pos++] = ie;
513         ied->buf[ied->pos++] = datalen;
514         ied->buf[ied->pos++] = cause;
515         memcpy(ied->buf + ied->pos, data, datalen-1);
516         ied->pos += datalen-1;
517         return 0;
518 }
519
520 int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, char *data)
521 {
522         char tmp[256];
523         int datalen = data ? strlen(data) + 2 : 2;
524         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
525                 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);
526                 errorf(tmp);
527                 return -1;
528         }
529         ied->buf[ied->pos++] = ie;
530         ied->buf[ied->pos++] = datalen;
531         flags = htons(flags);
532         memcpy(ied->buf + ied->pos, &flags, sizeof(flags));
533         ied->pos += 2;
534         memcpy(ied->buf + ied->pos, data, datalen-1);
535         ied->pos += datalen-2;
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 }