Index: doc/enum.txt
===================================================================
--- doc/enum.txt	(revision 28626)
+++ doc/enum.txt	(working copy)
@@ -3,6 +3,8 @@
 2005-09-06 
 jtodd@loligo.com
 
+(Carrier ENUM additions by lendl@nic.at, 2005-10-25)
+
 The ENUMLOOKUP function is more complex than it first may appear, and
 this guide is to give a general overview and set of examples that may
 be well-suited for the advanced user to evaluate in their
@@ -199,8 +201,7 @@
 
   g) Currently, the function ignores the settings in enum.conf as the
   search zone name is now specified within the function, and the H323
-  driver can be chosen by the user via the dialplan.  There were no
-  other values in this file, and so it becomes deprecated.
+  driver can be chosen by the user via the dialplan. 
 
   h) The function will digest and return NAPTRs which use older
   (depricated) style, reversed method strings such as "sip+E2U"
@@ -219,11 +220,47 @@
   the form ABC*DEF (where ABC and DEF are at least one numeric digit)
   then perform an ISN-style lookup where the lookup is manipulated to
   C.B.A.DEF.domain.tld (all other settings and options apply.)  See
-  http://www.freenum.org/ for more details on ISN lookups.  In the
-  unlikely event you wish to avoid ISN re-writes, put an "n" as the
-  first digit of the search string - the "n" will be ignored for the search.
+  http://www.freenum.org/ for more details on ISN lookups.  
+  In previous versions this feature was enable by default, now
+  you need to add an 'i' to the options to perform ISN-style lookups.
 
 
+  k) Carrier ENUM usage: If "r" is contained in the options field, then
+  the lookup will follow the Carrier ENUM rules as proposed in
+  ftp://ftp.rfc-editor.org/in-notes/internet-drafts/draft-haberler-carrier-enum-01.txt
+  Summary: an additional label will be inserted into the ENUM domain
+  name to branch off the carrier tree from the default user-ENUM tree.
+
+  The label to insert is configured in enum.conf as:
+
+  [carrier]
+  branchlabel     => carrier
+
+  Examples:
+  	4.9.7.1.carrier.e164.arpa
+  	4.9.7.carrier.1.e164.arpa
+  	carrier.4.9.7.1.e164.arpa
+ 
+  The position of this label within the DNS tree is controlled by the blr_alg
+  parameter (also in enum.conf). Currently, there are two algorithms to
+  choose from:
+
+  blr_alg         => cc
+  This algorithm always inserts the carrier label at the country-code level.
+  Examples: carrier.1.e164.arpa, carrier.3.4.e164.arpa, carrier.2.5.3.e164.arpa
+  This is the default.
+
+  blr_alg         => txt
+  With this setting, ENUMLOOKUP will look for a TXT record at 
+  <branchlabel>.<reverse-country-code>.<apex> to indicate after how many digits the
+  carrier label should in inserted. e.g. a
+
+  carrier.1.e164.arpa.	IN	TXT	"4"
+
+  record will tell the module to look for carrier ENUM for +1 234 5678 999 at
+  9.9.9.8.7.6.5.carrier.4.3.2.1.e164.arpa.
+
+
 ==EXAMPLES==
 
 All examples below except where noted use "e164.arpa" as the
Index: include/asterisk/enum.h
===================================================================
--- include/asterisk/enum.h	(revision 28626)
+++ include/asterisk/enum.h	(working copy)
@@ -35,9 +35,10 @@
                        If you need any record, then set it to empty string
 	\param maxtech  Max length
 	\param suffix   Zone suffix (if is NULL then use enum.conf 'search' variable)
