2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief IAX Provisioning Protocol
23 * \author Mark Spencer <markster@digium.com>
28 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
35 #include <netinet/in.h>
36 #include <netinet/in_systm.h>
37 #include <netinet/ip.h>
38 #include <sys/socket.h>
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"
51 #include "iax2-provision.h"
52 #include "iax2-parser.h"
55 #define IPTOS_MINCOST 0x02
58 static int provinit
= 0;
64 struct iax_template
*next
;
70 unsigned short serverport
;
71 unsigned int altserver
;
77 static struct iax_flag
{
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
},
91 char *iax_provflags2str(char *buf
, int buflen
, unsigned int flags
)
95 if (!buf
|| buflen
< 1)
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);
107 if (!ast_strlen_zero(buf
))
108 buf
[strlen(buf
) - 1] = '\0';
110 strncpy(buf
, "none", buflen
- 1);
115 static unsigned int iax_str2flags(const char *buf
)
120 unsigned int flags
= 0;
123 e
= strchr(buf
, ',');
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
;
138 while(*buf
&& (*buf
< 33))
145 AST_MUTEX_DEFINE_STATIC(provlock
);
147 static struct iax_template
*iax_template_find(const char *s
, int allowdead
)
149 struct iax_template
*cur
;
152 if (!strcasecmp(s
, cur
->name
)) {
153 if (!allowdead
&& cur
->dead
)
162 char *iax_prov_complete_template(const char *line
, const char *word
, int pos
, int state
)
164 struct iax_template
*c
;
167 int wordlen
= strlen(word
);
169 ast_mutex_lock(&provlock
);
170 for (c
= templates
; c
; c
= c
->next
) {
171 if (!strncasecmp(word
, c
->name
, wordlen
) && ++which
> state
) {
172 ret
= strdup(c
->name
);
176 ast_mutex_unlock(&provlock
);
181 static unsigned int prov_ver_calc(struct iax_ie_data
*provdata
)
183 struct MD5Context md5
;
186 MD5Update(&md5
, provdata
->buf
, provdata
->pos
);
187 MD5Final((unsigned char *)tmp
, &md5
);
188 return tmp
[0] ^ tmp
[1] ^ tmp
[2] ^ tmp
[3];
191 int iax_provision_build(struct iax_ie_data
*provdata
, unsigned int *signature
, const char *template, int force
)
193 struct iax_template
*cur
;
196 memset(provdata
, 0, sizeof(*provdata
));
197 ast_mutex_lock(&provlock
);
198 cur
= iax_template_find(template, 1);
199 /* If no match, try searching for '*' */
201 cur
= iax_template_find("*", 1);
203 /* found it -- add information elements as appropriate */
204 if (force
|| strlen(cur
->user
))
205 iax_ie_append_str(provdata
, PROV_IE_USER
, cur
->user
);
206 if (force
|| strlen(cur
->pass
))
207 iax_ie_append_str(provdata
, PROV_IE_PASS
, cur
->pass
);
208 if (force
|| strlen(cur
->lang
))
209 iax_ie_append_str(provdata
, PROV_IE_LANG
, cur
->lang
);
210 if (force
|| cur
->port
)
211 iax_ie_append_short(provdata
, PROV_IE_PORTNO
, cur
->port
);
212 if (force
|| cur
->server
)
213 iax_ie_append_int(provdata
, PROV_IE_SERVERIP
, cur
->server
);
214 if (force
|| cur
->serverport
)
215 iax_ie_append_short(provdata
, PROV_IE_SERVERPORT
, cur
->serverport
);
216 if (force
|| cur
->altserver
)
217 iax_ie_append_int(provdata
, PROV_IE_ALTSERVER
, cur
->altserver
);
218 if (force
|| cur
->flags
)
219 iax_ie_append_int(provdata
, PROV_IE_FLAGS
, cur
->flags
);
220 if (force
|| cur
->format
)
221 iax_ie_append_int(provdata
, PROV_IE_FORMAT
, cur
->format
);
222 if (force
|| cur
->tos
)
223 iax_ie_append_byte(provdata
, PROV_IE_TOS
, cur
->tos
);
225 /* Calculate checksum of message so far */
226 sig
= prov_ver_calc(provdata
);
229 /* Store signature */
230 iax_ie_append_int(provdata
, PROV_IE_PROVVER
, sig
);
231 /* Cache signature for later verification so we need not recalculate all this */
232 snprintf(tmp
, sizeof(tmp
), "v0x%08x", sig
);
233 ast_db_put("iax/provisioning/cache", template, tmp
);
235 ast_db_put("iax/provisioning/cache", template, "u");
236 ast_mutex_unlock(&provlock
);
240 int iax_provision_version(unsigned int *version
, const char *template, int force
)
243 struct iax_ie_data ied
;
245 memset(&ied
, 0, sizeof(ied
));
247 ast_mutex_lock(&provlock
);
248 ast_db_get("iax/provisioning/cache", template, tmp
, sizeof(tmp
));
249 if (sscanf(tmp
, "v%x", version
) != 1) {
250 if (strcmp(tmp
, "u")) {
251 ret
= iax_provision_build(&ied
, version
, template, force
);
253 ast_log(LOG_DEBUG
, "Unable to create provisioning packet for '%s'\n", template);
256 } else if (option_debug
)
257 ast_log(LOG_DEBUG
, "Retrieved cached version '%s' = '%08x'\n", tmp
, *version
);
258 ast_mutex_unlock(&provlock
);
262 static int iax_template_parse(struct iax_template
*cur
, struct ast_config
*cfg
, const char *s
, const char *def
)
264 struct ast_variable
*v
;
266 int foundserverportno
= 0;
270 struct ast_hostent h
;
271 struct iax_template
*src
, tmp
;
274 t
= ast_variable_retrieve(cfg
, s
,"template");
276 if (t
&& strlen(t
)) {
277 src
= iax_template_find(t
, 0);
279 ast_log(LOG_WARNING
, "Unable to find base template '%s' for creating '%s'. Trying '%s'\n", t
, s
, def
);
284 src
= iax_template_find(def
, 0);
286 ast_log(LOG_WARNING
, "Unable to locate default base template '%s' for creating '%s', omitting.\n", def
, s
);
290 ast_mutex_lock(&provlock
);
291 /* Backup old data */
292 memcpy(&tmp
, cur
, sizeof(tmp
));
293 /* Restore from src */
294 memcpy(cur
, src
, sizeof(tmp
));
295 /* Restore important headers */
296 memcpy(cur
->name
, tmp
.name
, sizeof(cur
->name
));
297 cur
->dead
= tmp
.dead
;
298 cur
->next
= tmp
.next
;
299 ast_mutex_unlock(&provlock
);
302 strncpy(cur
->src
, def
, sizeof(cur
->src
) - 1);
305 v
= ast_variable_browse(cfg
, s
);
307 if (!strcasecmp(v
->name
, "port") || !strcasecmp(v
->name
, "serverport")) {
308 if ((sscanf(v
->value
, "%d", &x
) == 1) && (x
> 0) && (x
< 65535)) {
309 if (!strcasecmp(v
->name
, "port")) {
314 foundserverportno
= 1;
317 ast_log(LOG_WARNING
, "Ignoring invalid %s '%s' for '%s' at line %d\n", v
->name
, v
->value
, s
, v
->lineno
);
318 } else if (!strcasecmp(v
->name
, "server") || !strcasecmp(v
->name
, "altserver")) {
319 hp
= ast_gethostbyname(v
->value
, &h
);
321 memcpy(&ia
, hp
->h_addr
, sizeof(ia
));
322 if (!strcasecmp(v
->name
, "server"))
323 cur
->server
= ntohl(ia
.s_addr
);
325 cur
->altserver
= ntohl(ia
.s_addr
);
327 ast_log(LOG_WARNING
, "Ignoring invalid %s '%s' for '%s' at line %d\n", v
->name
, v
->value
, s
, v
->lineno
);
328 } else if (!strcasecmp(v
->name
, "codec")) {
329 if ((x
= ast_getformatbyname(v
->value
)) > 0) {
332 ast_log(LOG_WARNING
, "Ignoring invalid codec '%s' for '%s' at line %d\n", v
->value
, s
, v
->lineno
);
333 } else if (!strcasecmp(v
->name
, "tos")) {
334 if (ast_str2tos(v
->value
, &cur
->tos
))
335 ast_log(LOG_WARNING
, "Invalid tos value at line %d, see doc/ip-tos.txt for more information.\n", v
->lineno
);
336 } else if (!strcasecmp(v
->name
, "user")) {
337 strncpy(cur
->user
, v
->value
, sizeof(cur
->user
) - 1);
338 if (strcmp(cur
->user
, v
->value
))
339 ast_log(LOG_WARNING
, "Truncating username from '%s' to '%s' for '%s' at line %d\n", v
->value
, cur
->user
, s
, v
->lineno
);
340 } else if (!strcasecmp(v
->name
, "pass")) {
341 strncpy(cur
->pass
, v
->value
, sizeof(cur
->pass
) - 1);
342 if (strcmp(cur
->pass
, v
->value
))
343 ast_log(LOG_WARNING
, "Truncating password from '%s' to '%s' for '%s' at line %d\n", v
->value
, cur
->pass
, s
, v
->lineno
);
344 } else if (!strcasecmp(v
->name
, "language")) {
345 strncpy(cur
->lang
, v
->value
, sizeof(cur
->lang
) - 1);
346 if (strcmp(cur
->lang
, v
->value
))
347 ast_log(LOG_WARNING
, "Truncating language from '%s' to '%s' for '%s' at line %d\n", v
->value
, cur
->lang
, s
, v
->lineno
);
348 } else if (!strcasecmp(v
->name
, "flags")) {
349 cur
->flags
= iax_str2flags(v
->value
);
350 } else if (!strncasecmp(v
->name
, "flags", 5) && strchr(v
->name
, '+')) {
351 cur
->flags
|= iax_str2flags(v
->value
);
352 } else if (!strncasecmp(v
->name
, "flags", 5) && strchr(v
->name
, '-')) {
353 cur
->flags
&= ~iax_str2flags(v
->value
);
354 } else if (strcasecmp(v
->name
, "template")) {
355 ast_log(LOG_WARNING
, "Unknown keyword '%s' in definition of '%s' at line %d\n", v
->name
, s
, v
->lineno
);
360 cur
->port
= IAX_DEFAULT_PORTNO
;
361 if (!foundserverportno
)
362 cur
->serverport
= IAX_DEFAULT_PORTNO
;
366 static int iax_process_template(struct ast_config
*cfg
, char *s
, char *def
)
368 /* Find an already existing one if there */
369 struct iax_template
*cur
;
373 if (!strcasecmp(cur
->name
, s
))
379 cur
= malloc(sizeof(struct iax_template
));
381 ast_log(LOG_WARNING
, "Out of memory!\n");
384 /* Initialize entry */
385 memset(cur
, 0, sizeof(*cur
));
386 strncpy(cur
->name
, s
, sizeof(cur
->name
) - 1);
389 if (!iax_template_parse(cur
, cfg
, s
, def
))
392 /* Link if we're mallocd */
394 ast_mutex_lock(&provlock
);
395 cur
->next
= templates
;
397 ast_mutex_unlock(&provlock
);
402 static char show_provisioning_usage
[] =
403 "Usage: iax list provisioning [template]\n"
404 " Lists all known IAX provisioning templates or a\n"
405 " specific one if specified.\n";
407 static const char *ifthere(const char *s
)
412 return "<unspecified>";
415 static const char *iax_server(unsigned int addr
)
420 return "<unspecified>";
422 ia
.s_addr
= htonl(addr
);
424 return ast_inet_ntoa(ia
);
428 static int iax_show_provisioning(int fd
, int argc
, char *argv
[])
430 struct iax_template
*cur
;
431 char server
[INET_ADDRSTRLEN
];
432 char alternate
[INET_ADDRSTRLEN
];
433 char flags
[80]; /* Has to be big enough for 'flags' too */
435 if ((argc
!= 3) && (argc
!= 4))
436 return RESULT_SHOWUSAGE
;
437 ast_mutex_lock(&provlock
);
438 for (cur
= templates
;cur
;cur
= cur
->next
) {
439 if ((argc
== 3) || (!strcasecmp(argv
[3], cur
->name
))) {
442 ast_copy_string(server
, iax_server(cur
->server
), sizeof(server
));
443 ast_copy_string(alternate
, iax_server(cur
->altserver
), sizeof(alternate
));
444 ast_cli(fd
, "== %s ==\n", cur
->name
);
445 ast_cli(fd
, "Base Templ: %s\n", strlen(cur
->src
) ? cur
->src
: "<none>");
446 ast_cli(fd
, "Username: %s\n", ifthere(cur
->user
));
447 ast_cli(fd
, "Secret: %s\n", ifthere(cur
->pass
));
448 ast_cli(fd
, "Language: %s\n", ifthere(cur
->lang
));
449 ast_cli(fd
, "Bind Port: %d\n", cur
->port
);
450 ast_cli(fd
, "Server: %s\n", server
);
451 ast_cli(fd
, "Server Port: %d\n", cur
->serverport
);
452 ast_cli(fd
, "Alternate: %s\n", alternate
);
453 ast_cli(fd
, "Flags: %s\n", iax_provflags2str(flags
, sizeof(flags
), cur
->flags
));
454 ast_cli(fd
, "Format: %s\n", ast_getformatname(cur
->format
));
455 ast_cli(fd
, "TOS: 0x%x\n", cur
->tos
);
459 ast_mutex_unlock(&provlock
);
462 ast_cli(fd
, "No provisioning templates found\n");
464 ast_cli(fd
, "No provisioning template matching '%s' found\n", argv
[3]);
466 return RESULT_SUCCESS
;
469 static struct ast_cli_entry cli_iax2_provision
[] = {
470 { { "iax2", "show", "provisioning", NULL
},
471 iax_show_provisioning
, "Display iax provisioning",
472 show_provisioning_usage
, iax_prov_complete_template
, },
475 static int iax_provision_init(void)
477 ast_cli_register_multiple(cli_iax2_provision
, sizeof(cli_iax2_provision
) / sizeof(struct ast_cli_entry
));
482 int iax_provision_unload(void)
485 ast_cli_unregister_multiple(cli_iax2_provision
, sizeof(cli_iax2_provision
) / sizeof(struct ast_cli_entry
));
489 int iax_provision_reload(void)
491 struct ast_config
*cfg
;
492 struct iax_template
*cur
, *prev
, *next
;
496 iax_provision_init();
497 /* Mark all as dead. No need for locking */
503 cfg
= ast_config_load("iaxprov.conf");
505 /* Load as appropriate */
506 cat
= ast_category_browse(cfg
, NULL
);
508 if (strcasecmp(cat
, "general")) {
509 iax_process_template(cfg
, cat
, found
? "default" : NULL
);
511 if (option_verbose
> 2)
512 ast_verbose(VERBOSE_PREFIX_3
"Loaded provisioning template '%s'\n", cat
);
514 cat
= ast_category_browse(cfg
, cat
);
516 ast_config_destroy(cfg
);
518 ast_log(LOG_NOTICE
, "No IAX provisioning configuration found, IAX provisioning disabled.\n");
519 ast_mutex_lock(&provlock
);
520 /* Drop dead entries while locked */
535 ast_mutex_unlock(&provlock
);
536 /* Purge cached signature DB entries */
537 ast_db_deltree("iax/provisioning/cache", NULL
);