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