-	\param options  Options ('c' to count number of NAPTR RR, or number - the position of required RR in the answer list
+	\param options  Options ('c' to count number of NAPTR RR, 'r' for I-ENUM, or 'i' for ITAD processing)
+	\param record   the position of required RR in the answer list
 */
-int ast_get_enum(struct ast_channel *chan, const char *number, char *location, int maxloc, char *technology, int maxtech, char* suffix, char* options);
+int ast_get_enum(struct ast_channel *chan, const char *number, char *location, int maxloc, char *technology, int maxtech, char* suffix, char* options, char* record);
 
 /*!	\brief Lookup DNS TXT record (used by app TXTCIDnum
 	\param chan	Channel
Index: enum.c
===================================================================
--- enum.c	(revision 28626)
+++ enum.c	(working copy)
@@ -40,6 +40,7 @@
  * - See http://bugs.digium.com/view.php?id=6739
  */
 
+#include <sys/times.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -86,7 +87,17 @@
 } *toplevs;
 
 static int enumver = 0;
+static int enum_verbose = 0;
+static char carrier_branchlabel[32] = "carrier";
+/* how to do carrier enum branch location resolution? */
+#define ENUMLOOKUP_BLR_CC     0
+#define ENUMLOOKUP_BLR_TXT      1
+#define ENUMLOOKUP_BLR_EBL      2
+static int blr_alg = ENUMLOOKUP_BLR_CC;
 
+/* EBL record provisional type code */
+#define T_EBL      65300
+
 AST_MUTEX_DEFINE_STATIC(enumlock);
 
 struct naptr {
@@ -94,6 +105,330 @@
 	unsigned short pref;
 } __attribute__ ((__packed__));
 
