782a4097e11d5f4f137e52cad2a74debd19950c8
[asterisk/asterisk.git] / channels / iax2-provision.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, 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 IAX Provisioning Protocol 
22  *
23  * \author Mark Spencer <markster@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdio.h>
34 #include <netdb.h>
35 #include <netinet/in.h>
36 #include <netinet/in_systm.h>
37 #include <netinet/ip.h>
38 #include <sys/socket.h>
39
40 #include "asterisk/config.h"
41 #include "asterisk/logger.h"
42 #include "asterisk/cli.h"
43 #include "asterisk/lock.h"
44 #include "asterisk/frame.h"
45 #include "asterisk/options.h"
46 #include "asterisk/md5.h"
47 #include "asterisk/astdb.h"
48 #include "asterisk/utils.h"
49 #include "asterisk/acl.h"
50 #include "iax2.h"
51 #include "iax2-provision.h"
52 #include "iax2-parser.h"
53
54 #ifndef IPTOS_MINCOST
55 #define IPTOS_MINCOST 0x02
56 #endif
57
58 static int provinit = 0;
59
60 struct iax_template {
61         int dead;
62         char name[80];
63         char src[80];
64         struct iax_template *next;
65         char user[20];
66         char pass[20];
67         char lang[10];
68         unsigned short port;
69         unsigned int server;
70         unsigned short serverport;
71         unsigned int altserver;
72         unsigned int flags;
73         unsigned int format;
74         unsigned int tos;       
75 } *templates;
76
77 static struct iax_flag {
78         char *name;
79         int value;
80 } iax_flags[] = {
81         { "register", PROV_FLAG_REGISTER },
82         { "secure", PROV_FLAG_SECURE },
83         { "heartbeat", PROV_FLAG_HEARTBEAT },
84         { "debug", PROV_FLAG_DEBUG },
85         { "disablecid", PROV_FLAG_DIS_CALLERID },
86         { "disablecw", PROV_FLAG_DIS_CALLWAIT },
87         { "disablecidcw", PROV_FLAG_DIS_CIDCW },
88         { "disable3way", PROV_FLAG_DIS_THREEWAY },
89 };
90
91 char *iax_provflags2str(char *buf, int buflen, unsigned int flags)
92 {
93         int x;
94
95         if (!buf || buflen < 1)
96                 return NULL;
97         
98         buf[0] = '\0';
99
100         for (x = 0; x < sizeof(iax_flags) / sizeof(iax_flags[0]); x++) {
101                 if (flags & iax_flags[x].value){
102                         strncat(buf, iax_flags[x].name, buflen - strlen(buf) - 1);
103                         strncat(buf, ",", buflen - strlen(buf) - 1);
104                 }
105         }
106
107         if (!ast_strlen_zero(buf)) 
108                 buf[strlen(buf) - 1] = '\0';
109         else
110                 strncpy(buf, "none", buflen - 1);
111
112         return buf;
113 }
114
115 static unsigned int iax_str2flags(const char *buf)
116 {
117         int x;
118         int len;
119         int found;
120         unsigned int flags = 0;
121         char *e;
122         while(buf && *buf) {
123                 e = strchr(buf, ',');
124                 if (e)
125                         len = e - buf;
126                 else
127                         len = 0;
128                 found = 0;
129                 for (x=0;x<sizeof(iax_flags) / sizeof(iax_flags[0]); x++) {
130                         if ((len && !strncasecmp(iax_flags[x].name, buf, len)) ||
131                             (!len && !strcasecmp(iax_flags[x].name, buf))) {
132                                 flags |= iax_flags[x].value;
133                                 break;
134                         }
135                 }
136                 if (e) {
137                         buf = e + 1;
138                         while(*buf && (*buf < 33))
139                                 buf++;
140                 } else
141                         break;
142         }
143         return flags;
144 }
145 AST_MUTEX_DEFINE_STATIC(provlock);
146
147 static struct iax_template *iax_template_find(const char *s, int allowdead)
148 {
149         struct iax_template *cur;
150         cur = templates;
151         while(cur) {
152                 if (!strcasecmp(s, cur->name)) {
153                         if (!allowdead && cur->dead)
154                                 cur = NULL;
155                         break;
156                 }
157                 cur = cur->next;
158         }
159         return cur;
160 }
161
162 char *iax_prov_complete_template(const char *line, const char *word, int pos, int state)
163 {
164         struct iax_template *c;
165         int which=0;
166         char *ret = NULL;
167         int wordlen = strlen(word);
168
169         if (pos == 3) {
170                 ast_mutex_lock(&provlock);
171                 for (c = templates; c; c = c->next) {
172                         if (!strncasecmp(word, c->name, wordlen) && ++which > state) {
173                                 ret = ast_strdup(c->name);
174                                 break;
175                         }
176                 }
177                 ast_mutex_unlock(&provlock);
178         }
179         return ret;
180 }
181
182 static unsigned int prov_ver_calc(struct iax_ie_data *provdata)
183 {
184         struct MD5Context md5;
185         unsigned int tmp[4];
186         MD5Init(&md5);
187         MD5Update(&md5, provdata->buf, provdata->pos);
188         MD5Final((unsigned char *)tmp, &md5);
189         return tmp[0] ^ tmp[1] ^ tmp[2] ^ tmp[3];
190 }
191
192 int iax_provision_build(struct iax_ie_data *provdata, unsigned int *signature, const char *template, int force)
193 {
194         struct iax_template *cur;
195         unsigned int sig;
196         char tmp[40];
197         memset(provdata, 0, sizeof(*provdata));
198         ast_mutex_lock(&provlock);
199         cur = iax_template_find(template, 1);
200         /* If no match, try searching for '*' */
201         if (!cur)
202                 cur = iax_template_find("*", 1);
203         if (cur) {
204                 /* found it -- add information elements as appropriate */
205                 if (force || strlen(cur->user))
206                         iax_ie_append_str(provdata, PROV_IE_USER, cur->user);
207                 if (force || strlen(cur->pass))
208                         iax_ie_append_str(provdata, PROV_IE_PASS, cur->pass);
209                 if (force || strlen(cur->lang))
210                         iax_ie_append_str(provdata, PROV_IE_LANG, cur->lang);
211                 if (force || cur->port)
212                         iax_ie_append_short(provdata, PROV_IE_PORTNO, cur->port);
213                 if (force || cur->server)
214                         iax_ie_append_int(provdata, PROV_IE_SERVERIP, cur->server);
215                 if (force || cur->serverport)
216                         iax_ie_append_short(provdata, PROV_IE_SERVERPORT, cur->serverport);
217                 if (force || cur->altserver)
218                         iax_ie_append_int(provdata, PROV_IE_ALTSERVER, cur->altserver);
219                 if (force || cur->flags)
220                         iax_ie_append_int(provdata, PROV_IE_FLAGS, cur->flags);
221                 if (force || cur->format)
222                         iax_ie_append_int(provdata, PROV_IE_FORMAT, cur->format);
223                 if (force || cur->tos)
224                         iax_ie_append_byte(provdata, PROV_IE_TOS, cur->tos);
225                 
226                 /* Calculate checksum of message so far */
227                 sig = prov_ver_calc(provdata);
228                 if (signature)
229                         *signature = sig;
230                 /* Store signature */
231                 iax_ie_append_int(provdata, PROV_IE_PROVVER, sig);
232                 /* Cache signature for later verification so we need not recalculate all this */
233                 snprintf(tmp, sizeof(tmp), "v0x%08x", sig);
234                 ast_db_put("iax/provisioning/cache", template, tmp);
235         } else
236                 ast_db_put("iax/provisioning/cache", template, "u");
237         ast_mutex_unlock(&provlock);
238         return cur ? 0 : -1;
239 }
240
241 int iax_provision_version(unsigned int *version, const char *template, int force)
242 {
243         char tmp[80] = "";
244         struct iax_ie_data ied;
245         int ret=0;
246         memset(&ied, 0, sizeof(ied));
247
248         ast_mutex_lock(&provlock);
249         ast_db_get("iax/provisioning/cache", template, tmp, sizeof(tmp));
250         if (sscanf(tmp, "v%x", version) != 1) {
251                 if (strcmp(tmp, "u")) {
252                         ret = iax_provision_build(&ied, version, template, force);
253                         if (ret)
254                                 ast_debug(1, "Unable to create provisioning packet for '%s'\n", template);
255                 } else
256                         ret = -1;
257         } else
258                 ast_debug(1, "Retrieved cached version '%s' = '%08x'\n", tmp, *version);
259         ast_mutex_unlock(&provlock);
260         return ret;
261 }
262
263 static int iax_template_parse(struct iax_template *cur, struct ast_config *cfg, const char *s, const char *def)
264 {
265         struct ast_variable *v;
266         int foundportno = 0;
267         int foundserverportno = 0;
268         int x;
269         struct in_addr ia;
270         struct hostent *hp;
271         struct ast_hostent h;
272         struct iax_template *src, tmp;
273         const char *t;
274         if (def) {
275                 t = ast_variable_retrieve(cfg, s ,"template");
276                 src = NULL;
277                 if (t && strlen(t)) {
278                         src = iax_template_find(t, 0);
279                         if (!src)
280                                 ast_log(LOG_WARNING, "Unable to find base template '%s' for creating '%s'.  Trying '%s'\n", t, s, def);
281                         else
282                                 def = t;
283                 } 
284                 if (!src) {
285                         src = iax_template_find(def, 0);
286                         if (!src)
287                                 ast_log(LOG_WARNING, "Unable to locate default base template '%s' for creating '%s', omitting.\n", def, s);
288                 }
289                 if (!src)
290                         return -1;
291                 ast_mutex_lock(&provlock);      
292                 /* Backup old data */
293                 memcpy(&tmp, cur, sizeof(tmp));
294                 /* Restore from src */
295                 memcpy(cur, src, sizeof(tmp));
296                 /* Restore important headers */
297                 memcpy(cur->name, tmp.name, sizeof(cur->name));
298                 cur->dead = tmp.dead;
299                 cur->next = tmp.next;
300                 ast_mutex_unlock(&provlock);    
301         }
302         if (def)
303                 strncpy(cur->src, def, sizeof(cur->src) - 1);
304         else
305                 cur->src[0] = '\0';
306         v = ast_variable_browse(cfg, s);
307         while(v) {
308                 if (!strcasecmp(v->name, "port") || !strcasecmp(v->name, "serverport")) {
309                         if ((sscanf(v->value, "%d", &x) == 1) && (x > 0) && (x < 65535)) {
310                                 if (!strcasecmp(v->name, "port")) {
311                                         cur->port = x;
312                                         foundportno = 1;
313                                 } else {
314                                         cur->serverport = x;
315                                         foundserverportno = 1;
316                                 }
317                         } else
318                                 ast_log(LOG_WARNING, "Ignoring invalid %s '%s' for '%s' at line %d\n", v->name, v->value, s, v->lineno);
319                 } else if (!strcasecmp(v->name, "server") || !strcasecmp(v->name, "altserver")) {
320                         hp = ast_gethostbyname(v->value, &h);
321                         if (hp) {
322                                 memcpy(&ia, hp->h_addr, sizeof(ia));
323                                 if (!strcasecmp(v->name, "server"))
324                                         cur->server = ntohl(ia.s_addr);
325                                 else
326                                         cur->altserver = ntohl(ia.s_addr);
327                         } else 
328                                 ast_log(LOG_WARNING, "Ignoring invalid %s '%s' for '%s' at line %d\n", v->name, v->value, s, v->lineno);
329                 } else if (!strcasecmp(v->name, "codec")) {
330                         if ((x = ast_getformatbyname(v->value)) > 0) {
331                                 cur->format = x;
332                         } else
333                                 ast_log(LOG_WARNING, "Ignoring invalid codec '%s' for '%s' at line %d\n", v->value, s, v->lineno);
334                 } else if (!strcasecmp(v->name, "tos")) {
335                         if (ast_str2tos(v->value, &cur->tos))
336                                 ast_log(LOG_WARNING, "Invalid tos value at line %d, see doc/qos.tex for more information.\n", v->lineno);
337                 } else if (!strcasecmp(v->name, "user")) {
338                         strncpy(cur->user, v->value, sizeof(cur->user) - 1);
339                         if (strcmp(cur->user, v->value))
340                                 ast_log(LOG_WARNING, "Truncating username from '%s' to '%s' for '%s' at line %d\n", v->value, cur->user, s, v->lineno);
341                 } else if (!strcasecmp(v->name, "pass")) {
342                         strncpy(cur->pass, v->value, sizeof(cur->pass) - 1);
343                         if (strcmp(cur->pass, v->value))
344                                 ast_log(LOG_WARNING, "Truncating password from '%s' to '%s' for '%s' at line %d\n", v->value, cur->pass, s, v->lineno);
345                 } else if (!strcasecmp(v->name, "language")) {
346                         strncpy(cur->lang, v->value, sizeof(cur->lang) - 1);
347                         if (strcmp(cur->lang, v->value))
348                                 ast_log(LOG_WARNING, "Truncating language from '%s' to '%s' for '%s' at line %d\n", v->value, cur->lang, s, v->lineno);
349                 } else if (!strcasecmp(v->name, "flags")) {
350                         cur->flags = iax_str2flags(v->value);
351                 } else if (!strncasecmp(v->name, "flags", 5) && strchr(v->name, '+')) {
352                         cur->flags |= iax_str2flags(v->value);
353                 } else if (!strncasecmp(v->name, "flags", 5) && strchr(v->name, '-')) {
354                         cur->flags &= ~iax_str2flags(v->value);
355                 } else if (strcasecmp(v->name, "template")) {
356                         ast_log(LOG_WARNING, "Unknown keyword '%s' in definition of '%s' at line %d\n", v->name, s, v->lineno);
357                 }
358                 v = v->next;
359         }
360         if (!foundportno)
361                 cur->port = IAX_DEFAULT_PORTNO;
362         if (!foundserverportno)
363                 cur->serverport = IAX_DEFAULT_PORTNO;
364         return 0;
365 }
366
367 static int iax_process_template(struct ast_config *cfg, char *s, char *def)
368 {
369         /* Find an already existing one if there */
370         struct iax_template *cur;
371         int mallocd = 0;
372         cur = templates;
373         while(cur) {
374                 if (!strcasecmp(cur->name, s))
375                         break;
376                 cur = cur->next;
377         }
378         if (!cur) {
379                 mallocd = 1;
380                 cur = ast_calloc(1, sizeof(*cur));
381                 if (!cur) {
382                         ast_log(LOG_WARNING, "Out of memory!\n");
383                         return -1;
384                 }
385                 /* Initialize entry */
386                 strncpy(cur->name, s, sizeof(cur->name) - 1);
387                 cur->dead = 1;
388         }
389         if (!iax_template_parse(cur, cfg, s, def))
390                 cur->dead = 0;
391
392         /* Link if we're mallocd */
393         if (mallocd) {
394                 ast_mutex_lock(&provlock);
395                 cur->next = templates;
396                 templates = cur;
397                 ast_mutex_unlock(&provlock);
398         }
399         return 0;
400 }
401
402 static const char *ifthere(const char *s)
403 {
404         if (strlen(s))
405                 return s;
406         else
407                 return "<unspecified>";
408 }
409
410 static const char *iax_server(unsigned int addr)
411 {
412         struct in_addr ia;
413         
414         if (!addr)
415                 return "<unspecified>";
416         
417         ia.s_addr = htonl(addr);
418
419         return ast_inet_ntoa(ia);
420 }
421
422
423 static char *iax_show_provisioning(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
424 {
425         struct iax_template *cur;
426         char server[INET_ADDRSTRLEN];
427         char alternate[INET_ADDRSTRLEN];
428         char flags[80]; /* Has to be big enough for 'flags' too */
429         int found = 0;
430
431         switch (cmd) {
432         case CLI_INIT:
433                 e->command = "iax2 show provisioning";
434                 e->usage =
435                         "Usage: iax2 show provisioning [template]\n"
436                         "       Lists all known IAX provisioning templates or a\n"
437                         "       specific one if specified.\n";
438                 return NULL;
439         case CLI_GENERATE:
440                 return iax_prov_complete_template(a->line, a->word, a->pos, a->n);
441         }
442
443         if ((a->argc != 3) && (a->argc != 4))
444                 return CLI_SHOWUSAGE;
445         ast_mutex_lock(&provlock);
446         for (cur = templates;cur;cur = cur->next) {
447                 if ((a->argc == 3) || (!strcasecmp(a->argv[3], cur->name)))  {
448                         if (found) 
449                                 ast_cli(a->fd, "\n");
450                         ast_copy_string(server, iax_server(cur->server), sizeof(server));
451                         ast_copy_string(alternate, iax_server(cur->altserver), sizeof(alternate));
452                         ast_cli(a->fd, "== %s ==\n", cur->name);
453                         ast_cli(a->fd, "Base Templ:   %s\n", strlen(cur->src) ? cur->src : "<none>");
454                         ast_cli(a->fd, "Username:     %s\n", ifthere(cur->user));
455                         ast_cli(a->fd, "Secret:       %s\n", ifthere(cur->pass));
456                         ast_cli(a->fd, "Language:     %s\n", ifthere(cur->lang));
457                         ast_cli(a->fd, "Bind Port:    %d\n", cur->port);
458                         ast_cli(a->fd, "Server:       %s\n", server);
459                         ast_cli(a->fd, "Server Port:  %d\n", cur->serverport);
460                         ast_cli(a->fd, "Alternate:    %s\n", alternate);
461                         ast_cli(a->fd, "Flags:        %s\n", iax_provflags2str(flags, sizeof(flags), cur->flags));
462                         ast_cli(a->fd, "Format:       %s\n", ast_getformatname(cur->format));
463                         ast_cli(a->fd, "TOS:          0x%x\n", cur->tos);
464                         found++;
465                 }
466         }
467         ast_mutex_unlock(&provlock);
468         if (!found) {
469                 if (a->argc == 3)
470                         ast_cli(a->fd, "No provisioning templates found\n");
471                 else
472                         ast_cli(a->fd, "No provisioning template matching '%s' found\n", a->argv[3]);
473         }
474         return CLI_SUCCESS;
475 }
476
477 static struct ast_cli_entry cli_iax2_provision[] = {
478         NEW_CLI(iax_show_provisioning, "Display iax provisioning"),
479 };
480
481 static int iax_provision_init(void)
482 {
483         ast_cli_register_multiple(cli_iax2_provision, sizeof(cli_iax2_provision) / sizeof(struct ast_cli_entry));
484         provinit = 1;
485         return 0;
486 }
487
488 int iax_provision_unload(void)
489 {
490         provinit = 0;
491         ast_cli_unregister_multiple(cli_iax2_provision, sizeof(cli_iax2_provision) / sizeof(struct ast_cli_entry));
492         return 0;
493 }
494
495 int iax_provision_reload(int reload)
496 {
497         struct ast_config *cfg;
498         struct iax_template *cur, *prev, *next;
499         char *cat;
500         int found = 0;
501         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
502         if (!provinit)
503                 iax_provision_init();
504         /* Mark all as dead.  No need for locking */
505         cur = templates;
506         while(cur) {
507                 cur->dead = 1;
508                 cur = cur->next;
509         }
510         cfg = ast_config_load("iaxprov.conf", config_flags);
511         if (cfg != NULL && cfg != CONFIG_STATUS_FILEUNCHANGED) {
512                 /* Load as appropriate */
513                 cat = ast_category_browse(cfg, NULL);
514                 while(cat) {
515                         if (strcasecmp(cat, "general")) {
516                                 iax_process_template(cfg, cat, found ? "default" : NULL);
517                                 found++;
518                                 ast_verb(3, "Loaded provisioning template '%s'\n", cat);
519                         }
520                         cat = ast_category_browse(cfg, cat);
521                 }
522                 ast_config_destroy(cfg);
523         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
524                 return 0;
525         else
526                 ast_log(LOG_NOTICE, "No IAX provisioning configuration found, IAX provisioning disabled.\n");
527         ast_mutex_lock(&provlock);
528         /* Drop dead entries while locked */
529         prev = NULL;
530         cur = templates;
531         while(cur) {
532                 next = cur->next;
533                 if (cur->dead) {
534                         if (prev)
535                                 prev->next = next;
536                         else
537                                 templates = next;
538                         ast_free(cur);
539                 } else 
540                         prev = cur;
541                 cur = next;
542         }
543         ast_mutex_unlock(&provlock);
544         /* Purge cached signature DB entries */
545         ast_db_deltree("iax/provisioning/cache", NULL);
546         return 0;
547         
548 }