Support providers with multiple inbound signaling IPs (e.g. Anveo Direct)
VoIP.ms's one-POP-one-hostname model meant a single resolved IP was enough for the trunk's identify match=, but Anveo Direct (researched as an alternative after finding VoIP.ms's ToS selectively requires government ID at signup) sends inbound signaling from a fixed set of published IPs instead. Generalizes the pjsip include to accept multiple match= lines in one identify object (PJSIP allows repeating it to build one match set - no separate config objects needed per IP), with a new install prompt for any additional known source IPs beyond the auto-resolved one. No dial-prefix support was added despite earlier research suggesting Anveo Direct needed one - verified against their current official FAQ (fetched directly rather than trusting a stale search-result claim) that plain number dialing works with no prefix. Fixed a real bug caught while testing the update-mode round trip: .pstn-trunk.env was written with unquoted values, which breaks `source` entirely once any value contains a space - a multi-IP list or a multi-word provider name (e.g. "Anveo Direct") would make bash try to run the second word as a command. Predates this change (a multi-extension ring group has the same shape); only surfaced by actually re-sourcing the file and reapplying, not by inspecting generator output. Fixed by quoting every value. Updated docs/pstn-calling-voipms-plan.md with the Anveo Direct/DIDLogic/ SIP.US research findings and what's now implemented vs. still open (real-time balance enforcement at Anveo specifically is unconfirmed).
This commit is contained in:
@@ -224,9 +224,52 @@ separately from that hourly check.
|
|||||||
legitimately-registered extension being used for toll fraud — nobody
|
legitimately-registered extension being used for toll fraud — nobody
|
||||||
should assume CrowdSec alone already covers this.
|
should assume CrowdSec alone already covers this.
|
||||||
|
|
||||||
## Provider landscape (for reference — not chosen)
|
## Provider landscape (for reference)
|
||||||
- **SIP.US** — also prepaid, flat per-channel rate, built-in fraud
|
Looked at as alternatives to VoIP.ms specifically because VoIP.ms's own ToS
|
||||||
detection. Considered, not chosen.
|
confirms it *selectively* requires a government ID scan at signup (VPN/proxy
|
||||||
|
use, certain number types, internal risk scoring — not universal, but real).
|
||||||
|
None of these were verified to *never* require ID either; KYC requirements
|
||||||
|
are trending industry-wide (FCC STIR/SHAKEN, Robocall Mitigation Database),
|
||||||
|
so treat "no ID requirement found" as exactly that, not confirmation.
|
||||||
|
|
||||||
|
- **DIDLogic** — **ruled out**: has a $20/month minimum, confirmed directly
|
||||||
|
by checking their portal (not something search results surfaced). Would
|
||||||
|
otherwise have needed zero code changes — same IP-auth model and
|
||||||
|
single-IP-per-regional-POP hostname pattern this service already handles.
|
||||||
|
- **Anveo Direct** (anveodirect.com — a *different product* from plain
|
||||||
|
Anveo/anveo.com, its hosted-PBX sibling with monthly subscription tiers;
|
||||||
|
don't confuse the two, their pricing models aren't comparable) — leading
|
||||||
|
candidate. ~$0.001/min outbound, no minimum commitment, $25 minimum to
|
||||||
|
fund the account (close to VoIP.ms's $15). Confirmed via their current
|
||||||
|
official FAQ (fetched directly, not via search — search results had gone
|
||||||
|
stale on this exact point):
|
||||||
|
- IP authentication, no SIP registration — matches this service's
|
||||||
|
architecture with zero changes needed to the auth model.
|
||||||
|
- **No dial-prefix requirement** — a stale/outdated forum claim (found via
|
||||||
|
search) suggested one was needed; the *current* official FAQ's own
|
||||||
|
sample config shows plain number dialing, nothing prepended. Don't
|
||||||
|
trust that claim if it resurfaces.
|
||||||
|
- **Sends inbound signaling from multiple fixed IPs**, not one:
|
||||||
|
`169.48.232.158`, `204.216.109.55`, `176.9.39.206`, `72.9.149.25` (per
|
||||||
|
their FAQ as of this writing — confirmed different from what web search
|
||||||
|
surfaced independently, which was a different, apparently stale set of
|
||||||
|
5 IPs). This is what prompted the multi-IP `identify` support below.
|
||||||
|
- STIR/SHAKEN signing is handled by Anveo as intermediate provider for
|
||||||
|
personal use (originating your own calls, not reselling to customers) —
|
||||||
|
no FCC registration/certificate needed on our side.
|
||||||
|
- Still open: does Anveo actually block *new* outbound calls in real time
|
||||||
|
at $0 balance (matching VoIP.ms's documented behavior), or only enforce
|
||||||
|
via the recurring-fee grace-period mechanism their FAQ describes? Not
|
||||||
|
confirmed either way — ask their support before funding a real account,
|
||||||
|
since it's the crux of the toll-fraud backstop assumption.
|
||||||
|
- **SIP.US** — prepaid, but a flat ~$24.95/mo per-channel (unlimited minutes
|
||||||
|
on that channel) model, not pay-per-minute — **not actually
|
||||||
|
similarly-priced** at this project's volume (100 min/month costs ~$1-3/mo
|
||||||
|
pay-per-minute vs. $24.95/mo flat). Notable anyway for automated real-time
|
||||||
|
fraud detection (kills in-progress unauthorized calls, auto-disables
|
||||||
|
international calling on suspicious activity) — a provider-side version of
|
||||||
|
what this repo hand-built in the dialplan, if that trade-off (higher
|
||||||
|
price, less to maintain yourself) ever appeals.
|
||||||
- Several providers (Nextiva, IDT Express) advertise AI/ML-based fraud
|
- Several providers (Nextiva, IDT Express) advertise AI/ML-based fraud
|
||||||
monitoring as a second layer on top of normal billing — an extra net, not
|
monitoring as a second layer on top of normal billing — an extra net, not
|
||||||
a substitute for a hard prepaid ceiling.
|
a substitute for a hard prepaid ceiling.
|
||||||
@@ -234,6 +277,34 @@ separately from that hourly check.
|
|||||||
prepaid-balance + auto-recharge-off pattern is the de facto mechanism
|
prepaid-balance + auto-recharge-off pattern is the de facto mechanism
|
||||||
across the space, VoIP.ms included.
|
across the space, VoIP.ms included.
|
||||||
|
|
||||||
|
## Multi-provider support beyond VoIP.ms — implemented
|
||||||
|
`services/pstn-trunk.sh` now generalizes two things that were originally
|
||||||
|
built VoIP.ms-shaped, driven directly by researching Anveo Direct as an
|
||||||
|
alternative:
|
||||||
|
- **Multiple inbound `identify` match IPs**, not just one. The install
|
||||||
|
prompt still auto-resolves the server hostname to an IP (VoIP.ms's
|
||||||
|
one-POP-one-hostname model), but now also asks for any *additional* known
|
||||||
|
source IPs a provider might publish (Anveo Direct's case) — PJSIP allows
|
||||||
|
repeating `match=` within a single `identify` object to build one match
|
||||||
|
set against the trunk endpoint, so this needed no new config objects, just
|
||||||
|
a loop emitting one `match=` line per IP.
|
||||||
|
- No dial-prefix support was added — turned out not to be needed for Anveo
|
||||||
|
Direct once verified against their current official FAQ rather than a
|
||||||
|
stale search result. If a future provider genuinely needs one, that's the
|
||||||
|
next generalization point (a prefix prepended to `${EXTEN}` before
|
||||||
|
`Dial()`).
|
||||||
|
|
||||||
|
**Bug caught while testing this**: `.pstn-trunk.env` (the structural
|
||||||
|
settings file "update in place" reapplies) was being written with unquoted
|
||||||
|
values. Harmless as long as every value happened to be a single word, but a
|
||||||
|
multi-IP list (always space-separated once there's more than one) or a
|
||||||
|
multi-word provider name (e.g. "Anveo Direct") broke `source`-ing it
|
||||||
|
entirely — bash treats the second word as a command to run ("Direct:
|
||||||
|
command not found"). This was a latent bug that predates the multi-IP work
|
||||||
|
(a multi-extension ring group has the same shape), only surfaced by
|
||||||
|
actually exercising the update-mode round trip instead of just inspecting
|
||||||
|
generator output. Fixed by quoting every value in that heredoc.
|
||||||
|
|
||||||
## Open items for whoever picks this up next
|
## Open items for whoever picks this up next
|
||||||
1. ~~Decide: new `services/pstn-trunk.sh`...~~ Done — separate service file,
|
1. ~~Decide: new `services/pstn-trunk.sh`...~~ Done — separate service file,
|
||||||
generalized to any IP-auth SIP provider (VoIP.ms is just the default).
|
generalized to any IP-auth SIP provider (VoIP.ms is just the default).
|
||||||
|
|||||||
+62
-26
@@ -110,19 +110,24 @@ _pstn_patch_vendor_files() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ── Shared: pjsip trunk config (aor/identify/endpoint, IP-authenticated) ───
|
# ── Shared: pjsip trunk config (aor/identify/endpoint, IP-authenticated) ───
|
||||||
|
# SERVER_IPS is space-separated — one IP is the common case (one POP, one
|
||||||
|
# hostname resolution, e.g. VoIP.ms), but some providers (e.g. Anveo Direct)
|
||||||
|
# send inbound signaling from a fixed set of published IPs regardless of
|
||||||
|
# which hostname you dial out to. PJSIP's identify object allows repeating
|
||||||
|
# match= to build one match set against a single endpoint — no separate
|
||||||
|
# identify/endpoint objects needed per IP, unlike the older chan_sip
|
||||||
|
# peer-per-source-IP pattern some providers' sample configs still show.
|
||||||
_pstn_write_pjsip_include() {
|
_pstn_write_pjsip_include() {
|
||||||
local FILE="$1" SERVER="$2" SERVER_IP="$3" DID="$4"
|
local FILE="$1" SERVER="$2" SERVER_IPS="$3" DID="$4"
|
||||||
cat > "$FILE" << 'EOF'
|
cat > "$FILE" << 'EOF'
|
||||||
; SIP PSTN trunk — IP authentication, no password stored (see
|
; SIP PSTN trunk — IP authentication, no password stored (see
|
||||||
; docs/pstn-calling-voipms-plan.md). Regenerated by services/pstn-trunk.sh —
|
; docs/pstn-calling-voipms-plan.md). Regenerated by services/pstn-trunk.sh —
|
||||||
; edit there, not here directly, or a reinstall/update will overwrite this.
|
; edit there, not here directly, or a reinstall/update will overwrite this.
|
||||||
;
|
;
|
||||||
; match= below is the resolved IP of the server hostname at install time.
|
; match= lines below are the known/resolved source IP(s) for inbound calls.
|
||||||
; Providers sometimes send inbound INVITEs from a different IP than the one
|
; If inbound calls stop matching after a provider-side change, re-run this
|
||||||
; their hostname resolves to (load balancing / multiple servers per POP) —
|
; service to re-resolve/re-enter them, or add extra match= lines here by
|
||||||
; if inbound calls stop matching after a provider-side change, re-run this
|
; hand for additional known source IPs.
|
||||||
; service to re-resolve and rewrite it, or add extra "type=identify" /
|
|
||||||
; "match=" lines here by hand for additional known source IPs.
|
|
||||||
|
|
||||||
[pstn-trunk]
|
[pstn-trunk]
|
||||||
type=aor
|
type=aor
|
||||||
@@ -132,7 +137,12 @@ qualify_frequency=60
|
|||||||
[pstn-trunk]
|
[pstn-trunk]
|
||||||
type=identify
|
type=identify
|
||||||
endpoint=pstn-trunk
|
endpoint=pstn-trunk
|
||||||
match=__PSTN_SERVER_IP__
|
EOF
|
||||||
|
local _ip
|
||||||
|
for _ip in $SERVER_IPS; do
|
||||||
|
echo "match=${_ip}" >> "$FILE"
|
||||||
|
done
|
||||||
|
cat >> "$FILE" << 'EOF'
|
||||||
|
|
||||||
[pstn-trunk]
|
[pstn-trunk]
|
||||||
type=endpoint
|
type=endpoint
|
||||||
@@ -145,7 +155,7 @@ from_domain=__PSTN_SERVER__
|
|||||||
callerid=__PSTN_DID__
|
callerid=__PSTN_DID__
|
||||||
direct_media=no
|
direct_media=no
|
||||||
EOF
|
EOF
|
||||||
sed -i "s/__PSTN_SERVER_IP__/${SERVER_IP}/g; s/__PSTN_SERVER__/${SERVER}/g; s/__PSTN_DID__/${DID}/g" "$FILE"
|
sed -i "s/__PSTN_SERVER__/${SERVER}/g; s/__PSTN_DID__/${DID}/g" "$FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Shared: one inbound ring-group member's live permission check ─────────
|
# ── Shared: one inbound ring-group member's live permission check ─────────
|
||||||
@@ -422,29 +432,35 @@ EOF
|
|||||||
# for why that file is managed separately.
|
# for why that file is managed separately.
|
||||||
_pstn_apply_settings() {
|
_pstn_apply_settings() {
|
||||||
local EA_DIR="$1" ASTERISK_DIR="$2"
|
local EA_DIR="$1" ASTERISK_DIR="$2"
|
||||||
local SERVER="$3" SERVER_IP="$4" DID="$5"
|
local SERVER="$3" SERVER_IPS="$4" DID="$5"
|
||||||
local RING_EXTS="$6" NTFY_URL="$7" RATE="$8" MONTH_THRESHOLD="$9" BURST_THRESHOLD="${10}"
|
local RING_EXTS="$6" NTFY_URL="$7" RATE="$8" MONTH_THRESHOLD="$9" BURST_THRESHOLD="${10}"
|
||||||
local PROVIDER_NAME="${11}"
|
local PROVIDER_NAME="${11}"
|
||||||
|
|
||||||
_pstn_patch_vendor_files "$EA_DIR" || return 1
|
_pstn_patch_vendor_files "$EA_DIR" || return 1
|
||||||
|
|
||||||
mkdir -p "$ASTERISK_DIR"
|
mkdir -p "$ASTERISK_DIR"
|
||||||
_pstn_write_pjsip_include "$ASTERISK_DIR/pstn-trunk-pjsip.conf" "$SERVER" "$SERVER_IP" "$DID"
|
_pstn_write_pjsip_include "$ASTERISK_DIR/pstn-trunk-pjsip.conf" "$SERVER" "$SERVER_IPS" "$DID"
|
||||||
_pstn_write_dialplan_include "$ASTERISK_DIR/pstn-trunk-dialplan.conf" "$DID" "$RING_EXTS" "$NTFY_URL"
|
_pstn_write_dialplan_include "$ASTERISK_DIR/pstn-trunk-dialplan.conf" "$DID" "$RING_EXTS" "$NTFY_URL"
|
||||||
_pstn_write_usage_alert_script "$EA_DIR/pstn-trunk-usage-alert.sh" "$EA_DIR" "$RATE" "$MONTH_THRESHOLD" "$BURST_THRESHOLD" "$NTFY_URL"
|
_pstn_write_usage_alert_script "$EA_DIR/pstn-trunk-usage-alert.sh" "$EA_DIR" "$RATE" "$MONTH_THRESHOLD" "$BURST_THRESHOLD" "$NTFY_URL"
|
||||||
ensure_docker_dir_ownership "$ASTERISK_DIR"
|
ensure_docker_dir_ownership "$ASTERISK_DIR"
|
||||||
chmod 644 "$ASTERISK_DIR/pstn-trunk-pjsip.conf" "$ASTERISK_DIR/pstn-trunk-dialplan.conf"
|
chmod 644 "$ASTERISK_DIR/pstn-trunk-pjsip.conf" "$ASTERISK_DIR/pstn-trunk-dialplan.conf"
|
||||||
|
|
||||||
|
# Values are double-quoted: this file is `source`d back in on "update"
|
||||||
|
# (and RING_EXTS/TRUNK_SERVER_IPS are space-separated whenever there's
|
||||||
|
# more than one entry, and PROVIDER_NAME can be multi-word, e.g. "Anveo
|
||||||
|
# Direct") — unquoted, bash's `source` would treat the second word of
|
||||||
|
# any such value as a command to run ("Direct: command not found"),
|
||||||
|
# confirmed live while testing the multi-IP change.
|
||||||
cat > "$EA_DIR/.pstn-trunk.env" << ENV
|
cat > "$EA_DIR/.pstn-trunk.env" << ENV
|
||||||
PROVIDER_NAME=${PROVIDER_NAME}
|
PROVIDER_NAME="${PROVIDER_NAME}"
|
||||||
TRUNK_SERVER=${SERVER}
|
TRUNK_SERVER="${SERVER}"
|
||||||
TRUNK_SERVER_IP=${SERVER_IP}
|
TRUNK_SERVER_IPS="${SERVER_IPS}"
|
||||||
TRUNK_DID=${DID}
|
TRUNK_DID="${DID}"
|
||||||
RING_EXTS=${RING_EXTS}
|
RING_EXTS="${RING_EXTS}"
|
||||||
NTFY_URL=${NTFY_URL}
|
NTFY_URL="${NTFY_URL}"
|
||||||
RATE_PER_MIN=${RATE}
|
RATE_PER_MIN="${RATE}"
|
||||||
MONTH_THRESHOLD=${MONTH_THRESHOLD}
|
MONTH_THRESHOLD="${MONTH_THRESHOLD}"
|
||||||
BURST_THRESHOLD=${BURST_THRESHOLD}
|
BURST_THRESHOLD="${BURST_THRESHOLD}"
|
||||||
ENV
|
ENV
|
||||||
chown "$ACTUAL_USER:$ACTUAL_USER" "$EA_DIR/.pstn-trunk.env" 2>/dev/null || true
|
chown "$ACTUAL_USER:$ACTUAL_USER" "$EA_DIR/.pstn-trunk.env" 2>/dev/null || true
|
||||||
|
|
||||||
@@ -485,7 +501,8 @@ install_pstn-trunk() {
|
|||||||
echo "[DRY-RUN] full-PSTN extensions, restricted-PSTN extensions + their approved numbers,"
|
echo "[DRY-RUN] full-PSTN extensions, restricted-PSTN extensions + their approved numbers,"
|
||||||
echo "[DRY-RUN] max concurrent outbound/inbound calls (default 10/10), inbound ring-group extensions,"
|
echo "[DRY-RUN] max concurrent outbound/inbound calls (default 10/10), inbound ring-group extensions,"
|
||||||
echo "[DRY-RUN] ntfy alert topic (optional), per-minute rate + monthly/hourly alert thresholds"
|
echo "[DRY-RUN] ntfy alert topic (optional), per-minute rate + monthly/hourly alert thresholds"
|
||||||
echo "[DRY-RUN] Would resolve the server hostname to an IP for inbound call matching"
|
echo "[DRY-RUN] Would resolve the server hostname to an IP, plus prompt for any additional"
|
||||||
|
echo "[DRY-RUN] known source IPs (some providers publish a fixed list), for inbound call matching"
|
||||||
echo "[DRY-RUN] Would patch vendor generator functions to #include the trunk config"
|
echo "[DRY-RUN] Would patch vendor generator functions to #include the trunk config"
|
||||||
echo "[DRY-RUN] Would write pjsip/dialplan includes, pstn-permissions.conf + pstn-limits.conf"
|
echo "[DRY-RUN] Would write pjsip/dialplan includes, pstn-permissions.conf + pstn-limits.conf"
|
||||||
echo "[DRY-RUN] (fresh install only), and an hourly usage-alert script + cron.d entry"
|
echo "[DRY-RUN] (fresh install only), and an hourly usage-alert script + cron.d entry"
|
||||||
@@ -537,7 +554,7 @@ install_pstn-trunk() {
|
|||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "$SETTINGS_FILE"
|
source "$SETTINGS_FILE"
|
||||||
_pstn_apply_settings "$EA_DIR" "$ASTERISK_DIR" \
|
_pstn_apply_settings "$EA_DIR" "$ASTERISK_DIR" \
|
||||||
"$TRUNK_SERVER" "$TRUNK_SERVER_IP" "$TRUNK_DID" \
|
"$TRUNK_SERVER" "$TRUNK_SERVER_IPS" "$TRUNK_DID" \
|
||||||
"$RING_EXTS" "$NTFY_URL" "$RATE_PER_MIN" \
|
"$RING_EXTS" "$NTFY_URL" "$RATE_PER_MIN" \
|
||||||
"$MONTH_THRESHOLD" "$BURST_THRESHOLD" "$PROVIDER_NAME" || return 1
|
"$MONTH_THRESHOLD" "$BURST_THRESHOLD" "$PROVIDER_NAME" || return 1
|
||||||
( cd "$EA_DIR" && docker compose restart asterisk ) \
|
( cd "$EA_DIR" && docker compose restart asterisk ) \
|
||||||
@@ -586,7 +603,7 @@ install_pstn-trunk() {
|
|||||||
local TRUNK_SERVER_IP=""
|
local TRUNK_SERVER_IP=""
|
||||||
TRUNK_SERVER_IP="$(getent ahostsv4 "$TRUNK_SERVER" 2>/dev/null | awk '{print $1}' | head -1)"
|
TRUNK_SERVER_IP="$(getent ahostsv4 "$TRUNK_SERVER" 2>/dev/null | awk '{print $1}' | head -1)"
|
||||||
if [[ -z "$TRUNK_SERVER_IP" ]]; then
|
if [[ -z "$TRUNK_SERVER_IP" ]]; then
|
||||||
log_warning "Couldn't resolve $TRUNK_SERVER — the identify section needs an IP to match inbound calls against."
|
log_warning "Couldn't resolve $TRUNK_SERVER — the identify section needs at least one IP to match inbound calls against."
|
||||||
prompt_text "Enter its IP manually (check your provider's server list page):" "" TRUNK_SERVER_IP
|
prompt_text "Enter its IP manually (check your provider's server list page):" "" TRUNK_SERVER_IP
|
||||||
if [[ -z "$TRUNK_SERVER_IP" ]]; then
|
if [[ -z "$TRUNK_SERVER_IP" ]]; then
|
||||||
log_error "No IP available — aborting."
|
log_error "No IP available — aborting."
|
||||||
@@ -596,6 +613,24 @@ install_pstn-trunk() {
|
|||||||
log_success "Resolved $TRUNK_SERVER -> $TRUNK_SERVER_IP"
|
log_success "Resolved $TRUNK_SERVER -> $TRUNK_SERVER_IP"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Some providers send inbound signaling from a fixed set of published IPs
|
||||||
|
# that don't necessarily match what the server hostname resolves to (e.g.
|
||||||
|
# Anveo Direct publishes 4 signaling IPs regardless of which hostname you
|
||||||
|
# dial out to) — the resolved IP above always gets included, this just
|
||||||
|
# adds any others the provider documents.
|
||||||
|
local EXTRA_IPS=""
|
||||||
|
prompt_text "Any additional known source IPs for inbound calls, space-separated (check your provider's docs — e.g. a firewall/signaling IP list; blank if the resolved IP above is the only one):" "" EXTRA_IPS
|
||||||
|
local _octet='(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
|
||||||
|
local _ip_re="^${_octet}\\.${_octet}\\.${_octet}\\.${_octet}\$"
|
||||||
|
local TRUNK_SERVER_IPS="$TRUNK_SERVER_IP" _ip
|
||||||
|
for _ip in $EXTRA_IPS; do
|
||||||
|
if [[ "$_ip" =~ $_ip_re ]]; then
|
||||||
|
TRUNK_SERVER_IPS="${TRUNK_SERVER_IPS} ${_ip}"
|
||||||
|
else
|
||||||
|
log_warning "Skipping '$_ip' — doesn't look like an IPv4 address."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
local TRUNK_DID=""
|
local TRUNK_DID=""
|
||||||
prompt_text "DID (the 10-digit US phone number assigned to this trunk, digits only):" "" TRUNK_DID
|
prompt_text "DID (the 10-digit US phone number assigned to this trunk, digits only):" "" TRUNK_DID
|
||||||
if [[ ! "$TRUNK_DID" =~ ^[0-9]{10}$ ]]; then
|
if [[ ! "$TRUNK_DID" =~ ^[0-9]{10}$ ]]; then
|
||||||
@@ -694,7 +729,7 @@ install_pstn-trunk() {
|
|||||||
prompt_text " Alert if more than this many outbound calls happen in one hour:" "10" BURST_THRESHOLD
|
prompt_text " Alert if more than this many outbound calls happen in one hour:" "10" BURST_THRESHOLD
|
||||||
|
|
||||||
_pstn_apply_settings "$EA_DIR" "$ASTERISK_DIR" \
|
_pstn_apply_settings "$EA_DIR" "$ASTERISK_DIR" \
|
||||||
"$TRUNK_SERVER" "$TRUNK_SERVER_IP" "$TRUNK_DID" \
|
"$TRUNK_SERVER" "$TRUNK_SERVER_IPS" "$TRUNK_DID" \
|
||||||
"$RING_EXTS" "$NTFY_URL" "$RATE_PER_MIN" \
|
"$RING_EXTS" "$NTFY_URL" "$RATE_PER_MIN" \
|
||||||
"$MONTH_THRESHOLD" "$BURST_THRESHOLD" "$PROVIDER_NAME" || return 1
|
"$MONTH_THRESHOLD" "$BURST_THRESHOLD" "$PROVIDER_NAME" || return 1
|
||||||
|
|
||||||
@@ -724,7 +759,7 @@ background, cost estimate, and toll-fraud reasoning.
|
|||||||
| Setting | Value |
|
| Setting | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Provider | ${PROVIDER_NAME} |
|
| Provider | ${PROVIDER_NAME} |
|
||||||
| Server/POP | ${TRUNK_SERVER} (${TRUNK_SERVER_IP}) |
|
| Server/POP | ${TRUNK_SERVER} (inbound match IPs: ${TRUNK_SERVER_IPS}) |
|
||||||
| DID | ${TRUNK_DID} |
|
| DID | ${TRUNK_DID} |
|
||||||
| Outbound scope | US/NANP only — \`_1NXXNXXXXX\` / \`_NXXNXXXXX\` patterns, no catch-all |
|
| Outbound scope | US/NANP only — \`_1NXXNXXXXX\` / \`_NXXNXXXXX\` patterns, no catch-all |
|
||||||
| Full-PSTN extensions | ${FULL_EXTS:-none} |
|
| Full-PSTN extensions | ${FULL_EXTS:-none} |
|
||||||
@@ -876,7 +911,8 @@ MD
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
log_success "PSTN trunk configured."
|
log_success "PSTN trunk configured."
|
||||||
echo " Provider: $PROVIDER_NAME ($TRUNK_SERVER / $TRUNK_SERVER_IP)"
|
echo " Provider: $PROVIDER_NAME ($TRUNK_SERVER)"
|
||||||
|
echo " Inbound match IPs: $TRUNK_SERVER_IPS"
|
||||||
echo " DID: $TRUNK_DID"
|
echo " DID: $TRUNK_DID"
|
||||||
echo " Outbound: US/NANP only, max $MAX_OUTBOUND concurrent calls"
|
echo " Outbound: US/NANP only, max $MAX_OUTBOUND concurrent calls"
|
||||||
echo " Inbound: max $MAX_INBOUND concurrent calls"
|
echo " Inbound: max $MAX_INBOUND concurrent calls"
|
||||||
|
|||||||
Reference in New Issue
Block a user