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
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/nameser.h>
47 #if __APPLE_CC__ >= 1495
48 #include <arpa/nameser_compat.h>
60 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
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
;
88 static int enumver
= 0;
90 AST_MUTEX_DEFINE_STATIC(enumlock
);
95 } __attribute__ ((__packed__
));
97 /*! \brief Parse NAPTR record information elements */
98 static int parse_ie(char *data
, int maxdatalen
, char *src
, int srclen
)
102 len
= olen
= (int)src
[0];
105 if (len
> srclen
|| len
< 0 ) {
106 ast_log(LOG_WARNING
, "ENUM parsing failed: Wanted %d characters, got %d\n", len
, srclen
);
109 if (len
> maxdatalen
)
111 memcpy(data
, src
, len
);
115 /*! \brief Parse DNS NAPTR record used in ENUM ---*/
116 static int parse_naptr(char *dst
, int dstsize
, char *tech
, int techsize
, char *answer
, int len
, char *naptrinput
)
119 char tech_return
[80];
120 char *oanswer
= answer
;
121 char flags
[512] = "";
122 char services
[512] = "";
124 char regexp
[512] = "";
129 char *pattern
, *subst
, *d
;
131 int regexp_len
, size
, backref
;
132 int d_len
= sizeof(temp
) - 1;
134 regmatch_t pmatch
[9];
136 tech_return
[0] = '\0';
140 if (len
< sizeof(struct naptr
)) {
141 ast_log(LOG_WARNING
, "NAPTR record length too short\n");
144 answer
+= sizeof(struct naptr
);
145 len
-= sizeof(struct naptr
);
146 if ((res
= parse_ie(flags
, sizeof(flags
) - 1, answer
, len
)) < 0) {
147 ast_log(LOG_WARNING
, "Failed to get flags from NAPTR record\n");
153 if ((res
= parse_ie(services
, sizeof(services
) - 1, answer
, len
)) < 0) {
154 ast_log(LOG_WARNING
, "Failed to get services from NAPTR record\n");
160 if ((res
= parse_ie(regexp
, sizeof(regexp
) - 1, answer
, len
)) < 0) {
161 ast_log(LOG_WARNING
, "Failed to get regexp from NAPTR record\n");
168 if ((res
= dn_expand((unsigned char *)oanswer
, (unsigned char *)answer
+ len
, (unsigned char *)answer
, repl
, sizeof(repl
) - 1)) < 0) {
169 ast_log(LOG_WARNING
, "Failed to expand hostname\n");
173 if (option_debug
> 2) /* Advanced NAPTR debugging */
174 ast_log(LOG_DEBUG
, "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
175 naptrinput
, flags
, services
, regexp
, repl
);
177 if (tolower(flags
[0]) != 'u') {
178 ast_log(LOG_WARNING
, "NAPTR Flag must be 'U' or 'u'.\n");
182 p
= strstr(services
, "e2u+");
184 p
= strstr(services
, "E2U+");
188 p
= strchr(p
, ':') + 1;
190 ast_copy_string(tech_return
, p
, sizeof(tech_return
));
193 p
= strstr(services
, "+e2u");
195 p
= strstr(services
, "+E2U");
198 p
= strchr(services
, ':');
201 ast_copy_string(tech_return
, services
, sizeof(tech_return
));
206 ast_copy_string(regexp, "!^\\+43(.*)$!\\1@bla.fasel!", sizeof(regexp) - 1);
209 regexp_len
= strlen(regexp
);
210 if (regexp_len
< 7) {
211 ast_log(LOG_WARNING
, "Regex too short to be meaningful.\n");
217 delim2
= strchr(regexp
+ 1, delim
);
218 if ((delim2
== NULL
) || (regexp
[regexp_len
-1] != delim
)) {
219 ast_log(LOG_WARNING
, "Regex delimiter error (on \"%s\").\n",regexp
);
223 pattern
= regexp
+ 1;
226 regexp
[regexp_len
-1] = 0;
229 printf("Pattern: %s\n", pattern
);
230 printf("Subst: %s\n", subst
);
231 printf("Input: %s\n", naptrinput
);
235 * now do the regex wizardry.
238 if (regcomp(&preg
, pattern
, REG_EXTENDED
| REG_NEWLINE
)) {
239 ast_log(LOG_WARNING
, "NAPTR Regex compilation error (regex = \"%s\").\n",regexp
);
243 if (preg
.re_nsub
> 9) {
244 ast_log(LOG_WARNING
, "NAPTR Regex compilation error: too many subs.\n");
249 if (regexec(&preg
, naptrinput
, 9, pmatch
, 0)) {
250 ast_log(LOG_WARNING
, "NAPTR Regex match failed.\n");
258 while (*subst
&& (d_len
> 0)) {
259 if ((subst
[0] == '\\') && isdigit(subst
[1]) && (pmatch
[subst
[1]-'0'].rm_so
!= -1)) {
260 backref
= subst
[1]-'0';
261 size
= pmatch
[backref
].rm_eo
- pmatch
[backref
].rm_so
;
263 ast_log(LOG_WARNING
, "Not enough space during NAPTR regex substitution.\n");
266 memcpy(d
, naptrinput
+ pmatch
[backref
].rm_so
, size
);
270 } else if (isprint(*subst
)) {
274 ast_log(LOG_WARNING
, "Error during regex substitution.\n");
279 ast_copy_string(dst
, temp
, dstsize
);
280 dst
[dstsize
- 1] = '\0';
282 if (*tech
!= '\0'){ /* check if it is requested NAPTR */
283 if (!strncasecmp(tech
, "ALL", techsize
)){
284 return 1; /* return or count any RR */
286 if (!strncasecmp(tech_return
, tech
, sizeof(tech_return
)<techsize
?sizeof(tech_return
):techsize
)){
287 ast_copy_string(tech
, tech_return
, techsize
);
288 return 1; /* we got out RR */
289 } else { /* go to the next RR in the DNS answer */
294 /* tech was not specified, return first parsed RR */
295 ast_copy_string(tech
, tech_return
, techsize
);
300 /* do not return requested value, just count RRs and return thei number in dst */
301 #define ENUMLOOKUP_OPTIONS_COUNT 1
303 struct enum_naptr_rr
{
304 struct naptr naptr
; /* order and preference of RR */
305 char *result
; /* result of naptr parsing,e.g.: tel:+5553 */
306 char *tech
; /* Technology (from URL scheme) */
307 int sort_pos
; /* sort position */
310 struct enum_context
{
311 char *dst
; /* Destination part of URL from ENUM */
312 int dstlen
; /* Length */
313 char *tech
; /* Technology (from URL scheme) */
314 int techlen
; /* Length */
315 char *txt
; /* TXT record in TXT lookup */
316 int txtlen
; /* Length */
317 char *naptrinput
; /* The number to lookup */
318 int position
; /* used as counter for RRs or specifies position of required RR */
319 int options
; /* options , see ENUMLOOKUP_OPTIONS_* defined above */
320 struct enum_naptr_rr
*naptr_rrs
; /* array of parsed NAPTR RRs */
321 int naptr_rrs_count
; /* Size of array naptr_rrs */
324 /*! \brief Callback for TXT record lookup */
325 static int txt_callback(void *context
, char *answer
, int len
, char *fullanswer
)
327 struct enum_context
*c
= (struct enum_context
*)context
;
329 printf("ENUMTXT Called\n");
332 if (answer
== NULL
) {
338 /* skip over first byte, as for some reason it's a vertical tab character */
342 /* answer is not null-terminated, but should be */
343 /* this is safe to do, as answer has extra bytes on the end we can
344 safely overwrite with a null */
346 /* now increment len so that len includes the null, so that we can
347 compare apples to apples */
350 /* finally, copy the answer into c->txt */
351 ast_copy_string(c
->txt
, answer
, len
< c
->txtlen
? len
: (c
->txtlen
));
353 /* just to be safe, let's make sure c->txt is null terminated */
354 c
->txt
[(c
->txtlen
)-1] = '\0';
359 /*! \brief Callback from ENUM lookup function */
360 static int enum_callback(void *context
, char *answer
, int len
, char *fullanswer
)
362 struct enum_context
*c
= context
;
366 res
= parse_naptr(c
->dst
, c
->dstlen
, c
->tech
, c
->techlen
, answer
, len
, c
->naptrinput
);
369 ast_log(LOG_WARNING
, "Failed to parse naptr :(\n");
371 } else if (res
> 0 && !ast_strlen_zero(c
->dst
)){ /* ok, we got needed NAPTR */
372 if (c
->options
& ENUMLOOKUP_OPTIONS_COUNT
){ /* counting RRs */
374 snprintf(c
->dst
, c
->dstlen
, "%d", c
->position
);
376 if ((p
= ast_realloc(c
->naptr_rrs
, sizeof(*c
->naptr_rrs
) * (c
->naptr_rrs_count
+ 1)))) {
378 memcpy(&c
->naptr_rrs
[c
->naptr_rrs_count
].naptr
, answer
, sizeof(c
->naptr_rrs
->naptr
));
379 /* printf("order=%d, pref=%d\n", ntohs(c->naptr_rrs[c->naptr_rrs_count].naptr.order), ntohs(c->naptr_rrs[c->naptr_rrs_count].naptr.pref)); */
380 c
->naptr_rrs
[c
->naptr_rrs_count
].result
= strdup(c
->dst
);
381 c
->naptr_rrs
[c
->naptr_rrs_count
].tech
= strdup(c
->tech
);
382 c
->naptr_rrs
[c
->naptr_rrs_count
].sort_pos
= c
->naptr_rrs_count
;
383 c
->naptr_rrs_count
++;
390 if (c
->options
& ENUMLOOKUP_OPTIONS_COUNT
) { /* counting RRs */
391 snprintf(c
->dst
, c
->dstlen
, "%d", c
->position
);
397 /*! \brief ENUM lookup */
398 int ast_get_enum(struct ast_channel
*chan
, const char *number
, char *dst
, int dstlen
, char *tech
, int techlen
, char* suffix
, char* options
)
400 struct enum_context context
;
402 char naptrinput
[512];
403 int pos
= strlen(number
) - 1;
406 struct enum_search
*s
= NULL
;
408 /* for ISN rewrite */
415 ast_copy_string(naptrinput
, number
[0] == 'n' ? number
+1 : number
, sizeof(naptrinput
));
417 context
.naptrinput
= naptrinput
; /* The number */
418 context
.dst
= dst
; /* Return string */
419 context
.dstlen
= dstlen
;
421 context
.techlen
= techlen
;
423 context
.position
= 1;
424 context
.naptr_rrs
= NULL
;
425 context
.naptr_rrs_count
= 0;
427 if (options
!= NULL
) {
428 if (*options
== 'c') {
429 context
.options
= ENUMLOOKUP_OPTIONS_COUNT
;
430 context
.position
= 0;
432 context
.position
= atoi(options
);
433 if (context
.position
< 1)
434 context
.position
= 1;
442 p1
= strchr(number
, '*');
444 if (number
[0] == 'n') { /* do not perform ISN rewrite ('n' is testing flag) */
446 k
= 1; /* strip 'n' from number */
457 while(*p2
&& newpos
< 128){
466 if (isdigit(number
[pos
])) {
467 tmp
[newpos
++] = number
[pos
];
474 if (chan
&& ast_autoservice_start(chan
) < 0)
478 ast_mutex_lock(&enumlock
);
479 if (version
!= enumver
) {
480 /* Ooh, a reload... */
486 ast_copy_string(tmp
+ newpos
, suffix
? suffix
: s
->toplev
, sizeof(tmp
) - newpos
);
487 ast_mutex_unlock(&enumlock
);
490 ret
= ast_search_dns(&context
, tmp
, C_IN
, T_NAPTR
, enum_callback
);
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();