diff options
author | kpfleming <kpfleming@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-09-14 01:36:15 +0000 |
---|---|---|
committer | kpfleming <kpfleming@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-09-14 01:36:15 +0000 |
commit | fa8ea94c177025426ad5429a3737e0b8aae6363f (patch) | |
tree | d4b59476a3096555262c97b7b28dc1fb6737c2eb | |
parent | 06e6b25ca44f1612b599b7f89d33367be9bd72b5 (diff) |
extensive ENUM support update, including ENUMLOOKUP() dialplan function (issue #5201 with mods)
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@6579 f38db490-d61c-443f-a65b-d21fe96a405b
-rwxr-xr-x | apps/app_enumlookup.c | 11 | ||||
-rwxr-xr-x | doc/README.enum | 306 | ||||
-rwxr-xr-x | enum.c | 275 | ||||
-rwxr-xr-x | funcs/func_enum.c | 174 | ||||
-rwxr-xr-x | include/asterisk/enum.h | 17 |
5 files changed, 716 insertions, 67 deletions
diff --git a/apps/app_enumlookup.c b/apps/app_enumlookup.c index aea6b9c03..6a9142005 100755 --- a/apps/app_enumlookup.c +++ b/apps/app_enumlookup.c @@ -38,7 +38,7 @@ static char *app = "EnumLookup"; static char *synopsis = "Lookup number in ENUM"; -static char *descrip = +static char *descrip = " EnumLookup(exten): Looks up an extension via ENUM and sets\n" "the variable 'ENUM'. For VoIP URIs this variable will \n" "look like 'TECHNOLOGY/URI' with the appropriate technology.\n" @@ -71,6 +71,9 @@ static int enumlookup_exec(struct ast_channel *chan, void *data) char dest[80]; char tmp[256]; char *c,*t; + + tech[0] = '\0'; + struct localuser *u; if (!data || ast_strlen_zero(data)) { @@ -79,7 +82,7 @@ static int enumlookup_exec(struct ast_channel *chan, void *data) } LOCAL_USER_ADD(u); if (!res) { - res = ast_get_enum(chan, data, dest, sizeof(dest), tech, sizeof(tech)); + res = ast_get_enum(chan, data, dest, sizeof(dest), tech, sizeof(tech), NULL, NULL); printf("ENUM got '%d'\n", res); } LOCAL_USER_REMOVE(u); @@ -105,7 +108,7 @@ static int enumlookup_exec(struct ast_channel *chan, void *data) snprintf(tmp, sizeof(tmp), "%s/%s", h323driver, c); /* do a s!;.*!! on the H323 URI */ t = strchr(c,';'); - if (t) + if (t) *t = 0; pbx_builtin_setvar_helper(chan, "ENUM", tmp); } else if (!strcasecmp(tech, "iax")) { @@ -130,7 +133,7 @@ static int enumlookup_exec(struct ast_channel *chan, void *data) res = 0; } else { /* now copy over the number, skipping all non-digits and stop at ; or NULL */ - t = tmp; + t = tmp; while( *c && (*c != ';') && (t - tmp < (sizeof(tmp) - 1))) { if (isdigit(*c)) *t++ = *c; diff --git a/doc/README.enum b/doc/README.enum new file mode 100755 index 000000000..a5619ce40 --- /dev/null +++ b/doc/README.enum @@ -0,0 +1,306 @@ +README.enum + +2005-09-06 +jtodd@loligo.com + +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 +consideration of ENUM or ENUM-like lookup strategies. This document +assumes a familiarity with ENUM (RFC3761) or ENUM-like methods, as +well as familiarity with NAPTR DNS records (RFC2915, RFC3401-3404). +For an overview of NAPTR records, and the use of NAPTRs in the ENUM +global phone-number-to-DNS mapping scheme, please see +http://www.voip-info.org/tiki-index.php?page=ENUM for more detail. + +Using ENUM within Asterisk can be simple or complex, depending on how +many failover methods and redundancy procedures you wish to utilize. +Implementation of ENUM paths is supposedly defined by the person +creating the NAPTR records, but the local administrator may choose to +ignore certain NAPTR response methods (URI types) or prefer some over +others, which is in contradiction to the RFC. The ENUMLOOKUP method +simply provides administrators a method for determining NAPTR results +in either the globally unique ENUM (e164.arpa) DNS tree, or in other +ENUM-like DNS trees which are not globally unique. The methods to +actually create channels ("dial") results given by the ENUMLOOKUP +function is then up to the administrator to implement in a way that +best suits their environment. + +Function: EnumLookup(<number>[,pointer_type[,options[,zone_suffix]]]) + + Performs an ENUM tree lookup on the specified number, method type, + and (optionally) ordinal offset, and returns one of four different values: + + 1) post-parsed NAPTR of one method (URI) type + 2) count of elements of one method (URI) type + 3) count of all method types + 4) full URI of method at a particular point in the list of all possible methods + +Arguments: + +number = telephone number or search string. Only numeric values +within this string are parsed; all other digits are ignored for +search, but are re-written during NAPTR regexp expansion. + +service_type = tel, sip, h323, iax2, mailto, ...[any other string], + ALL. Default type is "sip". + Special name of "ALL" will create a list of method types across + all NAPTR records for the search number, and then put the results + in an ordinal list starting with 1. The position <number> + specified will then be returned, starting with 1 as the first + record (lowest value) in the list. The service types are not + hardcoded in Asterisk except for the default (sip) if no other + service type specified; any method type string (IANA-approved or + not) may be used except for the string "ALL". + +options = optional specifiers. + c = count. Returns the number of records of this type are returned + (regardless of order or priority.) If "ALL" is the specified + service_type, then a count of all methods will be returned for the + DNS record. + <integer> = The record in priority/order sequence based on the + total count of records passed back by the query. If a service_type + is specified, all entries of that type will be sorted into an + ordinal list starting with 1 (by order first, then priority). + The default of <options> is "1" + +zone_suffix = allows customization of the ENUM zone. Default is e164.arpa. + + +EXAMPLE USES: + +Let's use this ENUM list as an example (note that these examples exist +in the DNS, and will hopefully remain in place as example +destinations, but they may change or become invalid over time. The +end result URIs are not guaranteed to actually work, since some of +these hostnames or SIP proxies are imaginary. Of course, the tel: +replies go to directory assistance for New York City and San +Francisco...) Also note that the complex SIP NAPTR at weight 30 will +strip off the leading "+" from the dialed string if it exists. This +is probably a better NAPTR than hard-coding the number into the NAPTR, +and it is included as a more complex regexp example, though other +simpler NAPTRs will work just as well. + + +0.2.0.1.1.6.5.1.0.3.1.fox-den.com. 3600 IN NAPTR 10 100 "u" "E2U+tel" "!^\\+13015611020$!tel:+12125551212!" . +0.2.0.1.1.6.5.1.0.3.1.fox-den.com. 3600 IN NAPTR 21 100 "u" "E2U+tel" "!^\\+13015611020$!tel:+14155551212!" . +0.2.0.1.1.6.5.1.0.3.1.fox-den.com. 3600 IN NAPTR 25 100 "u" "E2U+sip" "!^\\+13015611020$!sip:2203@sip.fox-den.com!" . +0.2.0.1.1.6.5.1.0.3.1.fox-den.com. 3600 IN NAPTR 26 100 "u" "E2U+sip" "!^\\+13015611020$!sip:1234@sip-2.fox-den.com!" . +0.2.0.1.1.6.5.1.0.3.1.fox-den.com. 3600 IN NAPTR 30 100 "u" "E2U+sip" "!^\\+*([^\\*]*)!sip:\\1@sip-3.fox-den.com!" . +0.2.0.1.1.6.5.1.0.3.1.fox-den.com. 3600 IN NAPTR 55 100 "u" "E2U+mailto" "!^\\+13015611020$!mailto:jtodd@fox-den.com!" . + +Example 1: Simplest case, using first SIP return (use all defaults +except for domain name) +exten => 100,1,Set(foo=ENUMLOOKUP(13015611020,,,fox-den.com)) + returns: ${foo}="2203@sip.fox-den.com" + +Example 2: What is the first "tel" pointer type for this number? +(after sorting by order/preference; default of "1" is assumed in +options field) +exten => 100,1,Set(foo=${ENUMLOOKUP(13015611020,tel,,loligo.com)}) + returns: ${foo}="+12125551212" + +Example 3: How many "sip" pointer type entries are there for this number? +exten => 100,1,Set(foo=${ENUMLOOKUP(13015611020,sip,c,loligo.com)}) + returns: ${foo}=3 + +Example 4: For all the "tel" pointer type entries, what is the second +one in the list? (after sorting by preference) +exten => 100,1,Set(foo=${ENUMLOOKUP(13015611020,tel,2,loligo.com)}) + returns: ${foo}="+5553" + +Example 5: How many NAPTRs (tel, sip, mailto, etc.) are in the list for this number? +exten => 100,1,Set(foo=${ENUMLOOKUP(13015611020,ALL,c,loligo.com)}) + returns: ${foo}=6 + +Example 6: Give back the second full URI in the sorted list of all NAPTR URIs: +exten => 100,1,Set(foo=${ENUMLOOKUP(13015611020,ALL,2,loligo.com)}) + returns: ${foo}="tel:14155551212" [note the "tel:" prefix in the string] + +Example 7: Look up first SIP entry for the number in the e164.arpa zone (all defaults) +exten => 100,1,Set(foo=${ENUMLOOKUP(+437203001721)}) + returns: ${foo}="enum-test@sip.nemox.net" [note: this result is + subject to change as it is "live" DNS and not under my control] + + +Example 8: Look up the ISN mapping in freenum.org alpha test zone +exten => 100,1,Set(foo=${ENUMLOOKUP(1234*256,,,freenum.org)}) + returns: ${foo}="1234@204.91.156.10" [note: this result is subject + to change as it is "live" DNS] + +Example 9: Give back the first SIP pointer for a number in the +enum.yoydynelabs.com zone (invalid lookup) +exten => 100,1,Set(foo=${ENUMLOOKUP(1234567890,sip,1,enum.yoyodynelabs.com)}) + returns: ${foo}="" + + +Usage notes and subtle features: + + a) The use of "+" in lookups is confusing, and warrants further + explanation. All E.164 numbers ("global phone numbers") by + definition need a leading "+" during ENUM lookup. If you neglect to + add a leading "+", you may discover that numbers that seem to exist + in the DNS aren't getting matched by the system or are returned with + a null string result. This is due to the NAPTR reply requiring a + "+" in the regular expression matching sequence. Older versions of + Asterisk add a "+" from within the code, which may confuse + administrators converting to the new function. Please ensure that + all ENUM (e164.arpa) lookups contain a leading "+" before lookup, so + ensure your lookup includes the leading plus sign. Other DNS trees + may or may not require a leading "+" - check before using those + trees, as it is possible the parsed NAPTRs will not provide correct + results unless you have the correct dialed string. If you get + console messages like "WARNING[24907]: enum.c:222 parse_naptr: NAPTR + Regex match failed." then it is very possible that the returned + NAPTR expects a leading "+" in the search string (or the returned + NAPTR is mis-formed.) + + b) If a query is performed of type "c" ("count") and let's say you + get back 5 records and then some seconds later a query is made + against record 5 in the list, it may not be the case that the DNS + resolver has the same answers as it did a second or two ago - maybe + there are only 4 records in the list in the newest query. The + resolver should be the canonical storage location for DNS records, + since that is the intent of ENUM. However, some obscure future + cases may have wildly changing NAPTR records within several seconds. + This is a corner case, and probably only worth noting as a very rare + circumstance. (note: I do not object to Asterisk's dnsmgr method of + locally caching DNS replies, but this method needs to honor the TTL + given by the remote zone master. Currently, the ENUMLOOKUP function + does not use the dnsmgr method of caching local DNS replies.) + + c) If you want strict NAPTR value ordering, then it will be + necessary to use the "ALL" method to incrementally step through the + different returned NAPTR pointers. You will need to use string + manipulation to strip off the returned method types, since the + results will look like "sip:12125551212" in the returned value. + This is a non-trivial task, though it is required in order to have + strict RFC compliance and to comply with the desires of the remote + party who is presenting NAPTRs in a particular order for a reason. + + d) Default behavior for the function (even in event of an error) is + to move to the next priority, and the result is a null value. Most + ENUM lookups are going to be failures, and it is the responsibility + of the dialplan administrator to manage error conditions within + their dialplan. This is a change from the old app_enumlookup method + and it's arbitrary priority jumping based on result type or failure. + + e) Anything other than digits will be ignored in lookup strings. + Example: a search string of "+4372030blah01721" will turn into + 1.2.7.1.0.0.3.0.2.7.3.4.e164.arpa. for the lookup. The NAPTR + parsing may cause unexpected results if there are strings inside + your NAPTR lookups. + + f) If there exist multiple records with the same weight and order as + a result of your query, the function will RANDOMLY select a single + NAPTR from those equal results. + + 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. + + h) The function will digest and return NAPTRs which use older + (depricated) style, reversed method strings such as "sip+E2U" + instead of the more modern "E2U+sip" + + i) There is no provision for multi-part methods at this time. If + there are multiple NAPTRs with (as an example) a method of + "E2U+voice:sip" and then another NAPTR in the same DNS record with a + method of ""E2U+sip", the system will treat these both as method + "sip" and they will be separate records from the perspective of the + function. Of course, if both records point to the same URI and have + equal priority/weight (as is often the case) then this will cause no + serious difficulty, but it bears mentioning. + + j) ISN (ITAD Subscriber Number) usage: If the search number is of + 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. + + +==EXAMPLES== + +All examples below except where noted use "e164.arpa" as the +referenced domain, which is the default domain name for ENUMLOOKUP. +All numbers are assumed to not have a leading "+" as dialed by the +inbound channel, so that character is added where necessary during +ENUMLOOKUP function calls. + +; example 1 +; +; Assumes North American international dialing (011) prefix. +; Look up the first SIP result and send the call there, otherwise +; send the call out a PRI. This is the most simple possible +; ENUM example, but only uses the first SIP reply in the list of +; NAPTR(s). +; +exten => _011.,1,Set(enumresult=${ENUMLOOKUP(+${EXTEN:3})}) +exten => _011.,n,Dial(SIP/${enumlookup}) +exten => _011.,n,Dial(Zap/g1/${EXTEN}) +; +; end example 1 + +; example 2 +; +; Assumes North American international dialing (011) prefix. +; Check to see if there are multiple SIP NAPTRs returned by +; the lookup, and dial each in order. If none work (or none +; exist) then send the call out a PRI, group 1. +; +exten => _011.,1,Set(sipcount=${ENUMLOOKUP(${EXTEN:3},sip,c)}|counter=0) +exten => _011.,n,While($["${counter}"<"${sipcount}"]) +exten => _011.,n,Set(counter=$[${counter}+1]) +exten => _011.,n,Dial(SIP/${ENUMLOOKUP(+${EXTEN:3},sip,${counter})}) +exten => _011.,n,EndWhile +exten => _011.,n,Dial(Zap/g1/${EXTEN}) +; +; end example 2 + +; example 3 +; +; This example expects an ${EXTEN} that is an e.164 number (like +; 14102241145 or 437203001721) +; Search through e164.arpa and then also search through e164.org +; to see if there are any valid SIP or IAX termination capabilities. +; If none, send call out via Zap channel 1. +; +; Start first with e164.arpa zone... +; +exten => _X.,1,Set(sipcount=${ENUMLOOKUP(${EXTEN},sip,c)}|counter=0) +exten => _X.,2,GotoIf($["${counter}"<"${sipcount}"]?3:6) +exten => _X.,3,Set(counter=$[${counter}+1]) +exten => _X.,4,Dial(SIP/${ENUMLOOKUP(+${EXTEN},sip,${counter})}) +exten => _X.,5,GotoIf($["${counter}"<"${sipcount}"]?3:6) +; +exten => _X.,6,Set(iaxcount=${ENUMLOOKUP(${EXTEN},iax2,c)}|counter=0) +exten => _X.,7,GotoIf($["${counter}"<"${iaxcount}"]?8:11) +exten => _X.,8,Set(counter=$[${counter}+1]) +exten => _X.,9,Dial(IAX2/${ENUMLOOKUP(+${EXTEN},iax,${counter})}) +exten => _X.,10,GotoIf($["${counter}"<"${iaxcount}"]?8:11) +; +exten => _X.,11,NoOp("No valid entries in e164.arpa for ${EXTEN} - checking in e164.org") +; +; ...then also try e164.org, and look for SIP and IAX NAPTRs... +; +exten => _X.,12,Set(sipcount=${ENUMLOOKUP(${EXTEN},sip,c,e164.org)}|counter=0) +exten => _X.,13,GotoIf($["${counter}"<"${sipcount}"]?14:17) +exten => _X.,14,Set(counter=$[${counter}+1]) +exten => _X.,15,Dial(SIP/${ENUMLOOKUP(+${EXTEN},sip,${counter},e164.org)}) +exten => _X.,16,GotoIf($["${counter}"<"${sipcount}"]?14:17) +; +exten => _X.,17,Set(iaxcount=${ENUMLOOKUP(${EXTEN},iax2,c,e164.org)}|counter=0) +exten => _X.,18,GotoIf($["${counter}"<"${iaxcount}"]?19:22) +exten => _X.,19,Set(counter=$[${counter}+1]) +exten => _X.,20,Dial(IAX2/${ENUMLOOKUP(+${EXTEN},iax,${counter},e164.org)}) +exten => _X.,21,GotoIf($["${counter}"<"${iaxcount}"]?19:22) +; +; ...then send out PRI. +; +exten => _X.,22,NoOp("No valid entries in e164.org for ${EXTEN} - sending out via Zap") +exten => _X.,23,Dial(Zap/g1/${EXTEN}) +; +; end example 3 @@ -87,9 +87,12 @@ static int parse_ie(char *data, int maxdatalen, char *src, int srclen) /*--- parse_naptr: Parse DNS NAPTR record used in ENUM ---*/ static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, char *answer, int len, char *naptrinput) { + + char tech_return[80]; char *oanswer = answer; char flags[512] = ""; char services[512] = ""; + unsigned char *p; char regexp[512] = ""; char repl[512] = ""; char temp[512] = ""; @@ -102,9 +105,10 @@ static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, char *a regex_t preg; regmatch_t pmatch[9]; + tech_return[0] = '\0'; dst[0] = '\0'; - + if (len < sizeof(struct naptr)) { ast_log(LOG_WARNING, "NAPTR record length too short\n"); return -1; @@ -113,29 +117,30 @@ static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, char *a len -= sizeof(struct naptr); if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) { ast_log(LOG_WARNING, "Failed to get flags from NAPTR record\n"); - return -1; - } else { - answer += res; - len -= res; + return -1; + } else { + answer += res; + len -= res; } if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) { ast_log(LOG_WARNING, "Failed to get services from NAPTR record\n"); - return -1; - } else { - answer += res; - len -= res; + return -1; + } else { + answer += res; + len -= res; } if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0) { ast_log(LOG_WARNING, "Failed to get regexp from NAPTR record\n"); - return -1; - } else { - answer += res; - len -= res; + return -1; + } else { + answer += res; + len -= res; } + if ((res = dn_expand((unsigned char *)oanswer, (unsigned char *)answer + len, (unsigned char *)answer, repl, sizeof(repl) - 1)) < 0) { ast_log(LOG_WARNING, "Failed to expand hostname\n"); return -1; - } + } if (option_debug > 2) /* Advanced NAPTR debugging */ ast_log(LOG_DEBUG, "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n", @@ -146,29 +151,27 @@ static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, char *a return -1; } - if ((!strncasecmp(services, "e2u+sip", 7)) || - (!strncasecmp(services, "sip+e2u", 7))) { - ast_copy_string(tech, "sip", techsize); - } else if ((!strncasecmp(services, "e2u+h323", 8)) || - (!strncasecmp(services, "h323+e2u", 8))) { - ast_copy_string(tech, "h323", techsize); - } else if ((!strncasecmp(services, "e2u+x-iax2", 10)) || - (!strncasecmp(services, "e2u+iax2", 8)) || - (!strncasecmp(services, "iax2+e2u", 8))) { - ast_copy_string(tech, "iax2", techsize); - } else if ((!strncasecmp(services, "e2u+x-iax", 9)) || - (!strncasecmp(services, "e2u+iax", 7)) || - (!strncasecmp(services, "iax+e2u", 7))) { - ast_copy_string(tech, "iax", techsize); - } else if ((!strncasecmp(services, "e2u+tel", 7)) || - (!strncasecmp(services, "tel+e2u", 7))) { - ast_copy_string(tech, "tel", techsize); - } else if (!strncasecmp(services, "e2u+voice:", 10)) { - ast_copy_string(tech, services+10, techsize); + p = strstr(services, "e2u+"); + if(p == NULL) + p = strstr(services, "E2U+"); + if(p){ + p = p + 4; + if(strchr(p, ':')){ + p = strchr(p, ':') + 1; + } + ast_copy_string(tech_return, p, sizeof(tech_return)); } else { - ast_log(LOG_DEBUG, - "Services must be e2u+${tech}, ${tech}+e2u, or e2u+voice: where $tech is from (sip, h323, tel, iax, iax2). \n"); - return 0; + + p = strstr(services, "+e2u"); + if(p == NULL) + p = strstr(services, "+E2U"); + if(p){ + *p = 0; + p = strchr(services, ':'); + if(p) + *p = 0; + ast_copy_string(tech_return, services, sizeof(tech_return)); + } } /* DEDBUGGING STUB @@ -179,7 +182,7 @@ static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, char *a if (regexp_len < 7) { ast_log(LOG_WARNING, "Regex too short to be meaningful.\n"); return -1; - } + } delim = regexp[0]; @@ -197,6 +200,7 @@ static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, char *a #if 0 printf("Pattern: %s\n", pattern); printf("Subst: %s\n", subst); + printf("Input: %s\n", naptrinput); #endif /* @@ -221,8 +225,8 @@ static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, char *a } regfree(&preg); - d = temp; - d_len--; + d = temp; + d_len--; while( *subst && (d_len > 0) ) { if ((subst[0] == '\\') && isdigit(subst[1]) && (pmatch[subst[1]-'0'].rm_so != -1)) { backref = subst[1]-'0'; @@ -246,9 +250,35 @@ static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, char *a *d = 0; ast_copy_string(dst, temp, dstsize); dst[dstsize - 1] = '\0'; - return 0; + + if(*tech != '\0'){ /* check if it is requested NAPTR */ + if(!strncasecmp(tech, "ALL", techsize)){ + return 1; /* return or count any RR */ + } + if(!strncasecmp(tech_return, tech, sizeof(tech_return)<techsize?sizeof(tech_return):techsize)){ + ast_copy_string(tech, tech_return, techsize); + return 1; /* we got out RR */ + } else { /* go to the next RR in the DNS answer */ + return 0; + } + } + + /* tech was not specified, return first parsed RR */ + ast_copy_string(tech, tech_return, techsize); + + return 1; } +/* do not return requested value, just count RRs and return thei number in dst */ +#define ENUMLOOKUP_OPTIONS_COUNT 1 + +struct enum_naptr_rr { + struct naptr naptr; /* order and preference of RR */ + char *result; /* result of naptr parsing,e.g.: tel:+5553 */ + char *tech; /* Technology (from URL scheme) */ + int sort_pos; /* sort position */ +}; + struct enum_context { char *dst; /* Destination part of URL from ENUM */ int dstlen; /* Length */ @@ -257,6 +287,10 @@ struct enum_context { char *txt; /* TXT record in TXT lookup */ int txtlen; /* Length */ char *naptrinput; /* The number to lookup */ + int position; /* used as counter for RRs or specifies position of required RR */ + int options; /* options , see ENUMLOOKUP_OPTIONS_* defined above */ + struct enum_naptr_rr *naptr_rrs; /* array of parsed NAPTR RRs */ + int naptr_rrs_count; /* Size of array naptr_rrs */ }; /*--- txt_callback: Callback for TXT record lookup */ @@ -278,7 +312,7 @@ static int txt_callback(void *context, char *answer, int len, char *fullanswer) len -= 1; /* answer is not null-terminated, but should be */ - /* this is safe to do, as answer has extra bytes on the end we can + /* this is safe to do, as answer has extra bytes on the end we can safely overwrite with a null */ answer[len] = '\0'; /* now increment len so that len includes the null, so that we can @@ -287,7 +321,7 @@ static int txt_callback(void *context, char *answer, int len, char *fullanswer) /* finally, copy the answer into c->txt */ ast_copy_string(c->txt, answer, len < c->txtlen ? len : (c->txtlen)); - + /* just to be safe, let's make sure c->txt is null terminated */ c->txt[(c->txtlen)-1] = '\0'; @@ -298,45 +332,122 @@ static int txt_callback(void *context, char *answer, int len, char *fullanswer) static int enum_callback(void *context, char *answer, int len, char *fullanswer) { struct enum_context *c = (struct enum_context *)context; + void *p = NULL; + int res; - if (parse_naptr(c->dst, c->dstlen, c->tech, c->techlen, answer, len, c->naptrinput)) { + res = parse_naptr(c->dst, c->dstlen, c->tech, c->techlen, answer, len, c->naptrinput); + + if(res < 0){ ast_log(LOG_WARNING, "Failed to parse naptr :(\n"); return -1; + } else if(res > 0 && !ast_strlen_zero(c->dst)){ /* ok, we got needed NAPTR */ + if(c->options & ENUMLOOKUP_OPTIONS_COUNT){ /* counting RRs */ + c->position++; + snprintf(c->dst, c->dstlen, "%d", c->position); + } else { + p = realloc(c->naptr_rrs, sizeof(struct enum_naptr_rr)*(c->naptr_rrs_count+1)); + if(p){ + c->naptr_rrs = (struct enum_naptr_rr*)p; + memcpy(&c->naptr_rrs[c->naptr_rrs_count].naptr, answer, sizeof(struct naptr)); + /* 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)); */ + c->naptr_rrs[c->naptr_rrs_count].result = strdup(c->dst); + c->naptr_rrs[c->naptr_rrs_count].tech = strdup(c->tech); + c->naptr_rrs[c->naptr_rrs_count].sort_pos = c->naptr_rrs_count; + c->naptr_rrs_count++; + } + c->dst[0] = 0; + } + return 0; } - if (!ast_strlen_zero(c->dst)) - return 1; + if(c->options & ENUMLOOKUP_OPTIONS_COUNT){ /* counting RRs */ + snprintf(c->dst, c->dstlen, "%d", c->position); + } return 0; } /*--- ast_get_enum: ENUM lookup */ -int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen) +int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char* suffix, char* options) { struct enum_context context; char tmp[259 + 512]; - char naptrinput[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); + /* for ISN rewrite */ + char *p1 = NULL; + char *p2 = NULL; + int k = 0; + int i = 0; + int z = 0; + + if(number[0] == 'n'){ + strncpy(naptrinput, number+1, sizeof(naptrinput)); + } else { + strncpy(naptrinput, number, sizeof(naptrinput)); + } context.naptrinput = naptrinput; /* The number */ context.dst = dst; /* Return string */ context.dstlen = dstlen; - context.tech = tech; /* Return string */ + context.tech = tech; context.techlen = techlen; + context.options = 0; + context.position = 1; + context.naptr_rrs = NULL; + context.naptr_rrs_count = 0; + + if(options != NULL){ + if(*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; - while(pos >= 0) { - tmp[newpos++] = number[pos--]; - tmp[newpos++] = '.'; + + /* ISN rewrite */ + p1 = strchr(number, '*'); + + if(number[0] == 'n'){ /* do not perform ISN rewrite ('n' is testing flag) */ + p1 = NULL; + k = 1; /* strip 'n' from number */ + } + + if(p1 != NULL){ + p2 = p1+1; + while(p1 > number){ + p1--; + tmp[newpos++] = *p1; + tmp[newpos++] = '.'; + } + if(*p2){ + while(*p2 && newpos < 128){ + tmp[newpos++] = *p2; + p2++; + } + tmp[newpos++] = '.'; + } + + } else { + while(pos >= k) { + if(isdigit(number[pos])){ + tmp[newpos++] = number[pos]; + tmp[newpos++] = '.'; + } + pos--; + } } - + if (chan && ast_autoservice_start(chan) < 0) return -1; @@ -349,7 +460,9 @@ int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int ds } else { s = s->next; } - if (s) { + if(suffix != NULL){ + strncpy(tmp + newpos, suffix, sizeof(tmp) - newpos - 1); + } else if (s) { strncpy(tmp + newpos, s->toplev, sizeof(tmp) - newpos - 1); } ast_mutex_unlock(&enumlock); @@ -358,17 +471,65 @@ int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int ds ret = ast_search_dns(&context, tmp, C_IN, T_NAPTR, enum_callback); if (ret > 0) break; + if(suffix != NULL) + break; } if (ret < 0) { ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno)); ret = 0; } + + if(context.naptr_rrs_count >= context.position && ! (context.options & ENUMLOOKUP_OPTIONS_COUNT)){ + /* sort array by NAPTR order/preference */ + for(k=0; k<context.naptr_rrs_count; k++){ + for(i=0; i<context.naptr_rrs_count; i++){ + /* use order first and then preference to compare */ + if((ntohs(context.naptr_rrs[k].naptr.order) < ntohs(context.naptr_rrs[i].naptr.order) + && context.naptr_rrs[k].sort_pos > context.naptr_rrs[i].sort_pos) + || (ntohs(context.naptr_rrs[k].naptr.order) > ntohs(context.naptr_rrs[i].naptr.order) + && context.naptr_rrs[k].sort_pos < context.naptr_rrs[i].sort_pos)){ + z = context.naptr_rrs[k].sort_pos; + context.naptr_rrs[k].sort_pos = context.naptr_rrs[i].sort_pos; + context.naptr_rrs[i].sort_pos = z; + continue; + } + if(ntohs(context.naptr_rrs[k].naptr.order) == ntohs(context.naptr_rrs[i].naptr.order)){ + if((ntohs(context.naptr_rrs[k].naptr.pref) < ntohs(context.naptr_rrs[i].naptr.pref) + && context.naptr_rrs[k].sort_pos > context.naptr_rrs[i].sort_pos) + || (ntohs(context.naptr_rrs[k].naptr.pref) > ntohs(context.naptr_rrs[i].naptr.pref) + && context.naptr_rrs[k].sort_pos < context.naptr_rrs[i].sort_pos)){ + z = context.naptr_rrs[k].sort_pos; + context.naptr_rrs[k].sort_pos = context.naptr_rrs[i].sort_pos; + context.naptr_rrs[i].sort_pos = z; + } + } + } + } + for(k=0; k<context.naptr_rrs_count; k++){ + if(context.naptr_rrs[k].sort_pos == context.position-1){ + ast_copy_string(context.dst, context.naptr_rrs[k].result, dstlen); + ast_copy_string(context.tech, context.naptr_rrs[k].tech, techlen); + break; + } + } + } else if( !(context.options & ENUMLOOKUP_OPTIONS_COUNT) ) { + context.dst[0] = 0; + } + if (chan) ret |= ast_autoservice_stop(chan); + + for(k=0; k<context.naptr_rrs_count; k++){ + free(context.naptr_rrs[k].result); + free(context.naptr_rrs[k].tech); + } + + free(context.naptr_rrs); + return ret; } -/*--- ast_get_txt: Get TXT record from DNS. +/*--- ast_get_txt: Get TXT record from DNS. Really has nothing to do with enum, but anyway... */ int ast_get_txt(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char *txt, int txtlen) @@ -398,7 +559,7 @@ int ast_get_txt(struct ast_channel *chan, const char *number, char *dst, int dst tmp[newpos++] = number[pos--]; tmp[newpos++] = '.'; } - + if (chan && ast_autoservice_start(chan) < 0) return -1; diff --git a/funcs/func_enum.c b/funcs/func_enum.c new file mode 100755 index 000000000..3128047e4 --- /dev/null +++ b/funcs/func_enum.c @@ -0,0 +1,174 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Enum Functions + * + * Copyright (C) 2005 + * + * Oleksiy Krivoshey <oleksiyk@gmail.com> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include <stdlib.h> + +#include "asterisk.h" + +#ifndef BUILTIN_FUNC +#include "asterisk/module.h" +#endif /* BUILTIN_FUNC */ +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/utils.h" + +#include "asterisk/lock.h" +#include "asterisk/file.h" +#include "asterisk/logger.h" + +#include "asterisk/pbx.h" +#include "asterisk/options.h" + +#include "asterisk/enum.h" + +static char* synopsis = "Syntax: ENUMLOOKUP(number[,Method-type[,options|record#[,zone-suffix]]])\n"; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +static char *function_enum(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) +{ + int res=0; + char tech[80]; + char dest[80] = ""; + char *zone; + char *options; + struct localuser *u; + char *params[4]; + char *p = data; + char *s; + int i = 0; + + + if (!data || ast_strlen_zero(data)) { + ast_log(LOG_WARNING, synopsis); + return ""; + } + + do { + if(i>3){ + ast_log(LOG_WARNING, synopsis); + return ""; + } + params[i++] = p; + p = strchr(p, '|'); + if(p){ + *p = '\0'; + p++; + } + } while(p); + + if(i < 1){ + ast_log(LOG_WARNING, synopsis); + return ""; + } + + if( (i > 1 && strlen(params[1]) == 0) || i < 2){ + ast_copy_string(tech, "sip", sizeof(tech)); + } else { + ast_copy_string(tech, params[1], sizeof(tech)); + } + + if( (i > 3 && strlen(params[3]) == 0) || i<4){ + zone = "e164.arpa"; + } else { + zone = params[3]; + } + + if( (i > 2 && strlen(params[2]) == 0) || i<3){ + options = "1"; + } else { + options = params[2]; + } + + /* strip any '-' signs from number */ + p = params[0]; + /* + while(*p == '+'){ + p++; + } + */ + s = p; + i = 0; + while(*p && *s){ + if(*s == '-'){ + s++; + } else { + p[i++] = *s++; + } + } + p[i] = 0; + + LOCAL_USER_ACF_ADD(u); + + res = ast_get_enum(chan, p, dest, sizeof(dest), tech, sizeof(tech), zone, options); + + LOCAL_USER_REMOVE(u); + + p = strchr(dest, ':'); + if(p && strncasecmp(tech, "ALL", sizeof(tech))) { + ast_copy_string(buf, p+1, sizeof(dest)); + } else { + ast_copy_string(buf, dest, sizeof(dest)); + } + + return buf; +} + + +#ifndef BUILTIN_FUNC +static +#endif +struct ast_custom_function enum_function = { + .name = "ENUMLOOKUP", + .synopsis = "ENUMLOOKUP allows for general or specific querying of NAPTR records" + " or counts of NAPTR types for ENUM or ENUM-like DNS pointers", + .syntax = "ENUMLOOKUP(number[,Method-type[,options|record#[,zone-suffix]]])", + .desc = "Option 'c' returns an integer count of the number of NAPTRs of a certain RR type.\n" + "Combination of 'c' and Method-type of 'ALL' will return a count of all NAPTRs for the record.\n" + "Defaults are: Method-type=sip, no options, record=1, zone-suffix=e164.arpa\n\n" + "For more information, see README.enum", + .read = function_enum, +}; + +#ifndef BUILTIN_FUNC + +static char *tdesc = "ENUMLOOKUP allows for general or specific querying of NAPTR records or counts of NAPTR types for ENUM or ENUM-like DNS pointers"; + +int unload_module(void) +{ + return ast_custom_function_unregister(&enum_function); +} + +int load_module(void) +{ + return ast_custom_function_register(&enum_function); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + return 0; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} +#endif /* BUILTIN_FUNC */ + diff --git a/include/asterisk/enum.h b/include/asterisk/enum.h index e52d1db20..732a42a85 100755 --- a/include/asterisk/enum.h +++ b/include/asterisk/enum.h @@ -29,20 +29,25 @@ #include "asterisk/channel.h" -/*! \brief Lookup entry in ENUM Returns 1 if found, 0 if not found, -1 on hangup +/*! \brief Lookup entry in ENUM Returns 1 if found, 0 if not found, -1 on hangup \param chan Channel - \param number Number in E164 format without the + (for e164.arpa) or format - requested by enum service used (enum.conf) + \param number E164 number with or without the leading + \param location Number returned (or SIP uri) \param maxloc Max length \param tech Technology (from url scheme in response) \param maxtech Max length -*/ -extern int ast_get_enum(struct ast_channel *chan, const char *number, char *location, int maxloc, char *technology, int maxtech); + \param tech Technology (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 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 +*/ +extern int ast_get_enum(struct ast_channel *chan, const char *number, char *location, int maxloc, char *technology, int maxtech, char* suffix, char* options); /*! \brief Lookup DNS TXT record (used by app TXTCIDnum \param chan Channel - \param number E164 number without the + + \param number E164 number with or without the leading + \param locatio Number returned (or SIP uri) \param maxloc Max length of number \param tech Technology (not used in TXT records) |