Synopsis
Set ‘DIAL_NUMBER’ to be the number you wish to do an enumlookup on, and hand it to this.
Recent code changes
- 2015-10-05: Use “same” prefix. Only ext script.
- 2010-03-29: Rename label for use with Gosub. Only ext script.
- 2010-03-29: Add NRENum.net to E164NETWORKS.
- 2010-03-29: Use ENUMQUERY and ENUMRESULT.
- 2010-03-29: Improved tel: redirection. Only AEL script.
- 2010-03-28: Update to v1.6 syntax (Gosub(), not ¯o). Only AEL script.
- 2010-03-28: Consistenly use enumdial context.
- 2008-11-27: Delimit e.164 hosts with space (not dash).
- 2008-06-14: Fix avoid quotes in numeric comparison. Only ext script.
- 2007-11-30: Fix avoid quotes in numeric comparison. Only ext script.
- 2007-05-25: Update to v1.4 ENUMLOOKUP syntax (5 params, not 4).
Example
exten => s,1,Gosub(enumdial,s,1(7079964444))
Original ext implementation
Requires Asterisk 1.6.2. Might work on earlier releases if changing the following:
- Rewrite to use ENUMLOOKUP() instead of ENUMQUERY() and ENUMRESULT().
- Rewrite to use “exten => …” instead of “same => “.
; RFC Compliant ENUM Macro
;
; Main author: Rob Thomas <[email protected]>
;
; Set 'DIAL_NUMBER' to be the number you wish to do an enumlookup on,
; and hand it to this.
;
; Latest code change: 2015-10-05 (wiki page rev. 36)
;
; Origin: https://www.voip-info.org/rfc-compliant-enum-macro/
[enumdial]
exten => s,1,Set(DIAL_NUMBER=${ARG1})
same => n,Set(E164NETWORKS=e164.arpa nrenum.net e164.info e164.org)
same => n,GotoIf($[${DIAL_NUMBER:0:1} = "+"]?begin) ; Skip next line if it already is prefixed by a plus
same => n,Set(DIAL_NUMBER=+${DIAL_NUMBER}) ; Add a plus to the start, because ENUMLOOKUP needs it.
; Checking here to see if there are any e164 networks left to check.
same => n(begin),GotoIf($[${LEN(${E164NETWORKS})} < 2]?failed)
; There are, so we take the first one
same => n,Set(ENUMNET=${CUT(E164NETWORKS, ,1)})
; And trim it from the front of E164NETWORKS
same => n,Set(E164NETWORKS=${CUT(E164NETWORKS, ,2-)})
; OK, this is now quite complex. To remain compliant, we have to iterate
; through, in order, the returned records. Since we want to make this
; call over the network, we can ignore tel: lines. Even if it's first
; priority.
same => n,Set(ENUMID=${ENUMQUERY(${DIAL_NUMBER},ALL,${ENUMNET})})
same => n,Set(ENUMCOUNT=${ENUMRESULT(${ENUMID},getnum)})
; Documentation is wrong. It can return nothing if the enum lookup fails. Grr.
; Now the count may be zero, so if it is, check the next network
same => n,GotoIf($["${ENUMCOUNT}" = "0"]?begin)
same => n,GotoIf($["x${ENUMCOUNT}" = "x"]?begin)
; Now, let's start through them.
same => n,Set(ENUMPTR=1)
same => n(startloop),Set(ENUM=${ENUMRESULT(${ENUMID},${ENUMPTR})})
; Sanity check the return, make sure there's something in there.
same => n,GotoIf($[${LEN(${ENUM})} = 0]?continue)
same => n,GotoIf($["${ENUM:0:3}" = "iax"]?iaxuri)
same => n,GotoIf($["${ENUM:0:3}" = "sip"]?sipuri)
; It doesn't matter if you don't have h323 enabled, as when it tries to dial, it cares
; about dialstatus and retries if there are any enum results left.
same => n,GotoIf($[${ENUM:0:3} = "h32"]?h323uri)
; If we're here, it's not a protocol we know about. Let's increment the pointer
; and if it's more than ENUMCOUNT, we know we've run out of options. Try the
; next e164 network.
same => n(continue),Set(ENUMPTR=$[${ENUMPTR} + 1])
same => n,GotoIf($[${ENUMPTR} > ${ENUMCOUNT}]?begin)
; OK. If we're here, we've still got some enum entries to go through. Back to
; the start with you!
same => n,Goto(startloop)
; If the prefix is 'sip:'...
same => n(sipuri),Set(DIALSTR=SIP/${ENUM:4})
same => n,Goto(dodial)
; If it's IAX2...
same => n(iaxuri),Set(DIALSTR=IAX2/${ENUM:5})
same => n,Goto(dodial)
; Or even if it's H323.
same => n(h323uri),Set(DIALSTR=H323/${ENUM:5})
same => n(dodial),Dial(${DIALSTR})
same => n,NoOp(Dial exited in macro-enum-dialout with ${DIALSTATUS})
; Now, if we're still here, that means the Dial failed for some reason.
; If it's CONGESTION or CHANUNAVAIL we probably want to try again on a
; different channel. However, if it's the last one, we don't have any
; left, and I didn't keep any previous dialstatuses, so hopefully
; someone looking throught the logs would have seen the NoOp's
same => n,GotoIf($[${ENUMPTR} = ${ENUMCOUNT}]?noneleft)
same => n,GotoIf($[$[${DIALSTATUS} = "CHANUNAVAIL"] | $[${DIALSTATUS} = "CONGESTION"] ]?continue)
; If we're here, then it's BUSY or NOANSWER or something and well, deal with it.
same => n(noneleft),Goto(s-${DIALSTATUS},1)
; Here are the exit points for the macro.
same => n(failed),NoOp(Enum lookups failed)
same => n,Goto(end)
same => n(nochans),NoOp(max channels used up)
same => n(end),NoOp(Exiting enumdial)
exten => s-BUSY,1,NoOp(Trunk is reporting BUSY)
same => n,Busy()
same => n,Wait(60)
same => n,NoOp()
exten => _s-.,1,NoOp(Enum dial failed due to ${DIALSTATUS})
AEL implementation
Functional differences from original:
- takes additional arguments:
- IsPOTS – generic flag if number called is a POTS/PSTN number or provider-specific (unused currently)
- Timeout
- DialOpts
- looks up tel: URLs
- limited to 5 successive lookups to avoid endless loop
- does not (but should it?) add delay at dialstatus BUSY
Requires Asterisk 1.6. Might work on earlier releases if changing the following:
- Rewrite to use ¯o instead of Gosub().
- Rewrite to use ENUMLOOKUP() instead of ENUMQUERY() and ENUMRESULT().
/*
RFC Compliant ENUM Macro
Main author: Luke-Jr <[email protected]>
Based on code by Rob Thomas <[email protected]>
Set 'DIAL_NUMBER' to be the number you wish to do an enumlookup on, and
hand it to this.
Latest code change: 2010-03-29 (wiki page rev. 34)
Origin: https://www.voip-info.org/rfc-compliant-enum-macro/
*/
macro enumdial(exten, IsPOTS, Timeout, DialOpts) {
Set(DIAL_NUMBER=${exten});
Set(E164NETWORKS=e164.arpa nrenum.net e164.info e164.org);
if ("${DIAL_NUMBER:0:1}" != "+")
// Add a plus to the start, because ENUMLOOKUP needs it.
Set(DIAL_NUMBER=+${DIAL_NUMBER});
Set(ENUMSEEN=${ENUMSEEN} ${DIAL_NUMBER});
if (${FIELDQTY(ENUMSEEN, )} > 5) { // max enum depth we will go
// This is to prevent a looping enum entry; there is no
// other safe way to be sure we don't get stuck in such
// a trap.
Set(DIALSTATUS=EnumMaxDepth);
goto end;
};
// Checking here to see if there are any e164 networks left to
// check.
for (Gosub(iterator,s,1(${E164NETWORKS},ENUMNET)); "${ENUMNET}" != ""; Gosub(iterate,s,1(ENUMNET))) {
// OK, this is now quite complex. To remain compliant,
// we have to iterate through, in order, the returned
// records.
Set(ENUMID=${ENUMQUERY(${DIAL_NUMBER},ALL,${ENUMNET})});
Set(ENUMCOUNT=${ENUMRESULT(${ENUMID},getnum)});
// Documentation is wrong. It can return nothing if the
// enum lookup fails. Grr.
if ("${ENUMCOUNT}" = "")
ENUMCOUNT=0;
// Now, let's start through them.
for (ENUMPTR=1; ${ENUMPTR} <= ${ENUMCOUNT}; ENUMPTR=${ENUMPTR} + 1) {
Set(ENUM=${ENUMRESULT(${ENUMID},${ENUMPTR})});
// Sanity check the return, make sure there's
// something in there.
if (${LEN(${ENUM})} = 0)
continue;
Set(DIALSTR=);
Set(DIALSTATUS=);
switch(${CUT(ENUM,:,1)}) {
case sip:
Set(DIALSTR=SIP/${ENUM:4});
break;
case iax2:
Set(DIALSTR=IAX2/${ENUM:5});
break;
// It doesn't matter if you don't have h323
// enabled, as when it tries to dial, it cares
// about dialstatus and retries if there are any
// enum results left.
case h323:
Set(DIALSTR=H323/${ENUM:5});
break;
case tel:
// redirect to another number...
if (${"LISTFILTER(ENUMSEEN, ,${DIAL_NUMBER})}" = "${DIAL_NUMBER}") {
NoOp(Redirect to tel: ${ENUM});
Gosub(enumdial,s,1(${ENUM:4}));
} else {
NoOp(Skip already tried tel: ${ENUM});
Set(DIALSTATUS=CONGESTION);
};
break;
default:
// If we're here, it's not a protocol we
// know about.
Set(DIALSTATUS=CHANUNAVAIL);
break;
};
if ("${DIALSTR}" != "") {
Dial(${DIALSTR},${Timeout},o${DialOpts});
NoOp(Dial exited in enumdial with ${DIALSTATUS});
};
// Now, if we're still here, that means the Dial
// failed for some reason.
// If it's CONGESTION or CHANUNAVAIL we probably
// want to try again on a different channel.
// However, if it's the last one, we don't have
// any left, and I didn't keep any previous
// dialstatuses, so hopefully someone looking
// throught the logs would have seen the NoOp's
if ("${DIALSTATUS}" != "CONGESTION" & "${DIALSTATUS}" != "CHANUNAVAIL") {
NoOp(Enum Dial(${DIALSTR}) failed due to ${DIALSTATUS});
goto end;
};
};
};
end:
NoOp(EnumLookups failed);
return;
};
macro iterate(varname) {
current=${${varname}_current} + 1;
SET(${varname}=${CUT(${varname}_choices, ,${current})});
SET(${varname}_current=${current});
return;
};
macro iterator(choices, varname) {
SET(${varname}_choices=${choices});
SET(${varname}_current=0);
Gosub(iterate,s,1(${varname}));
return;
};