RFC Compliant ENUM Macro


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 &macro). 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 <xrobau@gmail.com>
;
; 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: http://www.voip-info.org/wiki/view/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 &macro instead of Gosub().
  • Rewrite to use ENUMLOOKUP() instead of ENUMQUERY() and ENUMRESULT().


/*
RFC Compliant ENUM Macro

Main author: Luke-Jr <luke@dashjr.org>
Based on code by Rob Thomas <xrobau@gmail.com>

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: http://www.voip-info.org/wiki/view/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;
};




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 &macro). 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 <xrobau@gmail.com>
;
; 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: http://www.voip-info.org/wiki/view/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 &macro instead of Gosub().
  • Rewrite to use ENUMLOOKUP() instead of ENUMQUERY() and ENUMRESULT().


/*
RFC Compliant ENUM Macro

Main author: Luke-Jr <luke@dashjr.org>
Based on code by Rob Thomas <xrobau@gmail.com>

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: http://www.voip-info.org/wiki/view/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;
};



Created by: xrobau, Last modification: Mon 05 of Oct, 2015 (14:48 UTC) by JonasSmedegaard
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+