Asterisk Bounty ENUM app re-write

Here's the post. Please also see bug in mantis for the summary. Bounty is $150 USD payable via check or (preferred) PayPal.

Please ignore the wacky formatting in this post, and use the bug tracker for the real text. The wiki is eating many of the punctuation marks and inserting boxes and things where they're not wanted.

To: Edwin Groothuis <>
From: John Todd <>
Subject: Fwd: [Asterisk-Dev] ENUM multiple records handling

sorry for top-posting; the original post was fairly long

Edwin -
I saw from the Mantis notes on this that it wasn't implemented, because of unfortunate timing (conversion from apps to functions, and Kevin desired this as a function instead of an app.)

I have some real interest in getting ENUM to work more intelligently than it does now, since I think ENUM holds great promise (though is also fundamentally broken, as well, but that's a different story.) I'd put $100 towards getting this done as a function, if you (or someone) could re-write it. I also have another request, as well, if that is to be done: I'd like to see the ENUM zone able to be specified from the function call, so I can customize which zone I'd like to do the lookup in. This is different than your linked list idea, but it relies more on "hard" requests rather than "soft" looping array pointers which have their count values hidden from the administrator and which may get very confusing.

This function is different than any of the previous incarnations of EnumLookup (either in CVS or patches that have been submitted) because it does NOT assume a limited array of pointer types, and it also allows the dialplan designer to intelligently select or incrementally cascade through a list of pointer types and pointer preferences, and by doing so become more in sync with the goals of ENUM in general. Some of the flaws in my implementation are that I jumble some of the pointer types in the most simplistic methods of use, but there is a second method I show (the "ALL" incremental counter) which allows the administrator to really get their hands dirty and get the full query return values.

Function: EnumLookup(<number>[,pointer_type[,options[,zone_suffix]]])
  Performs an ENUM tree lookup on the specified number/pointer type and optional ordinal offset, and returns one of four different values (NAPTR of one pointer type, count of elements of one pointer type, count of all pointer types, or type of pointer.

<number> = e164 number

pointer_type = tel, sip, h323, iax2, mailto, ...[others], ALL. Default type is "sip". 
     Special name of "ALL" will create a list of pointer_types across all orders, run a "uniq" pass across them, and then put the results in an ordinal list starting with 1. The <number> specified will then be returned. If ALL is specified and there is no following <integer> or no pointers in the list, the routine returns a null value. (see Example 6, below.) The pointer types are not hardcoded in Asterisk except for the default if no other pointer type specified; any valid pointer type 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)
    <integer> = The record in priority/order sequence based on the total count of records passed back by the query. If a pointer 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


Let's use this ENUM list as an example (note the slight order/priority changes from the real world, and pardon my poor NAPTR regexp processing if I've made any errors in return values): 3600 IN NAPTR 10 100 "u" "tel+E2U" "!^\\+16503816199$!tel:+5551!" . 3600 IN NAPTR 21 100 "u" "tel+E2U" "!^\\+16503816199$!tel:+5553!" . 3600 IN NAPTR 25 100 "u" "sip+E2U" "!^\\+16503816199$!!" . 3600 IN NAPTR 25 100 "u" "sip+E2U" "!^\\+16503816199$!!" . 3600 IN NAPTR 55 100 "u" "mailto+E2U" "!^\\+16503816199$!!" .

Example 1: Simplest case, using first SIP return (all defaults)
exten => 100,1,Set(foo=EnumLookup(16503816199))
returns: ${foo}=""

Example 2: What is the first "tel" pointer type for this number? (after sorting by order/preference)
exten => 100,1,Set(foo=EnumLookup(16503816199,tel))
returns: ${foo}="+5551"

Example 3: How many "sip" pointer type entries are there for this number?
exten => 100,1,Set(foo=EnumLookup(16503816199,sip,c))
returns: ${foo}=2

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(16503816199,tel,2))
returns: ${foo}="+5553"

Example 5: What is the first pointer type referenced for this number? (after sorting by order/preference)
exten => 100,1,Set(foo=EnumLookup(16503816199,POINTERS))
returns: ${foo}="tel"

Example 6: What is the third pointer type referenced for this number? (after sorting by order/preference)
exten => 100,1,Set(foo=EnumLookup(16503816199,POINTERS,3))
returns: ${foo}="mailto"

Example 7: How many pointer types (tel, sip, mailto, etc.) are in the list for this number?
exten => 100,1,Set(foo=EnumLookup(16503816199,POINTERS,c))
returns: ${foo}=3

Example 8: Give back the first SIP pointer for the number in the zone (invalid lookup)
exten => 100,1,Set(foo=EnumLookup(16503816199,sip,1,
returns: ${foo}=[null]

With this method, you could discover:
  1. If there were any ENUM entries at all for the channel types you support
  2. If there are entries for more than 1 channel type you support, you can discover what they are, in order of preference
  3. If there is more than one priority for a channel type you support, you can discover what they are, in order of preference

This may take some looping within the dialplan, but it's actually not all that complex. No more or less complex than writing it into an AGI, and certainly a lot more portable.

a) If a query is performed of type "count" (let's say you get back 5 records) and then some seconds later a query is made against 0000005 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. 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 the dnsmgr method of locally caching DNS replies, but they need to honor the TTL given by the remote zone master.)

b) The "h323" specifier in the enum.conf file becomes irrelevant, and that whole config file can go away now since we can do sequential zone lookups from within the dialplan by referencing the EnumLookup function iteratively in the dialplan.

c) It is difficult, if not impossible, to sort a list of ordered URIs that have recurring types out of order (i.e.: 10 tel, 20 sip, 30 tel)

d) Default behavior (even in event of an error) should be to jump to the next priority. Leaping to +101 or +53 or whatever, sucks. Most ENUM lookups are going to be failures. Anyone trying to get ENUM to work should have somewhat clueful programming skills, so checking the value of the variable should be sufficient to determine if a successful lookup has happened (which is, I believe, one of the whole points for moving to functions in the first place.)


Created by: jtodd, Last modification: Wed 02 of May, 2012 (07:51 UTC) by admin
Please update this page with new information, just login and click on the "Edit" or "Discussion" tab. Get a free login here: Register Thanks! - Find us on Google+