Asterisk - The Open Source Telephony Project  21.4.1
Data Structures | Macros | Functions | Variables
enum.c File Reference

ENUM Support for Asterisk. More...

#include "asterisk.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <ctype.h>
#include <regex.h>
#include "asterisk/module.h"
#include "asterisk/enum.h"
#include "asterisk/dns.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/manager.h"

Go to the source code of this file.

Data Structures

struct  ebl_context
 
struct  txt_context
 

Macros

#define ENUMLOOKUP_BLR_CC   0
 
#define ENUMLOOKUP_BLR_EBL   2
 
#define ENUMLOOKUP_BLR_TXT   1
 
#define ENUMLOOKUP_OPTIONS_COUNT   1
 
#define ENUMLOOKUP_OPTIONS_DIRECT   8
 
#define ENUMLOOKUP_OPTIONS_IENUM   4
 
#define ENUMLOOKUP_OPTIONS_ISN   2
 
#define T_EBL   65300
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
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. More...
 
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) More...
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
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.
 
static int blr_txt (const char *cc, const char *suffix)
 Determine the branch location record as stored in a TXT record.
 
static int cclen (const char *number)
 Determine the length of a country code when given an E.164 string.
 
static int ebl_callback (void *context, unsigned char *answer, int len, unsigned char *fullanswer)
 Callback for EBL record lookup.
 
static int enum_callback (void *context, unsigned char *answer, int len, unsigned char *fullanswer)
 Callback from ENUM lookup function.
 
static char * format_numeric_domain (const char *number, const char *suffix)
 
static int load_module (void)
 
static unsigned int parse_ie (char *data, unsigned int maxdatalen, unsigned char *src, unsigned int srclen)
 Parse NAPTR record information elements.
 
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 —.
 
static int private_enum_init (int reload)
 Initialize the ENUM support subsystem.
 
static int reload_module (void)
 
static int txt_callback (void *context, unsigned char *answer, int len, unsigned char *fullanswer)
 Callback for TXT record lookup, /ol version.
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "ENUM Support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "da6642af068ee5e6490c5b1d2cc1d238" , .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload_module, .load_pri = AST_MODPRI_CORE, .requires = "extconfig", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static int ebl_alg = ENUMLOOKUP_BLR_CC
 
static ast_mutex_t enumlock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} }
 
static char ienum_branchlabel [32] = "i"
 

Detailed Description

ENUM Support for Asterisk.

Author
Mark Spencer marks.nosp@m.ter@.nosp@m.digiu.nosp@m.m.co.nosp@m.m
Enum standards
Possible improvement
Todo:

Implement a caching mechanism for multile enum lookups

The service type selection needs to be redone.

Definition in file enum.c.

Function Documentation

int ast_get_enum ( struct ast_channel chan,
const char *  number,
char *  location,
int  maxloc,
char *  technology,
int  maxtech,
char *  suffix,
char *  options,
unsigned int  record,
struct enum_context **  argcontext 
)

Lookup entry in ENUM.

Parameters
chanChannel
numberE164 number with or without the leading +
locationNumber returned (or SIP uri)
maxlocMax length
technologyTechnology (from url scheme in response) You can set it to get particular answer RR, if there are many techs in DNS response, example: "sip" If you need any record, then set it to "ALL" string
maxtechMax length
suffixZone suffix (WARNING: No defaults here any more)
optionsOptions 'c' - Count number of NAPTR RR number - Position of the requested RR in the answer list 'u' - Full URI return (does not strip URI scheme) 'i' - Infrastructure ENUM lookup 's' - ISN based lookup 'd' - Direct DNS query
recordThe position of required RR in the answer list
argcontextArgument for caching results into an enum_context pointer (NULL is used for not caching)
Return values
1if found
0if not found
-1on hangup

Definition at line 649 of file enum.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_calloc, ast_copy_string(), ast_debug, ast_search_dns(), ast_tvdiff_ms(), ast_tvnow(), blr_ebl(), blr_txt(), cclen(), enum_context::count, enum_context::dst, enum_context::dstlen, enum_callback(), enum_naptr_rr::naptr, enum_context::naptr_rrs, enum_context::naptr_rrs_count, enum_context::naptrinput, enum_context::options, enum_context::position, enum_naptr_rr::result, enum_naptr_rr::sort_pos, enum_naptr_rr::tech, enum_context::tech, and enum_context::techlen.

