diff --git a/app.js b/app.js index 18f6e94..387250f 100644 --- a/app.js +++ b/app.js @@ -97,6 +97,7 @@ srf.locals = {...srf.locals, lookupCarrierByAccountLcr }, realtimeDbHelpers: { + client: redisClient, createHash, retrieveHash, incrKey, diff --git a/lib/call-session.js b/lib/call-session.js index 7d626d5..b4c895d 100644 --- a/lib/call-session.js +++ b/lib/call-session.js @@ -1,7 +1,7 @@ const Emitter = require('events'); const sdpTransform = require('sdp-transform'); const SrsClient = require('@jambonz/siprec-client-utils'); -const {makeRtpEngineOpts, nudgeCallCounts, isPrivateVoipNetwork} = require('./utils'); +const {makeRtpEngineOpts, nudgeCallCounts, isPrivateVoipNetwork, isBlackListedSipGateway} = require('./utils'); const {forwardInDialogRequests} = require('drachtio-fn-b2b-sugar'); const {SipError, stringifyUri, parseUri} = require('drachtio-srf'); const debug = require('debug')('jambonz:sbc-outbound'); @@ -168,7 +168,7 @@ class CallSession extends Emitter { subscribeAnswer, unsubscribe } = engine; - const {createHash, retrieveHash} = this.srf.locals.realtimeDbHelpers; + const {client, createHash, retrieveHash} = this.srf.locals.realtimeDbHelpers; this.offer = offer; this.answer = answer; this.del = del; @@ -186,7 +186,7 @@ class CallSession extends Emitter { this.rtpEngineOpts = makeRtpEngineOpts(this.req, false, this.useWss || teams, false, teams); this.rtpEngineResource = {destroy: this.del.bind(null, this.rtpEngineOpts.common)}; - let proxy, uris; + let proxy, uris = []; const mapGateways = new Map(); try { @@ -280,7 +280,13 @@ class CallSession extends Emitter { } const vc = await this.lookupCarrierBySid(voip_carrier_sid); const gateways = await this.lookupSipGatewaysByCarrier(voip_carrier_sid); - const gws = (gateways || []) + const goodGateways = []; + for (const g of gateways) { + if (!await isBlackListedSipGateway(client, this.logger, g.sip_gateway_sid)) { + goodGateways.push(g); + } + } + const gws = (goodGateways || []) .filter((gw) => gw.outbound); if (gws.length) { uris = []; @@ -332,6 +338,7 @@ class CallSession extends Emitter { } else { this.logger.info({voip_carrier_sid}, 'no outbound gateways found for requested carrier'); + this.res.send(603); } } catch (err) { debug(err); diff --git a/lib/utils.js b/lib/utils.js index 7a222db..c2f62f3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -213,6 +213,18 @@ const isPrivateVoipNetwork = async(uri) => { return false; }; +function makeBlacklistGatewayKey(key) { + return `blacklist-sip-gateway:${key}`; +} + +async function isBlackListedSipGateway(client, logger, sip_gateway_sid) { + try { + return await client.exists(makeBlacklistGatewayKey(sip_gateway_sid)) === 1; + } catch (err) { + logger.error({err}, `isBlackListedSipGateway: error while checking blacklist for ${sip_gateway_sid}`); + } +} + module.exports = { makeRtpEngineOpts, selectHostPort, @@ -223,5 +235,6 @@ module.exports = { systemHealth, createHealthCheckApp, nudgeCallCounts, - isPrivateVoipNetwork + isPrivateVoipNetwork, + isBlackListedSipGateway }; diff --git a/test/scenarios/uac-pcap-carrier-fail-blacklist.xml b/test/scenarios/uac-pcap-carrier-fail-blacklist.xml new file mode 100644 index 0000000..7ef88b8 --- /dev/null +++ b/test/scenarios/uac-pcap-carrier-fail-blacklist.xml @@ -0,0 +1,59 @@ + + + + + + + ;tag=[pid]SIPpTag09[call_number] + To: + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803 + X-Call-Sid: ff49e33-e771-403a-8c99-1780eabbc803 + X-Jambonz-Routing: phone + Subject: uac-pcap-carrier-fail-limits + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] + s=- + c=IN IP[local_ip_type] [local_ip] + t=0 0 + m=audio [auto_media_port] RTP/AVP 8 101 + a=rtpmap:8 PCMA/8000 + a=rtpmap:101 telephone-event/8000 + a=fmtp:101 0-11,16 + + ]]> + + + + + + + + + + ;tag=[pid]SIPpTag09[call_number] + To: [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Subject: uac-pcap-carrier-fail-blacklist + Content-Length: 0 + + ]]> + + + + diff --git a/test/sip-tests.js b/test/sip-tests.js index d4a5f77..5974e18 100644 --- a/test/sip-tests.js +++ b/test/sip-tests.js @@ -26,6 +26,7 @@ function connect(connectable) { test('sbc-outbound tests', async(t) => { const {srf} = require('../app'); const { queryCdrs } = srf.locals; + const redisClient = srf.locals.realtimeDbHelpers.client; try { await connect(srf); @@ -95,7 +96,17 @@ test('sbc-outbound tests', async(t) => { /* fails when session limit exceeded */ await sippUac('uac-pcap-carrier-fail-limits.xml'); t.pass('fails when max calls in progress'); - + + // re-rack test data + execSync(`mysql -h 127.0.0.1 -u root --protocol=tcp -D jambones_test < ${__dirname}/db/jambones-sql.sql`); + execSync(`mysql -h 127.0.0.1 -u root --protocol=tcp -D jambones_test < ${__dirname}/db/populate-test-data.sql`); + + // Black list good carrier for some seconds + await redisClient.setex('blacklist-sip-gateway:124a5339-c62c-4075-9e19-f4de70a96597', 3, ''); + await sippUac('uac-pcap-carrier-fail-blacklist.xml'); + t.pass('fails when carrier is blacklisted'); + await redisClient.del('blacklist-sip-gateway:124a5339-c62c-4075-9e19-f4de70a96597'); + await waitFor(25); const res = await queryCdrs({account_sid: 'ed649e33-e771-403a-8c99-1780eabbc803'});