+/*! \brief Determine the length of a country code when give a E.164 string */
+/*
+ * Input: E.164 number w/o leading +
+ *
+ * Output: number of digits in the country code
+ * 	   0 on invalid number
+ *
+ * convention:
+ *   3 digits is the default length of a country code.
+ *   country codes 1 and 7 are a single digit.
+ *   the following country codes are two digits: 20, 27, 30-34, 36, 39,
+ *     40, 41, 43-49, 51-58, 60-66, 81, 82, 84, 86, 90-95, 98.
+ */
+static int cclen(const char *number)
+{
+	char d1,d2;
+
+	if (!number || (strlen(number) < 3))
+		return(0);
+
+	d1 = number[0];
+	d2 = number[1];
+	
+	if (!isdigit(d2)) 
+		return(0);
+
+	switch(d1) {
+		case '1':
+		case '7':
+			return(1);
+		case '2':
+			if ((d2 == '0') || (d1 == '7'))
+				return(2);
+			break;
+		case '3':
+			if ((d2 >= '0') && (d1 <= '4'))
+				return(2);
+			if ((d2 == '6') || (d1 == '9'))
+				return(2);
+			break;
+		case '4':
+			if (d2 != '2')
+				return(2);
+			break;
+		case '5':
+			if ((d2 >= '1') && (d1 <= '8'))
+				return(2);
+			break;
+		case '6':
+			if (d1 <= '6')
+				return(2);
+			break;
+		case '8':
+			if ((d2 == '1') || (d1 == '2') || (d1 == '4') || (d1 == '6')) 
+				return(2);
+			break;
+		case '9':
+			if (d1 <= '5')
+				return(2);
+			if (d2 == '8')
+				return(2);
+			break;
+		default:
+			return(0);
+	}
+
+	return(3);
+}
+
+
+struct txt_context {
+	char txt[1024];		/* TXT record in TXT lookup */
+	int txtlen;		/* Length */
+};
+
+/*! \brief Callback for TXT record lookup, /ol version */
+static int txt_callback2(void *context, char *answer, int len, char *fullanswer)
+{
+	struct txt_context *c = (struct txt_context *)context;
+	unsigned int i;
+
+	c->txt[0] = 0;	/* default to empty */
+	c->txtlen = 0;
+
+	if (answer == NULL) {
+		return 0;
+	}
+
+	/* RFC1035: 
+	 *
+	 * <character-string> is a single length octet followed by that number of characters.
+	 * TXT-DATA        One or more <character-string>s.
+	 *
+	 * We only take the first string here.
+	 */
+
+	i = *answer++;
+	len -= 1;
+
+	if (i > len) {	/* illegal packet */
+		ast_log(LOG_WARNING, "txt_callback2: malformed TXT record.\n");
+		return 0;
+	}
+
+	if (i >= sizeof(c->txt)) {	/* too long? */
+		ast_log(LOG_WARNING, "txt_callback2: TXT record too long.\n");
+		i = sizeof(c->txt) - 1;
+	}
+
+	ast_copy_string(c->txt, answer, i + 1);
+	c->txtlen = i;
+
+	return 1;
+}
+
+
+/*! \brief Determine the branch location record as stored in a TXT record */
+/*
+ * Input: CC code
+ *
+ * Output: number of digits in the number before the carrier branch 
+ *
+ * Algorithm:  Build <carrier_branchlabel>.c.c.<suffix> and look for a TXT lookup.
+ * 		Return atoi(TXT-record).
+ * 		Return -1 on not found.
+ *
+ */
+static int blr_txt(const char *cc, const char *suffix)
+{
+	struct txt_context context;
+	char domain[128] = "";
+	char *p1,*p2;
+	int ret;
+
+	if (enum_verbose > 4)
+		ast_verbose(VERBOSE_PREFIX_4 "blr_txt()  cc='%s', suffix='%s', c_bl='%s'\n", 
+					cc, suffix, carrier_branchlabel);
+
+	ast_mutex_lock(&enumlock);
+	if (sizeof(domain) < (strlen(cc) * 2 + strlen(carrier_branchlabel) + strlen(suffix) + 2)) {
+		ast_mutex_unlock(&enumlock);
+		ast_log(LOG_WARNING, "ERROR: string sizing in blr_txt.\n");
+		return(-1);
+	}
+
+	strcpy(domain, carrier_branchlabel);
+	p1 = domain + strlen(domain);
+	ast_mutex_unlock(&enumlock);
+
+	*p1++ = '.';
+	*p1   = 0;
+		
+	p2 = (char *) cc + strlen(cc) - 1;
+	while(p2 >= cc) {
+		if (isdigit(*p2)) {
+			*p1++ = *p2;
+			*p1++ = '.';
+		}
+		p2--;
+	}
+	strcat(p1, suffix);
+
+	if (enum_verbose > 4)
+		ast_verbose(VERBOSE_PREFIX_4 "blr_txt() FQDN for TXT record: %s, cc was %s\n", domain, cc);
+
+	ret = ast_search_dns(&context, domain, C_IN, T_TXT, txt_callback2);
+	if (ret > 0) {
+		ret = atoi(context.txt);
+
+		if ((ret>=0) && (ret<20)) {
+			if (enum_verbose > 3)
+				ast_verbose(VERBOSE_PREFIX_3 "blr_txt() BLR TXT record for %s is %d (apex: %s)\n", cc, ret, suffix);
+			return(ret);
+		}
+	}
+	if (enum_verbose > 3)
+		ast_verbose(VERBOSE_PREFIX_3 "blr_txt() BLR TXT record for %s not found (apex: %s)\n", cc, suffix);
+	return(-1);
+}
+
+
+struct ebl_context {
+	unsigned char pos;
+	char separator[256];		/* label to insert */
+	int sep_len;			/* Length */
+	char apex[256];			/* new Apex */
+	int apex_len;			/* Length */
+};
+
+/*! \brief Callback for EBL record lookup */
+static int ebl_callback(void *context, char *answer, int len, char *fullanswer)
+{
+	struct ebl_context *c = (struct ebl_context *)context;
+	unsigned int i;
+
+	c->pos = 0;	/* default to empty */
+	c->separator[0] = 0;
+	c->sep_len = 0;
+	c->apex[0] = 0;	
+	c->apex_len = 0;
+
+	if (answer == NULL) {
+		return 0;
+	}
+
+	/* draft-lendl-enum-branch-location-record-00
+	 *
+	 *      0  1  2  3  4  5  6  7
+	 *    +--+--+--+--+--+--+--+--+
+	 *    |       POSITION        |
+	 *    +--+--+--+--+--+--+--+--+
+	 *    /       SEPARATOR       /
+	 *    +--+--+--+--+--+--+--+--+
+	 *    /         APEX          /
+	 *    +--+--+--+--+--+--+--+--+
+	 *
+	 *  where POSITION is a single byte, SEPARATOR is a <character-string>
+	 *  and APEX is a <domain-name>. 
+	 * 
+	 */
+
+	c->pos = *answer++;
+	len -= 1;
+
+	if ((c->pos > 15) || len < 2) {	/* illegal packet */
+		ast_log(LOG_WARNING, "ebl_callback: malformed EBL record.\n");
+		return 0;
+	}
+
+	i = *answer++;
+	len -= 1;
+	if (i > len) {	/* illegal packet */
+		ast_log(LOG_WARNING, "ebl_callback: malformed EBL record.\n");
+		return 0;
+	}
+
+	ast_copy_string(c->separator, answer, i + 1);
+	c->sep_len = i;
+
+	answer += i;
+	len -= i;
+
+	if ((i = dn_expand((unsigned char *)fullanswer, (unsigned char *)answer + len, 
+				(unsigned char *)answer, c->apex, sizeof(c->apex) - 1)) < 0) {
+		ast_log(LOG_WARNING, "Failed to expand hostname\n");
+		return 0;
+	}
+	c->apex[i] = 0;
+	c->apex_len = i;
+
+	return 1;
+}
+
+/*! \brief Evaluate the I-ENUM branch as stored in an EBL record */
+/*
+ * Input: CC code
+ *
+ * Output: number of digits in the number before the carrier branch 
+ *
+ * Algorithm:  Build <carrier_branchlabel>.c.c.<suffix> and look for an EBL record 
+ * 		Return pos and fill in separator and apex.
+ * 		Return -1 on not found.
+ *
+ */
+static int blr_ebl(const char *cc, const char *suffix, char *separator, int sep_len, char* apex, int apex_len)
+{
+	struct ebl_context context;
+	char domain[128] = "";
+	char *p1,*p2;
+	int ret;
+
+	if (enum_verbose > 4)
+		ast_verbose(VERBOSE_PREFIX_4 "blr_ebl()  cc='%s', suffix='%s', c_bl='%s'\n", 
+					cc, suffix, carrier_branchlabel);
+
+	ast_mutex_lock(&enumlock);
+	if (sizeof(domain) < (strlen(cc) * 2 + strlen(carrier_branchlabel) + strlen(suffix) + 2)) {
+		ast_mutex_unlock(&enumlock);
+		ast_log(LOG_WARNING, "ERROR: string sizing in blr_EBL.\n");
+		return(-1);
+	}
+
+	strcpy(domain, carrier_branchlabel);
+	p1 = domain + strlen(domain);
+	ast_mutex_unlock(&enumlock);
+
+	*p1++ = '.';
+	*p1   = 0;
+		
+	p2 = (char *) cc + strlen(cc) - 1;
+	while(p2 >= cc) {
+		if (isdigit(*p2)) {
+			*p1++ = *p2;
+			*p1++ = '.';
+		}
+		p2--;
+	}
+	strcat(p1, suffix);
+
+	if (enum_verbose > 4)
+		ast_verbose(VERBOSE_PREFIX_4 "blr_ebl() FQDN for EBL record: %s, cc was %s\n", domain, cc);
+
+	ret = ast_search_dns(&context, domain, C_IN, T_EBL, ebl_callback);
+	if (ret > 0) {
+		ret = context.pos;
+
+		if ((ret>=0) && (ret<20)) {
+			if (enum_verbose > 3)
+				ast_verbose(VERBOSE_PREFIX_3 "blr_txt() BLR EBL record for %s is %d/%s/%s)\n", 
+							cc, ret, context.separator, context.apex);
+			ast_copy_string(separator, context.separator, sep_len);
+			ast_copy_string(apex, context.apex, apex_len);
+			return(ret);
+		}
+	}
+	if (enum_verbose > 3)
+		ast_verbose(VERBOSE_PREFIX_3 "blr_txt() BLR EBL record for %s not found (apex: %s)\n", cc, suffix);
+	return(-1);
+}
+
+
+
+
+
 /*! \brief Parse NAPTR record information elements */
 static unsigned int parse_ie(char *data, unsigned int maxdatalen, char *src, unsigned int srclen)
 {
@@ -175,6 +510,10 @@
 	if (option_debug > 2)	/* Advanced NAPTR debugging */
 		ast_log(LOG_DEBUG, "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
 			naptrinput, flags, services, regexp, repl);
+	if (enum_verbose > 1)
+		ast_verbose(VERBOSE_PREFIX_2 "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
+			naptrinput, flags, services, regexp, repl);
+			
 
 	if (tolower(flags[0]) != 'u') {
 		ast_log(LOG_WARNING, "NAPTR Flag must be 'U' or 'u'.\n");
@@ -301,6 +640,10 @@
 
 /* do not return requested value, just count RRs and return thei number in dst */
 #define ENUMLOOKUP_OPTIONS_COUNT       1
+/* do an ISN style lookup */
+#define ENUMLOOKUP_OPTIONS_ISN         2
+/* do a carrier ENUM lookup */
+#define ENUMLOOKUP_OPTIONS_CARRIER     4
 
 struct enum_naptr_rr {
 	struct naptr naptr; /* order and preference of RR */
@@ -397,13 +740,16 @@
 }
 
 /*! \brief ENUM lookup */
-int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char* suffix, char* options)
+int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char* suffix, char* options, char *record)
 {
 	struct enum_context context;
-	char tmp[259 + 512];
-	char naptrinput[512];
-	int pos = strlen(number) - 1;
-	int newpos = 0;
+	char tmp[512];
+	char domain[256];
+	char left[128];
+	char middle[128];
+	char apex[128] = "";
+
+	char naptrinput[128];
 	int ret = -1;
 	struct enum_search *s = NULL;
 	int version = -1;
@@ -413,9 +759,32 @@
 	int k = 0;
 	int i = 0;
 	int z = 0;
+	int spaceleft = 0;
+	struct timeval time_start, time_end;
 
+	if (enum_verbose > 1)
+		ast_verbose(VERBOSE_PREFIX_2 "ast_get_enum(num='%s', tech='%s', suffix='%s', options='%s'\n",
+			number, tech, suffix, options);
+
+
+/*
+  We don't need that any more, that "n" preceding the number has been replaced by a flag
+  in the options paramter.
+
 	ast_copy_string(naptrinput, number[0] == 'n' ? number+1 : number, sizeof(naptrinput));
 
+*/
+	/*
+	 * The "number" paramter includes a leading '+' if it's a full E.164 number (and not ISN)
+	 * We need to preserve that as the regex inside NAPTRs expect the +.
+	 *
+	 * But for the domain generation, the '+' is a nuissance, so we get rid of it.
+	 */
+	ast_copy_string(naptrinput, number, sizeof(naptrinput));
+	if (number[0] == '+')
+		number++;
+
+
 	context.naptrinput = naptrinput;	/* The number */
 	context.dst = dst;			/* Return string */
 	context.dstlen = dstlen;
@@ -426,53 +795,146 @@
 	context.naptr_rrs = NULL;
 	context.naptr_rrs_count = 0;
 
-	if (options != NULL) {
-		if (*options == 'c') {
-			context.options = ENUMLOOKUP_OPTIONS_COUNT;
+	/*
+	 * Process options:
+	 *
+	 * 	c	Return count, not URI
+	 * 	i	Do ISN transformation
+	 * 	r	Use carrier ENUM 
+	 *
+	 */
+	if (options != NULL){
+		if (strchr(options,'i'))
+			context.options |= ENUMLOOKUP_OPTIONS_ISN;
+		else if (strchr(options,'r'))
+			context.options |= ENUMLOOKUP_OPTIONS_CARRIER;
+
+		if (strchr(options,'c')){
+			context.options |= ENUMLOOKUP_OPTIONS_COUNT;
 			context.position = 0;
-		} else {
-			context.position = atoi(options);
-			if (context.position < 1)
-				context.position = 1;
-		}
+		} 
 	}
 
-	if (pos > 128)
-		pos = 128;
+	context.position = atoi(record);
+	if (context.position < 1)
+		context.position = 1;
 
+	if (enum_verbose > 1)
+		ast_verbose(VERBOSE_PREFIX_2 "ENUM options(%s): pos=%d, options='%d'\n",
+			options, context.position, context.options);
+
+	/* default: the whole number will be flipped, no middle domain component */
+	ast_copy_string(left, number, sizeof(left));
+	middle[0] = '\0';
+	/*
+	 * I-ENUM can change the apex, thus we copy it 
+	 */
+	ast_copy_string(apex, suffix, sizeof(apex));
+
 	/* ISN rewrite */
-	p1 = strchr(number, '*');
+	if ((context.options & ENUMLOOKUP_OPTIONS_ISN) && (p1 = strchr(number, '*'))) {
+		*p1++ = '\0';
 
-	if (number[0] == 'n') { /* do not perform ISN rewrite ('n' is testing flag) */
-		p1 = NULL;
-		k = 1; /* strip 'n' from number */
-	}
+		ast_copy_string(left, number, sizeof(left));
+		ast_copy_string(middle, p1, sizeof(middle)-2);
+		strcat(middle, ".");
+		
+		if (enum_verbose > 1)
+			ast_verbose(VERBOSE_PREFIX_2 "ISM ENUM: left=%s, middle='%s'\n",
+				left, middle);
 
-	if (p1 != NULL) {
-		p2 = p1+1;
-		while (p1 > number){
-			p1--;
-			tmp[newpos++] = *p1;
-			tmp[newpos++] = '.';
+
+	/* Carrier ENUM rewrite */
+	} else if (context.options & ENUMLOOKUP_OPTIONS_CARRIER) {
+		int sdl=0;
+		char cc[8];
+		char sep[256], n_apex[256];
+		int cc_len = cclen(number);
+		sdl = cc_len;
+
+		ast_copy_string(sep, carrier_branchlabel, sizeof(sep)); /* default */
+
+		switch(blr_alg) {
+			case ENUMLOOKUP_BLR_EBL:
+				strncpy(cc, number, cc_len); /* cclen() never returns more than 3 */
+				cc[cc_len] = 0;
+
+				/* carrier ENUM is only called via ENUMLOOKUP, thus we have a suffix */
+				sdl = blr_ebl(cc, suffix, sep, 255, n_apex, 255);
+
+				if (sdl >= 0) {
+					ast_copy_string(apex, n_apex, sizeof(apex));
+					ast_verbose(VERBOSE_PREFIX_2 "EBL ENUM: sep=%s, apex='%s'\n", sep, n_apex);
+				} else {
+					sdl = cc_len;
+				}
+				break;
+
+			case ENUMLOOKUP_BLR_TXT:
+				strncpy(cc, number, cc_len); /* cclen() never returns more than 3 */
+				cc[cc_len] = 0;
+
+				/* carrier ENUM is only called via ENUMLOOKUP, thus we have a suffix */
+				sdl = blr_txt(cc, suffix);
+
+				if (sdl < 0) {
+					sdl = cc_len;
+				}
+				break;
+
+			case ENUMLOOKUP_BLR_CC:	/* BLR is at the country-code level */
+			default:
+				sdl = cc_len;
+				break;
 		}
-		if (*p2) {
-			while(*p2 && newpos < 128){
-				tmp[newpos++] = *p2;
-				p2++;
-			}
-			tmp[newpos++] = '.';
+
+		if (sdl > strlen(number)) {	/* Number too short for this sdl? */
+			ast_log(LOG_WARNING, "CARRIER ENUM: subdomain location %d behind number %s\n", sdl, number);
+			return(0);
 		}
 
-	} else {
-		while (pos >= k) {
-			if (isdigit(number[pos])) {
-				tmp[newpos++] = number[pos];
-				tmp[newpos++] = '.';
+		ast_copy_string(left, number+sdl, sizeof(left));
+
+		ast_mutex_lock(&enumlock);
+		ast_copy_string(middle, sep, sizeof(middle) - 2);
+		strcat(middle, ".");
+		ast_mutex_unlock(&enumlock);
+
+		p1 = middle + strlen(middle);
+		p2 = (char *) number + sdl - 1;
+		while(p2 >= number) {
+			if (isdigit(*p2)) {
+				*p1++ = *p2;
+				*p1++ = '.';
 			}
-			pos--;
+			p2--;
 		}
+		*p1 = '\0';
+
+		if (enum_verbose > 1)
+			ast_verbose(VERBOSE_PREFIX_2 "CARRIER ENUM: cclen=%d, left=%s, middle='%s', apex='%s'\n",
+				cc_len, left, middle, apex);
+
 	}
 
+
+	if (strlen(left)*2 + 2 > sizeof(domain)) {
+		ast_log(LOG_WARNING, "string to long in ast_get_enum\n");
+		return -1;
+	}
+
+	/* flip left into domain */
+	p1 = domain;
+	p2 = left + strlen(left);
+	while(p2 >= left) {
+		if (isdigit(*p2)) {
+			*p1++ = *p2;
+			*p1++ = '.';
+		}
+		p2--;
+	}
+	*p1 = '\0';
+
 	if (chan && ast_autoservice_start(chan) < 0)
 		return -1;
 
@@ -485,11 +947,33 @@
 		} else {
 			s = s->next;
 		}
-		ast_copy_string(tmp + newpos, suffix ? suffix : s->toplev, sizeof(tmp) - newpos);
+		spaceleft = sizeof(tmp) - 2;
+		ast_copy_string(tmp, domain, spaceleft);
+		spaceleft -= strlen(domain);
+		if (*middle) {
+			strncat(tmp, middle, spaceleft);
+			spaceleft -= strlen(middle);
+		}
+				 
+		if (suffix != NULL) {
+			strncat(tmp, apex, spaceleft);
+		} else if (s) {
+			strncat(tmp, s->toplev, spaceleft);
+		}
 		ast_mutex_unlock(&enumlock);
 		if (!s)
 			break;
+
+		(void) gettimeofday(&time_start, NULL);
 		ret = ast_search_dns(&context, tmp, C_IN, T_NAPTR, enum_callback);
+		(void) gettimeofday(&time_end, NULL);
+
+		if (enum_verbose > 1)
+			ast_verbose(VERBOSE_PREFIX_2 "ast_get_enum() profiling: %s, %s, %ld usec\n", 
+				(ret>0) ? "OK" : "FAIL", tmp, 
+				(time_end.tv_sec - time_start.tv_sec) * 1000000 + 
+				(time_end.tv_usec - time_start.tv_usec));
+
 		if (ret > 0)
 			break;
 		if (suffix != NULL)
@@ -557,16 +1041,13 @@
 {
 	struct enum_context context;
 	char tmp[259 + 512];
-	char naptrinput[512] = "+";
 	int pos = strlen(number) - 1;
 	int newpos = 0;
 	int ret = -1;
 	struct enum_search *s = NULL;
 	int version = -1;
 
-	strncat(naptrinput, number, sizeof(naptrinput) - 2);
-
-	context.naptrinput = naptrinput;
+	context.naptrinput = NULL;
 	context.dst = dst;
 	context.dstlen = dstlen;
 	context.tech = tech;
@@ -631,6 +1112,7 @@
 	struct ast_config *cfg;
 	struct enum_search *s, *sl;
 	struct ast_variable *v;
+	char *string;
 
 	/* Destroy existing list */
 	ast_mutex_lock(&enumlock);
@@ -643,10 +1125,18 @@
 	toplevs = NULL;
 	cfg = ast_config_load("enum.conf");
 	if (cfg) {
+		if ((string=ast_variable_retrieve(cfg, "general", "verbose"))) {
+			enum_verbose = atoi(string);
+		} else {
+			enum_verbose = 0;
+		}
+
 		sl = NULL;
 		v = ast_variable_browse(cfg, "general");
 		while(v) {
 			if (!strcasecmp(v->name, "search")) {
+				if (enum_verbose > 1)
+					ast_verbose(VERBOSE_PREFIX_2 "ast_enum_init() adding apex: %s\n", v->value);
 				s = enum_newtoplev(v->value);
 				if (s) {
 					if (sl)
@@ -658,6 +1148,20 @@
 			}
 			v = v->next;
 		}
+		if (!sl)	/* no search param in config file */
+			toplevs = enum_newtoplev(TOPLEV);
+
+		if ((string=ast_variable_retrieve(cfg, "carrier", "branchlabel"))) {
+			ast_copy_string(carrier_branchlabel, string, sizeof(carrier_branchlabel));
+		}
+
+		if ((string=ast_variable_retrieve(cfg, "carrier", "blr_alg"))) {
+			blr_alg = ENUMLOOKUP_BLR_CC; /* default */
+			if (!strcasecmp(string, "txt"))
+				blr_alg = ENUMLOOKUP_BLR_TXT; 
+			if (!strcasecmp(string, "ebl"))
+				blr_alg = ENUMLOOKUP_BLR_EBL; 
+		}
 		ast_config_destroy(cfg);
 	} else {
 		toplevs = enum_newtoplev(TOPLEV);
@@ -671,3 +1175,4 @@
 {
 	return ast_enum_init();
 }
+
Index: funcs/func_enum.c
===================================================================
--- funcs/func_enum.c	(revision 28626)
+++ funcs/func_enum.c	(working copy)
@@ -33,7 +33,9 @@
 #include <stdio.h>
 
 #include "asterisk.h"
+#include "asterisk/term.h"
 
+
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
@@ -102,7 +104,7 @@
 	}
 
 	res = ast_get_enum(chan, num, dest, sizeof(dest), tech, sizeof(tech), args.zone,
-			   args.options);
+			   args.options, args.record);
 
 	p = strchr(dest, ':');
 	if (p && strcasecmp(tech, "ALL"))
@@ -141,7 +143,14 @@
 
 	buf[0] = '\0';
 
+       char tmp[200], tmp2[200];
 
+	if (option_verbose > 3)
+		ast_verbose( VERBOSE_PREFIX_4 "Executing %s(\"%s\")\n",
+			term_color(tmp, cmd, COLOR_BRCYAN, 0, sizeof(tmp)),
+			term_color(tmp2, data, COLOR_BRMAGENTA, 0, sizeof(tmp2)));
+
+
 	if (ast_strlen_zero(data)) {
 		ast_log(LOG_WARNING, "TXTCIDNAME requires an argument (number)\n");
 		return -1;
Index: configs/enum.conf.sample
===================================================================
--- configs/enum.conf.sample	(revision 28626)
+++ configs/enum.conf.sample	(working copy)
@@ -9,6 +9,9 @@
 ; The search list for domains may be customized.  Domains are searched
 ; in the order they are listed here.
 ;
+; The "search" entries are only used by the EnumLookup application.
+; The ENUMLOOKUP function defaults to e164.arpa if no other apex is given.
+;
 search => e164.arpa
 ;
 ; If you'd like to use the E.164.org public ENUM registery in addition
@@ -20,3 +23,42 @@
 ; drive a H323 URI will map. Default is "H323".
 ;
 h323driver => H323
+;
+; Set verbosity of the ENUM module.
+;
+verbose => 0
+
+;
+; Carrier ENUM support
+;
+; The section configures experimental support for carrier ENUM as
+; described in ftp://ftp.rfc-editor.org/in-notes/internet-drafts/draft-haberler-carrier-enum-01.txt
+;
+[carrier]
+;
+; The branchlabel is the label which branches off the carrier ENUM tree from the
+; classic user-ENUM tree. Examples:
+;
+;  +---------------------------+-----------------+
+;  |         shape             | branch location |
+;  +---------------------------+-----------------+
+;  | 4.9.7.1.carrier.e164.arpa |        0        |
+;  | 4.9.7.carrier.1.e164.arpa |        1        |
+;  | carrier.4.9.7.1.e164.arpa |        4        |
+;  +---------------------------+-----------------+
+;
+branchlabel     => carrier
+;
+; The blr_alg parameter defines which algorithm the ENUM code should employ to
+; determine the "branch location" parameter for each country-code.
+; 
+; Possible values:
+;	txt	Use a TXT record at <branchlabel>.<reverse-country-code>.<apex>
+;		Examples:  carrier.3.4.e164.arpa, carrier.1.e164.arpa
+;
+;	cc	Always set the branch location at the country-code level
+;		(this is the default)
+;
+;
+blr_alg         => txt
+;