650 {
651  struct enum_context *context;
652  char tmp[512];
653  char domain[256];
654  char left[128];
655  char middle[128];
656  char naptrinput[128];
657  char apex[128] = "";
658  int ret = -1;
659  /* for ISN rewrite */
660  char *p1 = NULL;
661  char *p2 = NULL;
662  char *p3 = NULL;
663  int k = 0;
664  int i = 0;
665  int z = 0;
666  int spaceleft = 0;
667  struct timeval time_start, time_end;
668 
669  if (ast_strlen_zero(suffix)) {
670  ast_log(LOG_WARNING, "ast_get_enum need a suffix parameter now.\n");
671  return -1;
672  }
673 
674  ast_debug(2, "num='%s', tech='%s', suffix='%s', options='%s', record=%u\n", number, tech, suffix, options, record);
675 
676 /*
677  We don't need that any more, that "n" preceding the number has been replaced by a flag
678  in the options paramter.
679  ast_copy_string(naptrinput, number, sizeof(naptrinput));
680 */
681 /*
682  * The "number" parameter includes a leading '+' if it's a full E.164 number (and not ISN)
683  * We need to preserve that as the regex inside NAPTRs expect the +.
684  *
685  * But for the domain generation, the '+' is a nuisance, so we get rid of it.
686 */
687  ast_copy_string(naptrinput, number[0] == 'n' ? number + 1 : number, sizeof(naptrinput));
688  if (number[0] == '+') {
689  number++;
690  }
691 
692  if (!(context = ast_calloc(1, sizeof(*context)))) {
693  return -1;
694  }
695 
696  if ((p3 = strchr(naptrinput, '*'))) {
697  *p3='\0';
698  }
699 
700  context->naptrinput = naptrinput; /* The number */
701  context->dst = dst; /* Return string */
702  context->dstlen = dstlen;
703  context->tech = tech;
704  context->techlen = techlen;
705  context->options = 0;
706  context->position = record > 0 ? record : 1;
707  context->count = 0;
708  context->naptr_rrs = NULL;
709  context->naptr_rrs_count = 0;
710 
711  /*
712  * Process options:
713  *
714  * c Return count, not URI
715  * i Use infrastructure ENUM
716  * s Do ISN transformation
717  * d Direct DNS query: no reversing.
718  *
719  */
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;
727  }
728  if (strchr(options,'c')) {
729  context->options |= ENUMLOOKUP_OPTIONS_COUNT;
730  }
731  if (strchr(number,'*')) {
732  context->options |= ENUMLOOKUP_OPTIONS_ISN;
733  }
734  }
735  ast_debug(2, "ENUM options(%s): pos=%d, options='%d'\n", options, context->position, context->options);
736  ast_debug(1, "n='%s', tech='%s', suffix='%s', options='%d', record='%d'\n",
737  number, tech, suffix, context->options, context->position);
738 
739  /*
740  * This code does more than simple RFC3261 ENUM. All these rewriting
741  * schemes have in common that they build the FQDN for the NAPTR lookup
742  * by concatenating
743  * - a number which needs be flipped and "."-seperated (left)
744  * - some fixed string (middle)
745  * - an Apex. (apex)
746  *
747  * The RFC3261 ENUM is: left=full number, middle="", apex=from args.
748  * ISN: number = "middle*left", apex=from args
749  * I-ENUM: EBL parameters build the split, can change apex
750  * Direct: left="", middle=argument, apex=from args
751  *
752  */
753 
754  /* default: the whole number will be flipped, no middle domain component */
755  ast_copy_string(left, number, sizeof(left));
756  middle[0] = '\0';
757  /*
758  * I-ENUM can change the apex, thus we copy it
759  */
760  ast_copy_string(apex, suffix, sizeof(apex));
761  /* ISN rewrite */
762  if ((context->options & ENUMLOOKUP_OPTIONS_ISN) && (p1 = strchr(number, '*'))) {
763  *p1++ = '\0';
764  ast_copy_string(left, number, sizeof(left));
765  ast_copy_string(middle, p1, sizeof(middle) - 1);
766  strcat(middle, ".");
767  ast_debug(2, "ISN ENUM: left=%s, middle='%s'\n", left, middle);
768  /* Direct DNS lookup rewrite */
769  } else if (context->options & ENUMLOOKUP_OPTIONS_DIRECT) {
770  left[0] = 0; /* nothing to flip around */
771  ast_copy_string(middle, number, sizeof(middle) - 1);
772  strcat(middle, ".");
773  ast_debug(2, "DIRECT ENUM: middle='%s'\n", middle);
774  /* Infrastructure ENUM rewrite */
775  } else if (context->options & ENUMLOOKUP_OPTIONS_IENUM) {
776  int sdl = 0;
777  char cc[8];
778  char sep[256], n_apex[256];
779  int cc_len = cclen(number);
780  sdl = cc_len;
781  ast_mutex_lock(&enumlock);
782  ast_copy_string(sep, ienum_branchlabel, sizeof(sep)); /* default */
783  ast_mutex_unlock(&enumlock);
784 
785  switch (ebl_alg) {
786  case ENUMLOOKUP_BLR_EBL:
787  ast_copy_string(cc, number, cc_len); /* cclen() never returns more than 3 */
788  sdl = blr_ebl(cc, suffix, sep, sizeof(sep) - 1, n_apex, sizeof(n_apex) - 1);
789 
790  if (sdl >= 0) {
791  ast_copy_string(apex, n_apex, sizeof(apex));
792  ast_debug(2, "EBL ENUM: sep=%s, apex='%s'\n", sep, n_apex);
793  } else {
794  sdl = cc_len;
795  }
796  break;
797  case ENUMLOOKUP_BLR_TXT:
798  ast_copy_string(cc, number, cc_len); /* cclen() never returns more than 3 */
799  sdl = blr_txt(cc, suffix);
800 
801  if (sdl < 0) {
802  sdl = cc_len;
803  }
804  break;
805 
806  case ENUMLOOKUP_BLR_CC: /* BLR is at the country-code level */
807  default:
808  sdl = cc_len;
809  break;
810  }
811 
812  if (sdl > strlen(number)) { /* Number too short for this sdl? */
813  ast_log(LOG_WARNING, "I-ENUM: subdomain location %d behind number %s\n", sdl, number);
814  ast_free(context);
815  return 0;
816  }
817  ast_copy_string(left, number + sdl, sizeof(left));
818 
819  ast_mutex_lock(&enumlock);
820  ast_copy_string(middle, sep, sizeof(middle) - 1);
821  strcat(middle, ".");
822  ast_mutex_unlock(&enumlock);
823 
824  /* check the space we need for middle */
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");
827  ast_free(context);
828  return -1;
829  }
830 
831  p1 = middle + strlen(middle);
832  for (p2 = (char *) number + sdl - 1; p2 >= number; p2--) {
833  if (isdigit(*p2)) {
834  *p1++ = *p2;
835  *p1++ = '.';
836  }
837  }
838  *p1 = '\0';
839 
840  ast_debug(2, "I-ENUM: cclen=%d, left=%s, middle='%s', apex='%s'\n", cc_len, left, middle, apex);
841  }
842 
843  if (strlen(left) * 2 + 2 > sizeof(domain)) {
844  ast_log(LOG_WARNING, "string to long in ast_get_enum\n");
845  ast_free(context);
846  return -1;
847  }
848 
849  /* flip left into domain */
850  p1 = domain;
851  for (p2 = left + strlen(left); p2 >= left; p2--) {
852  if (isdigit(*p2)) {
853  *p1++ = *p2;
854  *p1++ = '.';
855  }
856  }
857  *p1 = '\0';
858 
859  if (chan && ast_autoservice_start(chan) < 0) {
860  ast_free(context);
861  return -1;
862  }
863 
864  spaceleft = sizeof(tmp) - 2;
865  ast_copy_string(tmp, domain, spaceleft);
866  spaceleft -= strlen(domain);
867 
868  if (*middle) {
869  strncat(tmp, middle, spaceleft);
870  spaceleft -= strlen(middle);
871  }
872 
873  strncat(tmp,apex,spaceleft);
874  time_start = ast_tvnow();
875  ret = ast_search_dns(context, tmp, C_IN, T_NAPTR, enum_callback);
876  time_end = ast_tvnow();
877 
878  ast_debug(2, "profiling: %s, %s, %" PRIi64 " ms\n",
879  (ret == 0) ? "OK" : "FAIL", tmp, ast_tvdiff_ms(time_end, time_start));
880 
881  if (ret < 0) {
882  ast_debug(1, "No such number found: %s (%s)\n", tmp, strerror(errno));
883  context->naptr_rrs_count = -1;
884  strcpy(dst, "0");
885  ret = 0;
886  }
887 
888  if (context->naptr_rrs_count >= context->position && ! (context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
889  /* sort array by NAPTR order/preference */
890  for (k = 0; k < context->naptr_rrs_count; k++) {
891  for (i = 0; i < context->naptr_rrs_count; i++) {
892  /* use order first and then preference to compare */
893  if ((ntohs(context->naptr_rrs[k].naptr.order) < ntohs(context->naptr_rrs[i].naptr.order)
894  && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
895  || (ntohs(context->naptr_rrs[k].naptr.order) > ntohs(context->naptr_rrs[i].naptr.order)
896  && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) {
897  z = context->naptr_rrs[k].sort_pos;
898  context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
899  context->naptr_rrs[i].sort_pos = z;
900  continue;
901  }
902  if (ntohs(context->naptr_rrs[k].naptr.order) == ntohs(context->naptr_rrs[i].naptr.order)) {
903  if ((ntohs(context->naptr_rrs[k].naptr.pref) < ntohs(context->naptr_rrs[i].naptr.pref)
904  && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
905  || (ntohs(context->naptr_rrs[k].naptr.pref) > ntohs(context->naptr_rrs[i].naptr.pref)
906  && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) {
907  z = context->naptr_rrs[k].sort_pos;
908  context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
909  context->naptr_rrs[i].sort_pos = z;
910  }
911  }
912  }
913  }
914  for (k = 0; k < context->naptr_rrs_count; k++) {
915  if (context->naptr_rrs[k].sort_pos == context->position - 1) {
916  ast_copy_string(context->dst, context->naptr_rrs[k].result, dstlen);
917  ast_copy_string(context->tech, context->naptr_rrs[k].tech, techlen);
918  break;
919  }
920  }
921  } else if (!(context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
922  context->dst[0] = 0;
923  } else if ((context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
924  snprintf(context->dst, context->dstlen, "%d", context->naptr_rrs_count + context->count);
925  }
926 
927  if (chan) {
928  ret |= ast_autoservice_stop(chan);
929  }
930 
931  if (!argcontext) {
932  for (k = 0; k < context->naptr_rrs_count; k++) {
933  ast_free(context->naptr_rrs[k].result);
934  ast_free(context->naptr_rrs[k].tech);
935  }
936  ast_free(context->naptr_rrs);
937  ast_free(context);
938  } else {
939  *argcontext = context;
940  }
941 
942  return ret;
943 }
static int enum_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
Callback from ENUM lookup function.
Definition: enum.c:616
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
int count
Definition: enum.h:51
struct enum_naptr_rr * naptr_rrs
Definition: enum.h:53
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)
Definition: dns.c:491
static int cclen(const char *number)
Determine the length of a country code when given an E.164 string.
Definition: enum.c:116
int dstlen
Definition: enum.h:44
int position
Definition: enum.h:50
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
Number structure.
Definition: app_followme.c:154
int techlen
Definition: enum.h:46
char * dst
Definition: enum.h:43
struct naptr naptr
Definition: enum.h:36
char * naptrinput
Definition: enum.h:49
#define ast_debug(level,...)
Log a DEBUG message.
char * tech
Definition: enum.h:38
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
char * result
Definition: enum.h:37
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
int naptr_rrs_count
Definition: enum.h:54
char * tech
Definition: enum.h:45
int sort_pos
Definition: enum.h:39
int options
Definition: enum.h:52
static int blr_txt(const char *cc, const char *suffix)
Determine the branch location record as stored in a TXT record.
Definition: enum.c:201
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.
Definition: enum.c:330
int ast_get_txt ( struct ast_channel chan,
const char *  number,
char *  txt,
int  maxtxt,
char *  suffix 
)

Lookup DNS TXT record (used by app TXTCIDnum)

Really has nothing to do with enum, but anyway... Actually, there is now an internet-draft which describes how callerID should be stored in ENUM domains: draft-ietf-enum-cnam-04.txt The algorithm implemented here will thus be obsolete soon.

Parameters
chanChannel
numberE164 number with or without the leading +
txtText string (return value)
maxtxtMax length of "txt"
suffixZone suffix
Version
1.6.1 new suffix parameter to take into account caller ids that aren't in e164.arpa
1.6.1 removed parameters location, maxloc, technology, maxtech as all the information is stored the txt string

Definition at line 995 of file enum.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), ast_debug, ast_search_dns(), and txt_callback().

996 {
997  struct txt_context context;
998  char *domain;
999  int ret;
1000  int autoservice = 0;
1001 
1002  ast_debug(4, "ast_get_txt: Number = '%s', suffix = '%s'\n", number, suffix);
1003 
1004  domain = format_numeric_domain(number, suffix);
1005  if (!domain) {
1006  return -1;
1007  }
1008 
1009  if (chan) {
1010  /* DNS might take a while, so service the channel while we're blocked */
1011  autoservice = !ast_autoservice_start(chan);
1012  }
1013 
1014  ret = ast_search_dns(&context, domain, C_IN, T_TXT, txt_callback);
1015  if (ret > 0) {
1016  ast_copy_string(txt, context.txt, txtlen);
1017  } else {
1018  ast_debug(2, "No such number found in ENUM: %s (%s)\n", domain, strerror(errno));
1019  }
1020 
1021  if (autoservice) {
1022  ast_autoservice_stop(chan);
1023  }
1024 
1025  ast_free(domain);
1026  return 0;
1027 }
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
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)
Definition: dns.c:491
Number structure.
Definition: app_followme.c:154
#define ast_debug(level,...)
Log a DEBUG message.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
static int txt_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
Callback for TXT record lookup, /ol version.
Definition: enum.c:151
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425