2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * Funding provided by nic.at
10 * See http://www.asterisk.org for more information about
11 * the Asterisk project. Please do not directly contact
12 * any of the maintainers of this project for assistance;
13 * the project provides a web site, mailing lists and IRC
14 * channels for your use.
16 * This program is free software, distributed under the terms of
17 * the GNU General Public License Version 2. See the LICENSE file
18 * at the top of the source tree.
23 * \brief ENUM Support for Asterisk
25 * \author Mark Spencer <markster@digium.com>
27 * \arg Funding provided by nic.at
31 * - NAPTR records: http://ietf.nri.reston.va.us/rfc/rfc2915.txt
32 * - DNS SRV records: http://www.ietf.org/rfc/rfc2782.txt
33 * - ENUM http://www.ietf.org/rfc/rfc3761.txt
34 * - ENUM for H.323: http://www.ietf.org/rfc/rfc3762.txt
35 * - ENUM SIP: http://www.ietf.org/rfc/rfc3764.txt
36 * - IANA ENUM Services: http://www.iana.org/assignments/enum-services
38 * \par Possible improvement
39 * \todo Implement a caching mechanism for multile enum lookups
40 * - See http://bugs.digium.com/view.php?id=6739
45 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
50 #include <arpa/nameser.h>
51 #if __APPLE_CC__ >= 1495
52 #include <arpa/nameser_compat.h>
62 #include "asterisk/logger.h"
63 #include "asterisk/options.h"
64 #include "asterisk/enum.h"
65 #include "asterisk/dns.h"
66 #include "asterisk/channel.h"
67 #include "asterisk/config.h"
68 #include "asterisk/utils.h"
80 #define TOPLEV "e164.arpa." /*!< The IETF Enum standard root, managed by the ITU */
82 /* Linked list from config file */
83 static struct enum_search
{
85 struct enum_search
*next
;
90 AST_MUTEX_DEFINE_STATIC(enumlock
);
95 } __attribute__ ((__packed__
));
97 /*! \brief Parse NAPTR record information elements */
98 static unsigned int parse_ie(char *data
, unsigned int maxdatalen
, unsigned char *src
, unsigned int srclen
)
100 unsigned int len
, olen
;
102 len
= olen
= (unsigned int) src
[0];
107 ast_log(LOG_WARNING
, "ENUM parsing failed: Wanted %d characters, got %d\n", len
, srclen
);
111 if (len
> maxdatalen
)
113 memcpy(data
, src
, len
);
118 /*! \brief Parse DNS NAPTR record used in ENUM ---*/
119 static int parse_naptr(char *dst
, int dstsize
, char *tech
, int techsize
, unsigned char *answer
, int len
, char *naptrinput
)
121 char tech_return
[80];
122 unsigned char *oanswer
= answer
;
123 char flags
[512] = "";
124 char services
[512] = "";
126 char regexp
[512] = "";
131 char *pattern
, *subst
, *d
;
133 int regexp_len
, size
, backref
;
134 int d_len
= sizeof(temp
) - 1;
136 regmatch_t pmatch
[9];
138 tech_return
[0] = '\0';
142 if (len
< sizeof(struct naptr
)) {
143 ast_log(LOG_WARNING
, "NAPTR record length too short\n");
146 answer
+= sizeof(struct naptr
);
147 len
-= sizeof(struct naptr
);
148 if ((res
= parse_ie(flags
, sizeof(flags
) - 1, answer
, len
)) < 0) {
149 ast_log(LOG_WARNING
, "Failed to get flags from NAPTR record\n");
155 if ((res
= parse_ie(services
, sizeof(services
) - 1, answer
, len
)) < 0) {
156 ast_log(LOG_WARNING
, "Failed to get services from NAPTR record\n");
162 if ((res
= parse_ie(regexp
, sizeof(regexp
) - 1, answer
, len
)) < 0) {
163 ast_log(LOG_WARNING
, "Failed to get regexp from NAPTR record\n");
170 if ((res
= dn_expand(oanswer
, answer
+ len
, answer
, repl
, sizeof(repl
) - 1)) < 0) {
171 ast_log(LOG_WARNING
, "Failed to expand hostname\n");
175 if (option_debug
> 2) /* Advanced NAPTR debugging */
176 ast_log(LOG_DEBUG
, "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
177 naptrinput
, flags
, services
, regexp
, repl
);
179 if (tolower(flags
[0]) != 'u') {
180 ast_log(LOG_WARNING
, "NAPTR Flag must be 'U' or 'u'.\n");
184 p
= strstr(services
, "e2u+");
186 p
= strstr(services
, "E2U+");
190 p
= strchr(p
, ':') + 1;
192 ast_copy_string(tech_return
, p
, sizeof(tech_return
));
195 p
= strstr(services
, "+e2u");
197 p
= strstr(services
, "+E2U");
200 p
= strchr(services
, ':');
203 ast_copy_string(tech_return
, services
, sizeof(tech_return
));
208 ast_copy_string(regexp, "!^\\+43(.*)$!\\1@bla.fasel!", sizeof(regexp) - 1);
211 regexp_len
= strlen(regexp
);
212 if (regexp_len
< 7) {
213 ast_log(LOG_WARNING
, "Regex too short to be meaningful.\n");
219 delim2
= strchr(regexp
+ 1, delim
);
220 if ((delim2
== NULL
) || (regexp
[regexp_len
-1] != delim
)) {
221 ast_log(LOG_WARNING
, "Regex delimiter error (on \"%s\").\n",regexp
);
225 pattern
= regexp
+ 1;
228 regexp
[regexp_len
-1] = 0;
231 * now do the regex wizardry.
234 if (regcomp(&preg
, pattern
, REG_EXTENDED
| REG_NEWLINE
)) {
235 ast_log(LOG_WARNING
, "NAPTR Regex compilation error (regex = \"%s\").\n",regexp
);
239 if (preg
.re_nsub
> 9) {
240 ast_log(LOG_WARNING
, "NAPTR Regex compilation error: too many subs.\n");
245 if (regexec(&preg
, naptrinput
, 9, pmatch
, 0)) {
246 ast_log(LOG_WARNING
, "NAPTR Regex match failed.\n");
254 while (*subst
&& (d_len
> 0)) {
255 if ((subst
[0] == '\\') && isdigit(subst
[1]) && (pmatch
[subst
[1]-'0'].rm_so
!= -1)) {
256 backref
= subst
[1]-'0';
257 size
= pmatch
[backref
].rm_eo
- pmatch
[backref
].rm_so
;
259 ast_log(LOG_WARNING
, "Not enough space during NAPTR regex substitution.\n");
262 memcpy(d
, naptrinput
+ pmatch
[backref
].rm_so
, size
);
266 } else if (isprint(*subst
)) {
270 ast_log(LOG_WARNING
, "Error during regex substitution.\n");
275 ast_copy_string(dst
, temp
, dstsize
);
276 dst
[dstsize
- 1] = '\0';
278 if (*tech
!= '\0'){ /* check if it is requested NAPTR */
279 if (!strncasecmp(tech
, "ALL", techsize
)){
280 return 1; /* return or count any RR */
282 if (!strncasecmp(tech_return
, tech
, sizeof(tech_return
)<techsize
?sizeof(tech_return
):techsize
)){
283 ast_copy_string(tech
, tech_return
, techsize
);
284 return 1; /* we got out RR */
285 } else { /* go to the next RR in the DNS answer */
290 /* tech was not specified, return first parsed RR */
291 ast_copy_string(tech
, tech_return
, techsize
);
296 /* do not return requested value, just count RRs and return thei number in dst */
297 #define ENUMLOOKUP_OPTIONS_COUNT 1
299 struct enum_naptr_rr
{
300 struct naptr naptr
; /* order and preference of RR */
301 char *result
; /* result of naptr parsing,e.g.: tel:+5553 */
302 char *tech
; /* Technology (from URL scheme) */
303 int sort_pos
; /* sort position */
306 struct enum_context
{
307 char *dst
; /* Destination part of URL from ENUM */
308 int dstlen
; /* Length */
309 char *tech
; /* Technology (from URL scheme) */
310 int techlen
; /* Length */
311 char *txt
; /* TXT record in TXT lookup */
312 int txtlen
; /* Length */
313 char *naptrinput
; /* The number to lookup */
314 int position
; /* used as counter for RRs or specifies position of required RR */
315 int options
; /* options , see ENUMLOOKUP_OPTIONS_* defined above */
316 struct enum_naptr_rr
*naptr_rrs
; /* array of parsed NAPTR RRs */
317 int naptr_rrs_count
; /* Size of array naptr_rrs */
320 /*! \brief Callback for TXT record lookup */
321 static int txt_callback(void *context
, unsigned char *answer
, int len
, unsigned char *fullanswer
)
323 struct enum_context
*c
= (struct enum_context
*)context
;
325 if (answer
== NULL
) {
331 /* skip over first byte, as for some reason it's a vertical tab character */
335 /* answer is not null-terminated, but should be */
336 /* this is safe to do, as answer has extra bytes on the end we can
337 * safely overwrite with a null */
339 /* now increment len so that len includes the null, so that we can
340 * compare apples to apples */
343 /* finally, copy the answer into c->txt */
344 ast_copy_string(c
->txt
, (const char *) answer
, len
< c
->txtlen
? len
: (c
->txtlen
));
346 /* just to be safe, let's make sure c->txt is null terminated */
347 c
->txt
[(c
->txtlen
)-1] = '\0';
352 /*! \brief Callback from ENUM lookup function */
353 static int enum_callback(void *context
, unsigned char *answer
, int len
, unsigned char *fullanswer
)
355 struct enum_context
*c
= context
;
359 res
= parse_naptr(c
->dst
, c
->dstlen
, c
->tech
, c
->techlen
, answer
, len
, c
->naptrinput
);
362 ast_log(LOG_WARNING
, "Failed to parse naptr :(\n");
364 } else if (res
> 0 && !ast_strlen_zero(c
->dst
)){ /* ok, we got needed NAPTR */
365 if (c
->options
& ENUMLOOKUP_OPTIONS_COUNT
){ /* counting RRs */
367 snprintf(c
->dst
, c
->dstlen
, "%d", c
->position
);
369 if ((p
= ast_realloc(c
->naptr_rrs
, sizeof(*c
->naptr_rrs
) * (c
->naptr_rrs_count
+ 1)))) {
371 memcpy(&c
->naptr_rrs
[c
->naptr_rrs_count
].naptr
, answer
, sizeof(c
->naptr_rrs
->naptr
));
372 c
->naptr_rrs
[c
->naptr_rrs_count
].result
= strdup(c
->dst
);
373 c
->naptr_rrs
[c
->naptr_rrs_count
].tech
= strdup(c
->tech
);
374 c
->naptr_rrs
[c
->naptr_rrs_count
].sort_pos
= c
->naptr_rrs_count
;
375 c
->naptr_rrs_count
++;
382 if (c
->options
& ENUMLOOKUP_OPTIONS_COUNT
) { /* counting RRs */
383 snprintf(c
->dst
, c
->dstlen
, "%d", c
->position
);
389 /*! \brief ENUM lookup */
390 int ast_get_enum(struct ast_channel
*chan
, const char *number
, char *dst
, int dstlen
, char *tech
, int techlen
, char* suffix
, char* options
, unsigned int record
)
392 struct enum_context context
;
394 char naptrinput
[512];
395 int pos
= strlen(number
) - 1;
398 struct enum_search
*s
= NULL
;
400 /* for ISN rewrite */
407 ast_copy_string(naptrinput
, number
[0] == 'n' ? number
+1 : number
, sizeof(naptrinput
));
409 context
.naptrinput
= naptrinput
; /* The number */
410 context
.dst
= dst
; /* Return string */
411 context
.dstlen
= dstlen
;
413 context
.techlen
= techlen
;
415 context
.position
= record
;
416 context
.naptr_rrs
= NULL
;
417 context
.naptr_rrs_count
= 0;
419 if (options
!= NULL
) {
420 if (*options
== 'c') {
421 context
.options
= ENUMLOOKUP_OPTIONS_COUNT
;
422 context
.position
= 0;
426 ast_log(LOG_DEBUG
, "ast_get_enum(): n='%s', tech='%s', suffix='%s', options='%d', record='%d'\n",
427 number
, tech
, suffix
, context
.options
, context
.position
);
433 p1
= strchr(number
, '*');
435 if (number
[0] == 'n') { /* do not perform ISN rewrite ('n' is testing flag) */
437 k
= 1; /* strip 'n' from number */
448 while(*p2
&& newpos
< 128){
457 if (isdigit(number
[pos
])) {
458 tmp
[newpos
++] = number
[pos
];
465 if (chan
&& ast_autoservice_start(chan
) < 0)
469 ast_copy_string(tmp
+ newpos
, suffix
, sizeof(tmp
) - newpos
);
470 ret
= ast_search_dns(&context
, tmp
, C_IN
, T_NAPTR
, enum_callback
);
471 ast_log(LOG_DEBUG
, "ast_get_enum: ast_search_dns(%s) returned %d\n", tmp
, ret
);
473 ret
= -1; /* this is actually dead code since the demise of app_enum.c */
475 ast_mutex_lock(&enumlock
);
476 if (version
!= enumver
) {
477 /* Ooh, a reload... */
483 ast_mutex_unlock(&enumlock
);
488 ast_copy_string(tmp
+ newpos
, s
->toplev
, sizeof(tmp
) - newpos
);
489 ret
= ast_search_dns(&context
, tmp
, C_IN
, T_NAPTR
, enum_callback
);
490 ast_log(LOG_DEBUG
, "ast_get_enum: ast_search_dns(%s) returned %d\n", tmp
, ret
);
497 ast_log(LOG_DEBUG
, "No such number found: %s (%s)\n", tmp
, strerror(errno
));
502 if (context
.naptr_rrs_count
>= context
.position
&& ! (context
.options
& ENUMLOOKUP_OPTIONS_COUNT
)) {
503 /* sort array by NAPTR order/preference */
504 for (k
= 0; k
< context
.naptr_rrs_count
; k
++) {
505 for (i
= 0; i
< context
.naptr_rrs_count
; i
++) {
506 /* use order first and then preference to compare */
507 if ((ntohs(context
.naptr_rrs
[k
].naptr
.order
) < ntohs(context
.naptr_rrs
[i
].naptr
.order
)
508 && context
.naptr_rrs
[k
].sort_pos
> context
.naptr_rrs
[i
].sort_pos
)
509 || (ntohs(context
.naptr_rrs
[k
].naptr
.order
) > ntohs(context
.naptr_rrs
[i
].naptr
.order
)
510 && context
.naptr_rrs
[k
].sort_pos
< context
.naptr_rrs
[i
].sort_pos
)){
511 z
= context
.naptr_rrs
[k
].sort_pos
;
512 context
.naptr_rrs
[k
].sort_pos
= context
.naptr_rrs
[i
].sort_pos
;
513 context
.naptr_rrs
[i
].sort_pos
= z
;
516 if (ntohs(context
.naptr_rrs
[k
].naptr
.order
) == ntohs(context
.naptr_rrs
[i
].naptr
.order
)) {
517 if ((ntohs(context
.naptr_rrs
[k
].naptr
.pref
) < ntohs(context
.naptr_rrs
[i
].naptr
.pref
)
518 && context
.naptr_rrs
[k
].sort_pos
> context
.naptr_rrs
[i
].sort_pos
)
519 || (ntohs(context
.naptr_rrs
[k
].naptr
.pref
) > ntohs(context
.naptr_rrs
[i
].naptr
.pref
)
520 && context
.naptr_rrs
[k
].sort_pos
< context
.naptr_rrs
[i
].sort_pos
)){
521 z
= context
.naptr_rrs
[k
].sort_pos
;
522 context
.naptr_rrs
[k
].sort_pos
= context
.naptr_rrs
[i
].sort_pos
;
523 context
.naptr_rrs
[i
].sort_pos
= z
;
528 for (k
= 0; k
< context
.naptr_rrs_count
; k
++) {
529 if (context
.naptr_rrs
[k
].sort_pos
== context
.position
-1) {
530 ast_copy_string(context
.dst
, context
.naptr_rrs
[k
].result
, dstlen
);
531 ast_copy_string(context
.tech
, context
.naptr_rrs
[k
].tech
, techlen
);
535 } else if (!(context
.options
& ENUMLOOKUP_OPTIONS_COUNT
)) {
539 ret
|= ast_autoservice_stop(chan
);
541 for (k
= 0; k
< context
.naptr_rrs_count
; k
++) {
542 free(context
.naptr_rrs
[k
].result
);
543 free(context
.naptr_rrs
[k
].tech
);
546 free(context
.naptr_rrs
);
551 /*! \brief Get TXT record from DNS.
552 Really has nothing to do with enum, but anyway...
554 int ast_get_txt(struct ast_channel
*chan
, const char *number
, char *dst
, int dstlen
, char *tech
, int techlen
, char *txt
, int txtlen
)
556 struct enum_context context
;
558 char naptrinput
[512] = "+";
559 int pos
= strlen(number
) - 1;
562 struct enum_search
*s
= NULL
;
565 strncat(naptrinput
, number
, sizeof(naptrinput
) - 2);
567 context
.naptrinput
= naptrinput
;
569 context
.dstlen
= dstlen
;
571 context
.techlen
= techlen
;
573 context
.txtlen
= txtlen
;
578 tmp
[newpos
++] = number
[pos
--];
582 if (chan
&& ast_autoservice_start(chan
) < 0)
586 ast_mutex_lock(&enumlock
);
587 if (version
!= enumver
) {
588 /* Ooh, a reload... */
595 ast_copy_string(tmp
+ newpos
, s
->toplev
, sizeof(tmp
) - newpos
);
597 ast_mutex_unlock(&enumlock
);
601 ret
= ast_search_dns(&context
, tmp
, C_IN
, T_TXT
, txt_callback
);
606 if (option_debug
> 1)
607 ast_log(LOG_DEBUG
, "No such number found in ENUM: %s (%s)\n", tmp
, strerror(errno
));
611 ret
|= ast_autoservice_stop(chan
);
615 /*! \brief Add enum tree to linked list */
616 static struct enum_search
*enum_newtoplev(char *s
)
618 struct enum_search
*tmp
;
620 if ((tmp
= ast_calloc(1, sizeof(*tmp
)))) {
621 ast_copy_string(tmp
->toplev
, s
, sizeof(tmp
->toplev
));
626 /*! \brief Initialize the ENUM support subsystem */
627 int ast_enum_init(void)
629 struct ast_config
*cfg
;
630 struct enum_search
*s
, *sl
;
631 struct ast_variable
*v
;
633 /* Destroy existing list */
634 ast_mutex_lock(&enumlock
);
642 cfg
= ast_config_load("enum.conf");
645 v
= ast_variable_browse(cfg
, "general");
647 if (!strcasecmp(v
->name
, "search")) {
648 s
= enum_newtoplev(v
->value
);
659 ast_config_destroy(cfg
);
661 toplevs
= enum_newtoplev(TOPLEV
);
664 ast_mutex_unlock(&enumlock
);
668 int ast_enum_reload(void)
670 return ast_enum_init();