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