calls: limit outside calls at once, per extension and in total
Owner's decision after the M11-T8 review: nothing capped how many calls a stolen password could run at once. Settings → Security: calls.maxPerExtension (3) and calls.maxTotal (10), 0 = no limit. Each sp-route-* counts the caller's extension (or whose forward it is: SP_LIMITEXT from sp-sub-forward) with GROUP(spext) and every outside call with GROUP(spout); over a limit the caller hears "all circuits are busy". 911 is never limited. trunk-test.ts on a real Asterisk, limits 1 and 2: a 2nd call from 101's phone and a 3rd in total are refused (with both 0 the same check fails). call-limits.test.ts covers the 0 combinations. Also fixes the Security tab's hint: browser phones still work from anywhere. Task: M11-T8
This commit is contained in:
1 parent
e4e50f1c0d
commit
f6a9b9a4b1
25 files changed
+203
-9
No files matched your search
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
+1
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
@@ -267,6 +268,12 @@ exten => _1[2-9]XXNXXXXXX,1,Goto(sp-route-bbbbbbbb-0000-4000-8000-000000000001,$
|
||||
exten => _X.,1,Set(__SP_DIRECTION=outbound)
|
||||
same => n,Set(CDR(SP_DIRECTION)=outbound)
|
||||
same => n,GotoIf($[${IF($["${SP_CLASS}" = ""]?0:${SP_CLASS})} < 2]?denied)
|
||||
same => n,Set(SP_LIMITEXT=${IF($["${SP_LIMITEXT}" = ""]?${SP_EXT}:${SP_LIMITEXT})})
|
||||
same => n,GotoIf($["${SP_LIMITEXT}" = ""]?total)
|
||||
same => n,Set(GROUP(spext)=${SP_LIMITEXT})
|
||||
same => n,GotoIf($[${GROUP_COUNT(${SP_LIMITEXT}@spext)} > 3]?busy)
|
||||
same => n(total),Set(GROUP(spout)=all)
|
||||
same => n,GotoIf($[${GROUP_COUNT(all@spout)} > 10]?busy)
|
||||
same => n,ExecIf($["${SP_RECMODE}" = "outbound" | "${SP_RECMODE}" = "all"]?Gosub(sp-sub-record,s,1))
|
||||
; caller ID: route override > user > the DID a forwarded call came in on > default
|
||||
same => n,Set(CID=)
|
||||
@@ -286,6 +293,12 @@ exten => _9NXXNXXXXXX,1,Goto(sp-route-bbbbbbbb-0000-4000-8000-000000000002,1${FI
|
||||
exten => _X.,1,Set(__SP_DIRECTION=outbound)
|
||||
same => n,Set(CDR(SP_DIRECTION)=outbound)
|
||||
same => n,GotoIf($[${IF($["${SP_CLASS}" = ""]?0:${SP_CLASS})} < 1]?denied)
|
||||
same => n,Set(SP_LIMITEXT=${IF($["${SP_LIMITEXT}" = ""]?${SP_EXT}:${SP_LIMITEXT})})
|
||||
same => n,GotoIf($["${SP_LIMITEXT}" = ""]?total)
|
||||
same => n,Set(GROUP(spext)=${SP_LIMITEXT})
|
||||
same => n,GotoIf($[${GROUP_COUNT(${SP_LIMITEXT}@spext)} > 3]?busy)
|
||||
same => n(total),Set(GROUP(spout)=all)
|
||||
same => n,GotoIf($[${GROUP_COUNT(all@spout)} > 10]?busy)
|
||||
same => n,ExecIf($["${SP_RECMODE}" = "outbound" | "${SP_RECMODE}" = "all"]?Gosub(sp-sub-record,s,1))
|
||||
; caller ID: route override > user > the DID a forwarded call came in on > default
|
||||
same => n,Set(CID=15551230000)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
@@ -256,6 +257,12 @@ exten => _1833NXXXXXX,1,Goto(sp-route-aaaaaaaa-0000-4000-8000-000000000002,${FIL
|
||||
exten => _X.,1,Set(__SP_DIRECTION=outbound)
|
||||
same => n,Set(CDR(SP_DIRECTION)=outbound)
|
||||
same => n,GotoIf($[${IF($["${SP_CLASS}" = ""]?0:${SP_CLASS})} < 1]?denied)
|
||||
same => n,Set(SP_LIMITEXT=${IF($["${SP_LIMITEXT}" = ""]?${SP_EXT}:${SP_LIMITEXT})})
|
||||
same => n,GotoIf($["${SP_LIMITEXT}" = ""]?total)
|
||||
same => n,Set(GROUP(spext)=${SP_LIMITEXT})
|
||||
same => n,GotoIf($[${GROUP_COUNT(${SP_LIMITEXT}@spext)} > 3]?busy)
|
||||
same => n(total),Set(GROUP(spout)=all)
|
||||
same => n,GotoIf($[${GROUP_COUNT(all@spout)} > 10]?busy)
|
||||
same => n,ExecIf($["${SP_RECMODE}" = "outbound" | "${SP_RECMODE}" = "all"]?Gosub(sp-sub-record,s,1))
|
||||
; caller ID: route override > user > the DID a forwarded call came in on > default
|
||||
same => n,Set(CID=)
|
||||
@@ -280,6 +287,12 @@ exten => _1NXXNXXXXXX,1,Goto(sp-route-aaaaaaaa-0000-4000-8000-000000000001,${FIL
|
||||
exten => _X.,1,Set(__SP_DIRECTION=outbound)
|
||||
same => n,Set(CDR(SP_DIRECTION)=outbound)
|
||||
same => n,GotoIf($[${IF($["${SP_CLASS}" = ""]?0:${SP_CLASS})} < 2]?denied)
|
||||
same => n,Set(SP_LIMITEXT=${IF($["${SP_LIMITEXT}" = ""]?${SP_EXT}:${SP_LIMITEXT})})
|
||||
same => n,GotoIf($["${SP_LIMITEXT}" = ""]?total)
|
||||
same => n,Set(GROUP(spext)=${SP_LIMITEXT})
|
||||
same => n,GotoIf($[${GROUP_COUNT(${SP_LIMITEXT}@spext)} > 3]?busy)
|
||||
same => n(total),Set(GROUP(spout)=all)
|
||||
same => n,GotoIf($[${GROUP_COUNT(all@spout)} > 10]?busy)
|
||||
same => n,ExecIf($["${SP_RECMODE}" = "outbound" | "${SP_RECMODE}" = "all"]?Gosub(sp-sub-record,s,1))
|
||||
; caller ID: route override > user > the DID a forwarded call came in on > default
|
||||
same => n,Set(CID=)
|
||||
|
||||
@@ -54,6 +54,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[${IF($["${SP_FWDHOPS}" = ""]?0:${SP_FWDHOPS})} +
|
||||
same => n,GotoIf($[${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[${DIALPLAN_EXISTS(sp-local-numbers,${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=${GLOBAL(SP_CLASS_${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,${ARG1},1)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Outside calls at once (08 §1): the lines in each sp-route-* for calls.maxPerExtension and
|
||||
// calls.maxTotal, 0 = no limit. trunk-test.ts checks them on a real Asterisk.
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { loadCase } from './golden-case.ts';
|
||||
import { render } from './index.ts';
|
||||
|
||||
const route = (perExt: number, total: number) => {
|
||||
const snap = loadCase('voipms-basic');
|
||||
snap.settings['calls.maxPerExtension'] = perExt;
|
||||
snap.settings['calls.maxTotal'] = total;
|
||||
const conf = render(snap)['extensions_selfphoneng.conf'];
|
||||
const start = conf.indexOf('[sp-route-');
|
||||
return conf.slice(start, conf.indexOf('\n[', start + 1));
|
||||
};
|
||||
|
||||
describe('call limits', () => {
|
||||
it('both: per extension, then the total', () => {
|
||||
const r = route(3, 10);
|
||||
expect(r).toContain('GotoIf($[${GROUP_COUNT(${SP_LIMITEXT}@spext)} > 3]?busy)');
|
||||
expect(r).toContain('GotoIf($["${SP_LIMITEXT}" = ""]?total)');
|
||||
expect(r).toContain(' same => n(total),Set(GROUP(spout)=all)');
|
||||
expect(r).toContain('GotoIf($[${GROUP_COUNT(all@spout)} > 10]?busy)');
|
||||
expect(r.indexOf('@spext')).toBeLessThan(r.indexOf('Dial('));
|
||||
});
|
||||
|
||||
it('per extension only: a call with no extension skips to the end of the check', () => {
|
||||
const r = route(2, 0);
|
||||
expect(r).toContain('GotoIf($["${SP_LIMITEXT}" = ""]?counted)');
|
||||
expect(r).toContain(' same => n(counted),NoOp()');
|
||||
expect(r).not.toContain('spout');
|
||||
});
|
||||
|
||||
it('total only, and neither', () => {
|
||||
expect(route(0, 5)).not.toContain('spext');
|
||||
expect(route(0, 5)).toContain(' same => n,Set(GROUP(spout)=all)');
|
||||
expect(route(0, 0)).not.toMatch(/spext|spout|SP_LIMITEXT/);
|
||||
});
|
||||
|
||||
it("a forward counts toward the forwarding extension's limit", () => {
|
||||
expect(render(loadCase('voipms-basic'))['extensions_selfphoneng.conf']).toContain(
|
||||
' same => n,Set(SP_LIMITEXT=${ARG2})',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,27 @@ export function renderOutbound(snap: Snapshot): string {
|
||||
.filter((r) => r.enabled && r.patterns.length)
|
||||
.sort((a, b) => a.position - b.position || a.id.localeCompare(b.id));
|
||||
|
||||
const perExt = snap.settings['calls.maxPerExtension'];
|
||||
const total = snap.settings['calls.maxTotal'];
|
||||
// Outside calls at once (08 §1): per extension (the caller, or whose forward this is) and in
|
||||
// total. A call with no extension (an admin's external destination) only counts in the total.
|
||||
const limits = [
|
||||
...(perExt
|
||||
? [
|
||||
' same => n,Set(SP_LIMITEXT=${IF($["${SP_LIMITEXT}" = ""]?${SP_EXT}:${SP_LIMITEXT})})',
|
||||
` same => n,GotoIf($["\${SP_LIMITEXT}" = ""]?${total ? 'total' : 'counted'})`,
|
||||
' same => n,Set(GROUP(spext)=${SP_LIMITEXT})',
|
||||
` same => n,GotoIf($[\${GROUP_COUNT(\${SP_LIMITEXT}@spext)} > ${s.digits(String(perExt))}]?busy)`,
|
||||
]
|
||||
: []),
|
||||
...(total
|
||||
? [
|
||||
` same => n${perExt ? '(total)' : ''},Set(GROUP(spout)=all)`,
|
||||
` same => n,GotoIf($[\${GROUP_COUNT(all@spout)} > ${s.digits(String(total))}]?busy)`,
|
||||
]
|
||||
: []),
|
||||
...(perExt && !total ? [' same => n(counted),NoOp()'] : []),
|
||||
];
|
||||
const top = ['[sp-outbound-routes]'];
|
||||
if (!routes.length) top.push('exten => pending,1,Hangup() ; no routes yet');
|
||||
const out: string[] = [];
|
||||
@@ -39,6 +60,7 @@ export function renderOutbound(snap: Snapshot): string {
|
||||
'exten => _X.,1,Set(__SP_DIRECTION=outbound)',
|
||||
' same => n,Set(CDR(SP_DIRECTION)=outbound)',
|
||||
` same => n,GotoIf($[\${IF($["\${SP_CLASS}" = ""]?0:\${SP_CLASS})} < ${CLASS_RANK[r.requiredClass]}]?denied)`,
|
||||
...limits,
|
||||
' same => n,ExecIf($["${SP_RECMODE}" = "outbound" | "${SP_RECMODE}" = "all"]?Gosub(sp-sub-record,s,1))',
|
||||
' ; caller ID: route override > user > the DID a forwarded call came in on > default',
|
||||
` same => n,Set(CID=${r.callerId ? s.digits(r.callerId) : ''})`,
|
||||
|
||||
@@ -57,6 +57,7 @@ exten => s,1,Set(__SP_FWDHOPS=$[\${IF($["\${SP_FWDHOPS}" = ""]?0:\${SP_FWDHOPS})
|
||||
same => n,GotoIf($[\${SP_FWDHOPS} > 3]?stop)
|
||||
same => n,GotoIf($[\${DIALPLAN_EXISTS(sp-local-numbers,\${ARG1},1)}]?internal)
|
||||
same => n,Set(SP_CLASS=\${GLOBAL(SP_CLASS_\${ARG2})})
|
||||
same => n,Set(SP_LIMITEXT=\${ARG2}) ; counts toward that extension's call limit
|
||||
same => n,Set(__SP_FORWARDED=1)
|
||||
same => n,Goto(sp-outbound-routes,\${ARG1},1)
|
||||
same => n(internal),Goto(sp-local-numbers,\${ARG1},1)
|
||||
|
||||
@@ -52,6 +52,8 @@ function snapshot(): Snapshot {
|
||||
.map((k) => [k, SettingsSchemas[k].parse(undefined)]),
|
||||
) as Snapshot['settings'];
|
||||
settings['routing.defaultCallerIdDidId'] = DID_ID;
|
||||
settings['calls.maxPerExtension'] = 1;
|
||||
settings['calls.maxTotal'] = 2;
|
||||
const trunk = (id: string, name: string, password: string) => ({
|
||||
id,
|
||||
name,
|
||||
@@ -398,6 +400,37 @@ async function main() {
|
||||
log('class check: ss-noservice');
|
||||
await ami.command('channel request hangup all');
|
||||
|
||||
// Outside calls at once (08 §1), 1 per extension and 2 in total here. Local channels carry
|
||||
// __SP_LIMITEXT like a forward of that extension's; the second try is 101's own phone.
|
||||
dials.length = 0;
|
||||
const out = (num: string, ext: string) =>
|
||||
ami.action({
|
||||
Action: 'Originate',
|
||||
Channel: `Local/${num}@sp-from-ext/n`, // /n: no optimizing away (and its GROUP with it)
|
||||
Application: 'Wait',
|
||||
Data: '30',
|
||||
Variable: `__SP_CLASS=2,__SP_LIMITEXT=${ext}`,
|
||||
Async: 'true',
|
||||
});
|
||||
const dialed = (num: string) =>
|
||||
dials.some(
|
||||
(m) =>
|
||||
String(m.DestChannel).startsWith('PJSIP/trunk-') && String(m.DialString).startsWith(num),
|
||||
);
|
||||
await out('13125550001', '101');
|
||||
await waitFor("101's first outside call", async () => dialed('13125550001'), 15);
|
||||
await ami.command('channel originate PJSIP/101 extension 13125550002@sp-from-ext');
|
||||
await out('13125550003', '102');
|
||||
await waitFor("102's call (2 in total)", async () => dialed('13125550003'), 15);
|
||||
await out('13125550004', '103');
|
||||
await sleep(5000);
|
||||
if (dialed('13125550002')) problems.push('101 had 2 outside calls at once, limit 1');
|
||||
if (dialed('13125550004')) problems.push('3 outside calls at once, total limit 2');
|
||||
if (!dialed('13125550002') && !dialed('13125550004'))
|
||||
log('call limits: a 2nd call for 101 and a 3rd in total were refused');
|
||||
await ami.command('channel request hangup all');
|
||||
await sleep(2000);
|
||||
|
||||
// M11-T8: SIP allows "," in a dialed number. Unfiltered, Goto(sp-route-…,${EXTEN},1) became
|
||||
// Goto(sp-route-…,011…,4,1) and started past the class check: international for anyone.
|
||||
await sleep(1000);
|
||||
|
||||
Reference in new issue
Block a user