Tue Mar 18 07:00:01 CET 2003
[asterisk/asterisk.git] / channels / iax2-parser.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Implementation of Inter-Asterisk eXchange
5  * 
6  * Copyright (C) 2003, Digium
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <sys/socket.h>
15 #include <string.h>
16 #include <netinet/in.h>
17 #include <asterisk/frame.h>
18 #include <arpa/inet.h>
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include "iax2.h"
23 #include "iax2-parser.h"
24
25
26 static void internaloutput(const char *str)
27 {
28         printf(str);
29 }
30
31 static void internalerror(const char *str)
32 {
33         fprintf(stderr, "WARNING: %s", str);
34 }
35
36 static void (*outputf)(const char *str) = internaloutput;
37 static void (*errorf)(const char *str) = internalerror;
38
39 static void dump_addr(char *output, int maxlen, void *value, int len)
40 {
41         struct sockaddr_in sin;
42         if (len == sizeof(sin)) {
43                 memcpy(&sin, value, len);
44                 snprintf(output, maxlen, "IPV4 %s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
45         } else {
46                 snprintf(output, maxlen, "Invalid Address");
47         }
48 }
49
50 static void dump_string(char *output, int maxlen, void *value, int len)
51 {
52         maxlen--;
53         if (maxlen > len)
54                 maxlen = len;
55         strncpy(output,value, maxlen);
56         output[maxlen] = '\0';
57 }
58
59 static void dump_int(char *output, int maxlen, void *value, int len)
60 {
61         if (len == sizeof(unsigned int))
62                 snprintf(output, maxlen, "%d", ntohl(*((unsigned int *)value)));
63         else
64                 snprintf(output, maxlen, "Invalid INT");
65 }
66
67 static void dump_short(char *output, int maxlen, void *value, int len)
68 {
69         if (len == sizeof(unsigned short))
70                 snprintf(output, maxlen, "%d", ntohs(*((unsigned short *)value)));
71         else
72                 snprintf(output, maxlen, "Invalid SHORT");
73 }
74
75 static void dump_byte(char *output, int maxlen, void *value, int len)
76 {
77         if (len == sizeof(unsigned char))
78                 snprintf(output, maxlen, "%d", ntohs(*((unsigned char *)value)));
79         else
80                 snprintf(output, maxlen, "Invalid BYTE");
81 }
82
83 static struct iax2_ie {
84         int ie;
85         char *name;
86         void (*dump)(char *output, int maxlen, void *value, int len);
87 } ies[] = {
88         { IAX_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string },
89         { IAX_IE_CALLING_NUMBER, "CALLING NUMBER", dump_string },
90         { IAX_IE_CALLING_NUMBER, "ANI", dump_string },
91         { IAX_IE_CALLING_NAME, "CALLING NAME", dump_string },
92         { IAX_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string },
93         { IAX_IE_USERNAME, "USERNAME", dump_string },
94         { IAX_IE_PASSWORD, "PASSWORD", dump_string },
95         { IAX_IE_CAPABILITY, "CAPABILITY", dump_int },
96         { IAX_IE_FORMAT, "FORMAT", dump_int },
97         { IAX_IE_LANGUAGE, "LANGUAGE", dump_string },
98         { IAX_IE_VERSION, "VERSION", dump_short },
99         { IAX_IE_ADSICPE, "ADSICPE", dump_short },
100         { IAX_IE_DNID, "DNID", dump_string },
101         { IAX_IE_AUTHMETHODS, "AUTHMETHODS", dump_short },
102         { IAX_IE_CHALLENGE, "CHALLENGE", dump_string },
103         { IAX_IE_MD5_RESULT, "MD5 RESULT", dump_string },
104         { IAX_IE_RSA_RESULT, "RSA RESULT", dump_string },
105         { IAX_IE_APPARENT_ADDR, "APPARENT ADDRESS", dump_addr },
106         { IAX_IE_REFRESH, "REFRESH", dump_short },
107         { IAX_IE_DPSTATUS, "DIALPLAN STATUS", dump_short },
108         { IAX_IE_CALLNO, "CALL NUMBER", dump_short },
109         { IAX_IE_CAUSE, "CAUSE", dump_string },
110         { IAX_IE_IAX_UNKNOWN, "UNKNOWN IAX CMD", dump_byte },
111         { IAX_IE_MSGCOUNT, "MESSAGE COUNT", dump_short },
112         { IAX_IE_AUTOANSWER, "AUTO ANSWER REQ" },
113 };
114
115 const char *iax_ie2str(int ie)
116 {
117         int x;
118         for (x=0;x<sizeof(ies) / sizeof(ies[0]); x++) {
119                 if (ies[x].ie == ie)
120                         return ies[x].name;
121         }
122         return "Unknown IE";
123 }
124
125 static void dump_ies(unsigned char *iedata, int len)
126 {
127         int ielen;
128         int ie;
129         int x;
130         int found;
131         char interp[80];
132         char tmp[256];
133         if (len < 2)
134                 return;
135         while(len > 2) {
136                 ie = iedata[0];
137                 ielen = iedata[1];
138                 if (ielen + 2> len) {
139                         snprintf(tmp, sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len);
140                         outputf(tmp);
141                         return;
142                 }
143                 found = 0;
144                 for (x=0;x<sizeof(ies) / sizeof(ies[0]); x++) {
145                         if (ies[x].ie == ie) {
146                                 if (ies[x].dump) {
147                                         ies[x].dump(interp, sizeof(interp), iedata + 2, ielen);
148                                         snprintf(tmp, sizeof(tmp), "   %-15.15s : %s\n", ies[x].name, interp);
149                                         outputf(tmp);
150                                 } else {
151                                         snprintf(tmp, sizeof(tmp), "   %-15.15s : Present\n", ies[x].name);
152                                         outputf(tmp);
153                                 }
154                                 found++;
155                         }
156                 }
157                 if (!found) {
158                         snprintf(tmp, sizeof(tmp), "   Unknown IE %03d  : Present\n", ie);
159                         outputf(tmp);
160                 }
161                 iedata += (2 + ielen);
162                 len -= (2 + ielen);
163         }
164         outputf("\n");
165 }
166
167 void iax_showframe(struct ast_iax2_frame *f, struct ast_iax2_full_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen)
168 {
169         char *frames[] = {
170                 "(0?)",
171                 "DTMF   ",
172                 "VOICE  ",
173                 "VIDEO  ",
174                 "CONTROL",
175                 "NULL   ",
176                 "IAX    ",
177                 "TEXT   ",
178                 "IMAGE  " };
179         char *iaxs[] = {
180                 "(0?)",
181                 "NEW    ",
182                 "PING   ",
183                 "PONG   ",
184                 "ACK    ",
185                 "HANGUP ",
186                 "REJECT ",
187                 "ACCEPT ",
188                 "AUTHREQ",
189                 "AUTHREP",
190                 "INVAL  ",
191                 "LAGRQ  ",
192                 "LAGRP  ",
193                 "REGREQ ",
194                 "REGAUTH",
195                 "REGACK ",
196                 "REGREJ ",
197                 "REGREL ",
198                 "VNAK   ",
199                 "DPREQ  ",
200                 "DPREP  ",
201                 "DIAL   ",
202                 "TXREQ  ",
203                 "TXCNT  ",
204                 "TXACC  ",
205                 "TXREADY",
206                 "TXREL  ",
207                 "TXREJ  ",
208                 "QUELCH ",
209                 "UNQULCH",
210                 "POKE",
211                 "PAGE",
212                 "MWI",
213                 "UNSUPPORTED",
214         };
215         char *cmds[] = {
216                 "(0?)",
217                 "HANGUP ",
218                 "RING   ",
219                 "RINGING",
220                 "ANSWER ",
221                 "BUSY   ",
222                 "TKOFFHK ",
223                 "OFFHOOK" };
224         struct ast_iax2_full_hdr *fh;
225         char retries[20];
226         char class2[20];
227         char subclass2[20];
228         char *class;
229         char *subclass;
230         char tmp[256];
231         if (f) {
232                 fh = f->data;
233                 snprintf(retries, sizeof(retries), "%03d", f->retries);
234         } else {
235                 fh = fhi;
236                 if (ntohs(fh->dcallno) & IAX_FLAG_RETRANS)
237                         strcpy(retries, "Yes");
238                 else
239                         strcpy(retries, "No");
240         }
241         if (!(ntohs(fh->scallno) & IAX_FLAG_FULL)) {
242                 /* Don't mess with mini-frames */
243                 return;
244         }
245         if (fh->type > sizeof(frames)/sizeof(char *)) {
246                 snprintf(class2, sizeof(class2), "(%d?)", fh->type);
247                 class = class2;
248         } else {
249                 class = frames[(int)fh->type];
250         }
251         if (fh->type == AST_FRAME_DTMF) {
252                 sprintf(subclass2, "%c", fh->csub);
253                 subclass = subclass2;
254         } else if (fh->type == AST_FRAME_IAX) {
255                 if (fh->csub >= sizeof(iaxs)/sizeof(iaxs[0])) {
256                         snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->csub);
257                         subclass = subclass2;
258                 } else {
259                         subclass = iaxs[(int)fh->csub];
260                 }
261         } else if (fh->type == AST_FRAME_CONTROL) {
262                 if (fh->csub > sizeof(cmds)/sizeof(char *)) {
263                         snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->csub);
264                         subclass = subclass2;
265                 } else {
266                         subclass = cmds[(int)fh->csub];
267                 }
268         } else {
269                 snprintf(subclass2, sizeof(subclass2), "%d", fh->csub);
270                 subclass = subclass2;
271         }
272 snprintf(tmp, sizeof(tmp), 
273 "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s Subclass: %s\n",
274         (rx ? "Rx" : "Tx"),
275         retries, fh->oseqno, fh->iseqno, class, subclass);
276         outputf(tmp);
277 snprintf(tmp, sizeof(tmp), 
278 "   Timestamp: %05dms  SCall: %5.5d  DCall: %5.5d [%s:%d]\n",
279         ntohl(fh->ts),
280         ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS,
281                 inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
282         outputf(tmp);
283         if (fh->type == AST_FRAME_IAX)
284                 dump_ies(fh->iedata, datalen);
285 }
286
287 int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, void *data, int datalen)
288 {
289         char tmp[256];
290         if (datalen > (sizeof(ied->buf) - ied->pos)) {
291                 snprintf(tmp, sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", iax_ie2str(ie), ie, datalen, sizeof(ied->buf) - ied->pos);
292                 errorf(tmp);
293                 return -1;
294         }
295         ied->buf[ied->pos++] = ie;
296         ied->buf[ied->pos++] = datalen;
297         memcpy(ied->buf + ied->pos, data, datalen);
298         ied->pos += datalen;
299         return 0;
300 }
301
302 int iax_ie_append_addr(struct iax_ie_data *ied, unsigned char ie, struct sockaddr_in *sin)
303 {
304         return iax_ie_append_raw(ied, ie, sin, sizeof(struct sockaddr_in));
305 }
306
307 int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value) 
308 {
309         unsigned int newval;
310         newval = htonl(value);
311         return iax_ie_append_raw(ied, ie, &newval, sizeof(newval));
312 }
313
314 int iax_ie_append_short(struct iax_ie_data *ied, unsigned char ie, unsigned short value) 
315 {
316         unsigned short newval;
317         newval = htons(value);
318         return iax_ie_append_raw(ied, ie, &newval, sizeof(newval));
319 }
320
321 int iax_ie_append_str(struct iax_ie_data *ied, unsigned char ie, unsigned char *str)
322 {
323         return iax_ie_append_raw(ied, ie, str, strlen(str));
324 }
325
326 int iax_ie_append_byte(struct iax_ie_data *ied, unsigned char ie, unsigned char dat)
327 {
328         return iax_ie_append_raw(ied, ie, &dat, 1);
329 }
330
331 int iax_ie_append(struct iax_ie_data *ied, unsigned char ie) 
332 {
333         return iax_ie_append_raw(ied, ie, NULL, 0);
334 }
335
336 void iax_set_output(void (*func)(const char *))
337 {
338         outputf = func;
339 }
340
341 void iax_set_error(void (*func)(const char *))
342 {
343         errorf = func;
344 }
345
346 int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
347 {
348         /* Parse data into information elements */
349         int len;
350         int ie;
351         char tmp[256];
352         memset(ies, 0, sizeof(struct iax_ies));
353         ies->msgcount = -1;
354         while(datalen >= 2) {
355                 ie = data[0];
356                 len = data[1];
357                 if (len > datalen - 2) {
358                         errorf("Information element length exceeds message size\n");
359                         return -1;
360                 }
361                 switch(ie) {
362                 case IAX_IE_CALLED_NUMBER:
363                         ies->called_number = data + 2;
364                         break;
365                 case IAX_IE_CALLING_NUMBER:
366                         ies->calling_number = data + 2;
367                         break;
368                 case IAX_IE_CALLING_ANI:
369                         ies->calling_ani = data + 2;
370                         break;
371                 case IAX_IE_CALLING_NAME:
372                         ies->calling_name = data + 2;
373                         break;
374                 case IAX_IE_CALLED_CONTEXT:
375                         ies->called_context = data + 2;
376                         break;
377                 case IAX_IE_USERNAME:
378                         ies->username = data + 2;
379                         break;
380                 case IAX_IE_PASSWORD:
381                         ies->password = data + 2;
382                         break;
383                 case IAX_IE_CAPABILITY:
384                         if (len != sizeof(unsigned int)) {
385                                 snprintf(tmp, sizeof(tmp), "Expecting capability to be %d bytes long but was %d\n", sizeof(unsigned int), len);
386                                 errorf(tmp);
387                         } else
388                                 ies->capability = ntohl(*((unsigned int *)(data + 2)));
389                         break;
390                 case IAX_IE_FORMAT:
391                         if (len != sizeof(unsigned int)) {
392                                 snprintf(tmp, sizeof(tmp), "Expecting format to be %d bytes long but was %d\n", sizeof(unsigned int), len);
393                                 errorf(tmp);
394                         } else
395                                 ies->format = ntohl(*((unsigned int *)(data + 2)));
396                         break;
397                 case IAX_IE_LANGUAGE:
398                         ies->language = data + 2;
399                         break;
400                 case IAX_IE_VERSION:
401                         if (len != sizeof(unsigned short)) {
402                                 snprintf(tmp, sizeof(tmp),  "Expecting version to be %d bytes long but was %d\n", sizeof(unsigned short), len);
403                                 errorf(tmp);
404                         } else
405                                 ies->version = ntohs(*((unsigned short *)(data + 2)));
406                         break;
407                 case IAX_IE_ADSICPE:
408                         if (len != sizeof(unsigned short)) {
409                                 snprintf(tmp, sizeof(tmp), "Expecting adsicpe to be %d bytes long but was %d\n", sizeof(unsigned short), len);
410                                 errorf(tmp);
411                         } else
412                                 ies->adsicpe = ntohs(*((unsigned short *)(data + 2)));
413                         break;
414                 case IAX_IE_DNID:
415                         ies->dnid = data + 2;
416                         break;
417                 case IAX_IE_AUTHMETHODS:
418                         if (len != sizeof(unsigned short))  {
419                                 snprintf(tmp, sizeof(tmp), "Expecting authmethods to be %d bytes long but was %d\n", sizeof(unsigned short), len);
420                                 errorf(tmp);
421                         } else
422                                 ies->authmethods = ntohs(*((unsigned short *)(data + 2)));
423                         break;
424                 case IAX_IE_CHALLENGE:
425                         ies->challenge = data + 2;
426                         break;
427                 case IAX_IE_MD5_RESULT:
428                         ies->md5_result = data + 2;
429                         break;
430                 case IAX_IE_RSA_RESULT:
431                         ies->rsa_result = data + 2;
432                         break;
433                 case IAX_IE_APPARENT_ADDR:
434                         ies->apparent_addr = ((struct sockaddr_in *)(data + 2));
435                         break;
436                 case IAX_IE_REFRESH:
437                         if (len != sizeof(unsigned short)) {
438                                 snprintf(tmp, sizeof(tmp),  "Expecting refresh to be %d bytes long but was %d\n", sizeof(unsigned short), len);
439                                 errorf(tmp);
440                         } else
441                                 ies->refresh = ntohs(*((unsigned short *)(data + 2)));
442                         break;
443                 case IAX_IE_DPSTATUS:
444                         if (len != sizeof(unsigned short)) {
445                                 snprintf(tmp, sizeof(tmp),  "Expecting dpstatus to be %d bytes long but was %d\n", sizeof(unsigned short), len);
446                                 errorf(tmp);
447                         } else
448                                 ies->dpstatus = ntohs(*((unsigned short *)(data + 2)));
449                         break;
450                 case IAX_IE_CALLNO:
451                         if (len != sizeof(unsigned short)) {
452                                 snprintf(tmp, sizeof(tmp),  "Expecting callno to be %d bytes long but was %d\n", sizeof(unsigned short), len);
453                                 errorf(tmp);
454                         } else
455                                 ies->callno = ntohs(*((unsigned short *)(data + 2)));
456                         break;
457                 case IAX_IE_CAUSE:
458                         ies->cause = data + 2;
459                         break;
460                 case IAX_IE_IAX_UNKNOWN:
461                         if (len == 1)
462                                 ies->iax_unknown = data[2];
463                         else {
464                                 snprintf(tmp, sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len);
465                                 errorf(tmp);
466                         }
467                         break;
468                 case IAX_IE_MSGCOUNT:
469                         if (len != sizeof(unsigned short)) {
470                                 snprintf(tmp, sizeof(tmp), "Expecting msgcount to be %d bytes long but was %d\n", sizeof(unsigned short), len);
471                                 errorf(tmp);
472                         } else
473                                 ies->msgcount = ntohs(*((unsigned short *)(data + 2))); 
474                         break;
475                 case IAX_IE_AUTOANSWER:
476                         ies->autoanswer = 1;
477                         break;
478                 case IAX_IE_MUSICONHOLD:
479                         ies->musiconhold = 1;
480                         break;
481                 default:
482                         snprintf(tmp, sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len);
483                         errorf(tmp);
484                 }
485                 /* Overwrite information element with 0, to null terminate previous portion */
486                 data[0] = 0;
487                 datalen -= (len + 2);
488                 data += (len + 2);
489         }
490         /* Null-terminate last field */
491         *data = '\0';
492         if (datalen) {
493                 errorf("Invalid information element contents, strange boundary\n");
494                 return -1;
495         }
496         return 0;
497 }
498