63 #include <sys/socket.h>
64 #include <netinet/in.h>
65 #include <arpa/nameser.h>
67 #include <arpa/nameser_compat.h>
91 static char ienum_branchlabel[32] =
"i";
93 #define ENUMLOOKUP_BLR_CC 0
94 #define ENUMLOOKUP_BLR_TXT 1
95 #define ENUMLOOKUP_BLR_EBL 2
96 static int ebl_alg = ENUMLOOKUP_BLR_CC;
101 AST_MUTEX_DEFINE_STATIC(enumlock);
121 if (!number || (strlen(number) < 3)) {
125 strncpy(digits, number, 2);
127 if (!sscanf(digits,
"%30d", &cc)) {
131 if (cc / 10 == 1 || cc / 10 == 7)
134 if (cc == 20 || cc == 27 || (cc >= 30 && cc <= 34) || cc == 36 ||
135 cc == 39 || cc == 40 || cc == 41 || (cc >= 40 && cc <= 41) ||
136 (cc >= 43 && cc <= 49) || (cc >= 51 && cc <= 58) ||
137 (cc >= 60 && cc <= 66) || cc == 81 || cc == 82 || cc == 84 ||
138 cc == 86 || (cc >= 90 && cc <= 95) || cc == 98) {
151 static int txt_callback(
void *context,
unsigned char *answer,
int len,
unsigned char *fullanswer)
159 if (answer == NULL) {
175 ast_log(LOG_WARNING,
"txt_callback: malformed TXT record.\n");
179 if (i >=
sizeof(c->txt)) {
180 ast_log(LOG_WARNING,
"txt_callback: TXT record too long.\n");
181 i =
sizeof(c->txt) - 1;
201 static int blr_txt(
const char *cc,
const char *suffix)
204 char domain[128] =
"";
208 ast_mutex_lock(&enumlock);
210 ast_verb(4,
"blr_txt() cc='%s', suffix='%s', c_bl='%s'\n", cc, suffix, ienum_branchlabel);
212 if (
sizeof(domain) < (strlen(cc) * 2 + strlen(ienum_branchlabel) + strlen(suffix) + 2)) {
213 ast_mutex_unlock(&enumlock);
214 ast_log(LOG_WARNING,
"ERROR: string sizing in blr_txt.\n");
218 p1 = domain + snprintf(domain,
sizeof(domain),
"%s.", ienum_branchlabel);
219 ast_mutex_unlock(&enumlock);
221 for (p2 = (
char *) cc + strlen(cc) - 1; p2 >= cc; p2--) {
229 ast_verb(4,
"blr_txt() FQDN for TXT record: %s, cc was %s\n", domain, cc);
234 ret = atoi(context.txt);
236 if ((ret >= 0) && (ret < 20)) {
237 ast_verb(3,
"blr_txt() BLR TXT record for %s is %d (apex: %s)\n", cc, ret, suffix);
242 ast_verb(3,
"blr_txt() BLR TXT record for %s not found (apex: %s)\n", cc, suffix);
256 static int ebl_callback(
void *context,
unsigned char *answer,
int len,
unsigned char *fullanswer)
267 if (answer == NULL) {
290 if ((c->pos > 15) || len < 2) {
291 ast_log(LOG_WARNING,
"ebl_callback: malformed EBL record.\n");
298 ast_log(LOG_WARNING,
"ebl_callback: malformed EBL record.\n");
308 if ((i = dn_expand((
unsigned char *)fullanswer, (
unsigned char *)answer + len,
309 (
unsigned char *)answer, c->apex,
sizeof(c->apex) - 1)) < 0) {
310 ast_log(LOG_WARNING,
"Failed to expand hostname\n");
330 static int blr_ebl(
const char *cc,
const char *suffix,
char *separator,
int sep_len,
char* apex,
int apex_len)
333 char domain[128] =
"";
337 ast_mutex_lock(&enumlock);
339 ast_verb(4,
"blr_ebl() cc='%s', suffix='%s', c_bl='%s'\n", cc, suffix, ienum_branchlabel);
341 if (
sizeof(domain) < (strlen(cc) * 2 + strlen(ienum_branchlabel) + strlen(suffix) + 2)) {
342 ast_mutex_unlock(&enumlock);
343 ast_log(LOG_WARNING,
"ERROR: string sizing in blr_EBL.\n");
347 p1 = domain + snprintf(domain,
sizeof(domain),
"%s.", ienum_branchlabel);
348 ast_mutex_unlock(&enumlock);
350 for (p2 = (
char *) cc + strlen(cc) - 1; p2 >= cc; p2--) {
358 ast_verb(4,
"blr_ebl() FQDN for EBL record: %s, cc was %s\n", domain, cc);
364 if ((ret >= 0) && (ret < 20)) {
365 ast_verb(3,
"blr_txt() BLR EBL record for %s is %d/%s/%s)\n", cc, ret, context.separator, context.apex);
371 ast_verb(3,
"blr_txt() BLR EBL record for %s not found (apex: %s)\n", cc, suffix);
376 static unsigned int parse_ie(
char *data,
unsigned int maxdatalen,
unsigned char *src,
unsigned int srclen)
378 unsigned int len, olen;
380 len = olen = (
unsigned int) src[0];
385 ast_log(LOG_WARNING,
"ENUM parsing failed: Wanted %u characters, got %u\n", len, srclen);
389 if (len > maxdatalen)
391 memcpy(data, src, len);
397 static int parse_naptr(
unsigned char *dst,
int dstsize,
char *tech,
int techsize,
unsigned char *answer,
int len,
unsigned char *naptrinput)
399 char tech_return[80];
400 char *oanswer = (
char *)answer;
401 char flags[512] =
"";
402 char services[512] =
"";
404 char regexp[512] =
"";
406 char tempdst[512] =
"";
407 char errbuff[512] =
"";
410 char *pattern, *subst, *d;
413 static const int max_bt = 10;
414 int size, matchindex;
415 size_t d_len =
sizeof(tempdst) - 1;
417 int re_flags = REG_EXTENDED | REG_NEWLINE;
418 regmatch_t pmatch[max_bt];
420 tech_return[0] =
'\0';
423 if (len <
sizeof(
struct naptr)) {
424 ast_log(LOG_WARNING,
"NAPTR record length too short\n");
427 answer +=
sizeof(
struct naptr);
428 len -=
sizeof(
struct naptr);
429 if ((res =
parse_ie(flags,
sizeof(flags) - 1, answer, len)) < 0) {
430 ast_log(LOG_WARNING,
"Failed to get flags from NAPTR record\n");
437 if ((res =
parse_ie(services,
sizeof(services) - 1, answer, len)) < 0) {
438 ast_log(LOG_WARNING,
"Failed to get services from NAPTR record\n");
444 if ((res =
parse_ie(regexp,
sizeof(regexp) - 1, answer, len)) < 0) {
445 ast_log(LOG_WARNING,
"Failed to get regexp from NAPTR record\n");
452 if ((res = dn_expand((
unsigned char *)oanswer, (
unsigned char *)answer + len, (
unsigned char *)answer, repl,
sizeof(repl) - 1)) < 0) {
453 ast_log(LOG_WARNING,
"Failed to expand hostname\n");
457 ast_debug(3,
"NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
458 naptrinput, flags, services, regexp, repl);
461 if (tolower(flags[0]) !=
'u') {
462 ast_log(LOG_WARNING,
"NAPTR Flag must be 'U' or 'u'.\n");
466 p = strstr(services,
"e2u+");
468 p = strstr(services,
"E2U+");
472 p = strchr(p,
':') + 1;
477 p = strstr(services,
"+e2u");
479 p = strstr(services,
"+E2U");
482 p = strchr(services,
':');
489 regexp_len = strlen(regexp);
490 if (regexp_len < 7) {
491 ast_log(LOG_WARNING,
"Regex too short to be meaningful.\n");
498 delim2 = strchr(regexp + 1, delim);
500 || ((regexp[regexp_len - 1] !=
'i' || regexp[regexp_len - 2] != delim)
501 && regexp[regexp_len - 1] != delim)) {
502 ast_log(LOG_WARNING,
"Regex delimiter error (on \"%s\").\n", regexp);
504 }
else if (strchr((delim2 + 1), delim) == NULL) {
505 ast_log(LOG_WARNING,
"Regex delimiter error (on \"%s\").\n", regexp);
512 if (regexp[regexp_len - 1] ==
'i') {
513 re_flags |= REG_ICASE;
516 pattern = regexp + 1;
519 regexp[regexp_len - 1] = 0;
525 if (regcomp(&preg, pattern, re_flags)) {
526 ast_log(LOG_WARNING,
"NAPTR Regex compilation error (regex = \"%s\").\n", regexp);
530 if (preg.re_nsub > ARRAY_LEN(pmatch)) {
531 ast_log(LOG_WARNING,
"NAPTR Regex compilation error: too many subs.\n");
537 if ((rc = regexec(&preg, (
char *) naptrinput, max_bt, pmatch, 0))) {
538 regerror(rc, &preg, errbuff,
sizeof(errbuff));
539 ast_log(LOG_WARNING,
"NAPTR Regex match failed. Reason: %s\n", errbuff);
553 while (*subst && (d_len > 0)) {
554 if ((subst[0] ==
'\\') && isdigit(subst[1])) {
555 matchindex = (int) (subst[1] -
'0');
556 if (matchindex >= ARRAY_LEN(pmatch)) {
557 ast_log(LOG_WARNING,
"Error during regex substitution. Invalid pmatch index.\n");
561 size = pmatch[matchindex].rm_eo - pmatch[matchindex].rm_so;
563 ast_log(LOG_WARNING,
"Not enough space during NAPTR regex substitution.\n");
567 if ((strlen((
char *) naptrinput) >= pmatch[matchindex].rm_eo) && (pmatch[matchindex].rm_so <= pmatch[matchindex].rm_eo)) {
568 memcpy(d, (naptrinput + (
int) pmatch[matchindex].rm_so), size);
573 ast_log(LOG_WARNING,
"Error during regex substitution. Invalid backreference index.\n");
576 }
else if (isprint(*subst)) {
580 ast_log(LOG_WARNING,
"Error during regex substitution.\n");
586 dst[dstsize - 1] =
'\0';
589 if (!strncasecmp(tech,
"ALL", techsize)){
592 if (!strncasecmp(tech_return, tech,
sizeof(tech_return) < techsize ?
sizeof(tech_return): techsize)){
607 #define ENUMLOOKUP_OPTIONS_COUNT 1
609 #define ENUMLOOKUP_OPTIONS_ISN 2
611 #define ENUMLOOKUP_OPTIONS_IENUM 4
613 #define ENUMLOOKUP_OPTIONS_DIRECT 8
616 static int enum_callback(
void *context,
unsigned char *answer,
int len,
unsigned char *fullanswer)
625 ast_log(LOG_WARNING,
"Failed to parse naptr\n");
627 }
else if ((res == 0) && !ast_strlen_zero(c->
dst)) {
628 if (c->
options & ENUMLOOKUP_OPTIONS_COUNT) {
667 struct timeval time_start, time_end;
669 if (ast_strlen_zero(suffix)) {
670 ast_log(LOG_WARNING,
"ast_get_enum need a suffix parameter now.\n");
674 ast_debug(2,
"num='%s', tech='%s', suffix='%s', options='%s', record=%u\n", number, tech, suffix, options, record);
687 ast_copy_string(naptrinput, number[0] ==
'n' ? number + 1 : number,
sizeof(naptrinput));
688 if (number[0] ==
'+') {
692 if (!(context =
ast_calloc(1,
sizeof(*context)))) {
696 if ((p3 = strchr(naptrinput,
'*'))) {
703 context->
tech = tech;
706 context->
position = record > 0 ? record : 1;
720 if (options != NULL) {
721 if (strchr(options,
's')) {
722 context->
options |= ENUMLOOKUP_OPTIONS_ISN;
723 }
else if (strchr(options,
'i')) {
724 context->
options |= ENUMLOOKUP_OPTIONS_IENUM;
725 }
else if (strchr(options,
'd')) {
726 context->
options |= ENUMLOOKUP_OPTIONS_DIRECT;
728 if (strchr(options,
'c')) {
729 context->
options |= ENUMLOOKUP_OPTIONS_COUNT;
731 if (strchr(number,
'*')) {
732 context->
options |= ENUMLOOKUP_OPTIONS_ISN;
736 ast_debug(1,
"n='%s', tech='%s', suffix='%s', options='%d', record='%d'\n",
762 if ((context->
options & ENUMLOOKUP_OPTIONS_ISN) && (p1 = strchr(number,
'*'))) {
767 ast_debug(2,
"ISN ENUM: left=%s, middle='%s'\n", left, middle);
769 }
else if (context->
options & ENUMLOOKUP_OPTIONS_DIRECT) {
773 ast_debug(2,
"DIRECT ENUM: middle='%s'\n", middle);
775 }
else if (context->
options & ENUMLOOKUP_OPTIONS_IENUM) {
778 char sep[256], n_apex[256];
779 int cc_len =
cclen(number);
781 ast_mutex_lock(&enumlock);
783 ast_mutex_unlock(&enumlock);
786 case ENUMLOOKUP_BLR_EBL:
788 sdl =
blr_ebl(cc, suffix, sep,
sizeof(sep) - 1, n_apex,
sizeof(n_apex) - 1);
792 ast_debug(2,
"EBL ENUM: sep=%s, apex='%s'\n", sep, n_apex);
797 case ENUMLOOKUP_BLR_TXT:
806 case ENUMLOOKUP_BLR_CC:
812 if (sdl > strlen(number)) {
813 ast_log(LOG_WARNING,
"I-ENUM: subdomain location %d behind number %s\n", sdl, number);
819 ast_mutex_lock(&enumlock);
822 ast_mutex_unlock(&enumlock);
825 if ((sdl * 2 + strlen(middle) + 2) >
sizeof(middle)) {
826 ast_log(LOG_WARNING,
"ast_get_enum: not enough space for I-ENUM rewrite.\n");
831 p1 = middle + strlen(middle);
832 for (p2 = (
char *) number + sdl - 1; p2 >= number; p2--) {
840 ast_debug(2,
"I-ENUM: cclen=%d, left=%s, middle='%s', apex='%s'\n", cc_len, left, middle, apex);
843 if (strlen(left) * 2 + 2 >
sizeof(domain)) {
844 ast_log(LOG_WARNING,
"string to long in ast_get_enum\n");
851 for (p2 = left + strlen(left); p2 >= left; p2--) {
864 spaceleft =
sizeof(tmp) - 2;
866 spaceleft -= strlen(domain);
869 strncat(tmp, middle, spaceleft);
870 spaceleft -= strlen(middle);
873 strncat(tmp,apex,spaceleft);
878 ast_debug(2,
"profiling: %s, %s, %" PRIi64
" ms\n",
879 (ret == 0) ?
"OK" :
"FAIL", tmp,
ast_tvdiff_ms(time_end, time_start));
882 ast_debug(1,
"No such number found: %s (%s)\n", tmp, strerror(errno));
921 }
else if (!(context->
options & ENUMLOOKUP_OPTIONS_COUNT)) {
923 }
else if ((context->
options & ENUMLOOKUP_OPTIONS_COUNT)) {
939 *argcontext = context;
955 static char *format_numeric_domain(
const char *
number,
const char *suffix)
958 size_t suffix_length;
959 size_t number_length = strlen(number);
960 const char *src = number + number_length - 1;
963 suffix =
"e164.arpa";
966 suffix_length = strlen(suffix);
975 while (src >= number) {
985 if (*suffix ==
'.') {
986 memcpy(dst, &suffix[1], suffix_length);
988 memcpy(dst, suffix, suffix_length + 1);
1000 int autoservice = 0;
1002 ast_debug(4,
"ast_get_txt: Number = '%s', suffix = '%s'\n", number, suffix);
1004 domain = format_numeric_domain(number, suffix);
1018 ast_debug(2,
"No such number found in ENUM: %s (%s)\n", domain, strerror(errno));
1036 if ((cfg =
ast_config_load2(
"enum.conf",
"enum", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
1038 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1043 ast_mutex_lock(&enumlock);
1045 if ((
string = ast_variable_retrieve(cfg,
"ienum",
"branchlabel"))) {
1046 ast_copy_string(ienum_branchlabel,
string,
sizeof(ienum_branchlabel));
1049 if ((
string = ast_variable_retrieve(cfg,
"ienum",
"ebl_alg"))) {
1050 ebl_alg = ENUMLOOKUP_BLR_CC;
1052 if (!strcasecmp(
string,
"txt"))
1053 ebl_alg = ENUMLOOKUP_BLR_TXT;
1054 else if (!strcasecmp(
string,
"ebl"))
1055 ebl_alg = ENUMLOOKUP_BLR_EBL;
1056 else if (!strcasecmp(
string,
"cc"))
1057 ebl_alg = ENUMLOOKUP_BLR_CC;
1059 ast_log(LOG_WARNING,
"No valid parameter for ienum/ebl_alg.\n");
1063 ast_mutex_unlock(&enumlock);
1067 static int load_module(
void)
1072 static int unload_module(
void)
1077 static int reload_module(
void)
1082 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER,
"ENUM Support",
1083 .support_level = AST_MODULE_SUPPORT_CORE,
1084 .load = load_module,
1085 .unload = unload_module,
1086 .reload = reload_module,
1088 .requires =
"extconfig",
static int enum_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
Callback from ENUM lookup function.
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
#define ast_realloc(p, len)
A wrapper for realloc()
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
struct enum_naptr_rr * naptr_rrs
static int private_enum_init(int reload)
Initialize the ENUM support subsystem.
int ast_search_dns(void *context, const char *dname, int class, int type, int(*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
Perform DNS lookup (used by DNS, enum and SRV lookups)
static int cclen(const char *number)
Determine the length of a country code when given an E.164 string.
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
#define ast_strdup(str)
A wrapper for strdup()
Configuration File Parser.
General Asterisk PBX channel definitions.
#define ast_malloc(len)
A wrapper for malloc()
#define ast_debug(level,...)
Log a DEBUG message.
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, struct enum_context **argcontext)
Lookup entry in ENUM.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
static int txt_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
Callback for TXT record lookup, /ol version.
static unsigned int parse_ie(char *data, unsigned int maxdatalen, unsigned char *src, unsigned int srclen)
Parse NAPTR record information elements.
#define ast_calloc(num, len)
A wrapper for calloc()
int ast_get_txt(struct ast_channel *chan, const char *number, char *txt, int txtlen, char *suffix)
Lookup DNS TXT record (used by app TXTCIDnum)
static int ebl_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
Callback for EBL record lookup.
Module could not be loaded properly.
Structure used to handle boolean flags.
static int parse_naptr(unsigned char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len, unsigned char *naptrinput)
Parse DNS NAPTR record used in ENUM —.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
static int blr_txt(const char *cc, const char *suffix)
Determine the branch location record as stored in a TXT record.
DNS support for Asterisk.
static int blr_ebl(const char *cc, const char *suffix, char *separator, int sep_len, char *apex, int apex_len)
Evaluate the I-ENUM branch as stored in an EBL record.