Skip to content

Instantly share code, notes, and snippets.

@lgaetz
Last active November 30, 2024 00:03
Show Gist options
  • Save lgaetz/bdc261b0d12efafeb1eed64651c7d32b to your computer and use it in GitHub Desktop.
Save lgaetz/bdc261b0d12efafeb1eed64651c7d32b to your computer and use it in GitHub Desktop.
FreePBX Dialplan Captcha
; FreePBX Dialplan to block robocalls
;
; License: GNU GPL3+
; Latest version: https://gist.github.com/lgaetz/bdc261b0d12efafeb1eed64651c7d32b
;
;
; Usage: Place the dialplan below in /etc/asterisk/extensions_custom.conf
; change the context for the trunk(s) to from-pstn-captcha1
; Apply Config
;
; version history 2024-11-29 First commit, working but needs broader testing
[from-pstn-captcha1]
exten => _.,1,Noop(Entering user defined context from-pstn-captcha1 in extensions_custom.conf)
; first normalize CID/DID to 10 digits
exten => _.,n,Goto(from-pstn-captcha2,${EXTEN},1)
exten => h,1,Hangup()
exten => t,1,Noop()
[from-pstn-captcha2]
; Doing this to normalize CID and DID to 10 digits so blocklist and allowlist work efficiently
; you'll want to skip or rework this entire context for non NANP numbers
; based on the from-pstn-e164-us context included in fpbx
; change CID and DID from e164 to 10 digit
exten => _+1NXXNXXXXXX/_+1NXXNXXXXXX,1,Set(CALLERID(number)=${CALLERID(number):2})
exten => _+1NXXNXXXXXX/_NXXNXXXXXX,2,Goto(from-pstn-captcha3,${EXTEN:2},1)
exten => _+1NXXNXXXXXX/_+NX.,1,Set(CALLERID(number)=011${CALLERID(number):1})
exten => _+1NXXNXXXXXX/_011NX.,n,Goto(from-pstn-captcha3,${EXTEN:2},1)
exten => _+1NXXNXXXXXX,1,Goto(from-pstn-captcha3,${EXTEN:2},1)
; change CID and DID from 11 digit to 10 digit
exten => _1NXXNXXXXXX/_+1NXXNXXXXXX,1,Set(CALLERID(number)=${CALLERID(number):2})
exten => _1NXXNXXXXXX/_1NXXNXXXXXX,1,Set(CALLERID(number)=${CALLERID(number):1})
exten => _1NXXNXXXXXX/_NXXNXXXXXX,n,Goto(from-pstn-captcha3,${EXTEN:1},1)
exten => _1NXXNXXXXXX/_+NX.,1,Set(CALLERID(number)=011${CALLERID(number):1})
exten => _1NXXNXXXXXX/_011NX.,n,Goto(from-pstn-captcha3,${EXTEN:1},1)
exten => _1NXXNXXXXXX,1,Goto(from-pstn-captcha3,${EXTEN:1},1)
; DID already 10 digits, change CID to 10 digit
exten => _NXXNXXXXXX/_+1NXXNXXXXXX,1,Set(CALLERID(number)=${CALLERID(number):2})
exten => _NXXNXXXXXX/_1NXXNXXXXXX,1,Set(CALLERID(number)=${CALLERID(number):1})
exten => _NXXNXXXXXX/_NXXNXXXXXX,n,Goto(from-pstn-captcha3,${EXTEN},1)
exten => _NXXNXXXXXX/_+NX.,1,Set(CALLERID(number)=011${CALLERID(number):1})
exten => _NXXNXXXXXX/_011NX.,n,Goto(from-pstn-captcha3,${EXTEN},1)
exten => _NXXNXXXXXX,1,Goto(from-pstn-captcha3,${EXTEN},1)
; this will only get used for international or edge cases
exten => _[0-9+]./_+1NXXNXXXXXX,1,Set(CALLERID(number)=${CALLERID(number):2})
exten => _[0-9+]./_1NXXNXXXXXX,1,Set(CALLERID(number)=${CALLERID(number):1})
exten => _[0-9+]./_NXXNXXXXXX,n,Goto(from-pstn-captcha3,${EXTEN},1)
exten => _[0-9+]./_+NX.,1,Set(CALLERID(number)=011${CALLERID(number):1})
exten => _[0-9+]./_011NX.,n,Goto(from-pstn-captcha3,${EXTEN},1)
exten => _[0-9+].,1,Goto(from-pstn-captcha3,${EXTEN},1)
exten => s/_+1NXXNXXXXXX,1,Set(CALLERID(number)=${CALLERID(number):2})
exten => s/_NXXNXXXXXX,n,Goto(from-pstn-captcha3,${EXTEN},1)
exten => s/_+NX.,1,Set(CALLERID(number)=011${CALLERID(number):1})
exten => s/_011NX.,n,Goto(from-pstn-captcha3,${EXTEN},1)
exten => s,1,Goto(from-pstn-captcha3,${EXTEN},1)
[from-pstn-captcha3]
exten => _.,1,Noop(Entering user defined context from-pstn-captcha3 in extensions_custom.conf)
; check fpbx allowlist asdb and bypass captcha if CID present
exten => _.,n,GotoIf($["${DB_EXISTS(allowlist/${CALLERID(num)})}"!="0"]?allowed)
; check blacklist and exit immediately if CID present
exten => _.,n,GotoIf($["${BLACKLIST()}"="1"]?blocked)
; challenge caller with captcha
exten => _.,n,Answer
exten => _.,n,Set(counter=0)
exten => _.,n(restart),set(goal=${RAND(0,9)}${RAND(0,9)}) ; generates a 2 digit rnd number
exten => _.,n,Playback(custom/captcha_announce) ; edit to reference the recording you need
exten => _.,n,SayDigits(${goal})
exten => _.,n,Read(dtmf-in,,${LEN(${goal})},,,6) ; caller has 6 seconds to enter code
exten => _.,n,GotoIf($["${dtmf-in}"="${goal}"]?allowed) ; if input matches continue with fpbx dialplan
; give caller multiple chances
exten => _.,n,Set(counter=$[${counter}+1])
exten => _.,n,Gotoif($["${counter}"="3"]?blocked) ; bail after 3rd attempt adjust to suit
exten => _.,n,Wait(1) ; not sure a delay accomplishes anything here
exten => _.,n,goto(restart)
exten => _.,n(allowed),Noop(Inbound caller is whitelisted or passed the captcha challenge)
exten => _.,n,Goto(from-pstn,${EXTEN},1) ; go to fpbx inbound routes
exten => _.,n(blocked),Noop(Inbound caller is blacklisted or failed the captcha challenge)
exten => _.,n,Set(CDR(userfield,r)=AUTO BLOCKED) ; document block in the CDR
exten => _.,n,Hangup()
exten => h,1,Hangup()
exten => t,1,Noop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment