From c21851c2e099676ad0f413ffb7dcd4b105b91af1 Mon Sep 17 00:00:00 2001 From: Dave Horton <daveh@beachdognet.com> Date: Thu, 12 Dec 2024 13:05:52 -0500 Subject: [PATCH 1/4] wip --- app.js | 4 ---- lib/call-session.js | 28 ++++++++++++++++++++-------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/app.js b/app.js index 6af4038..c653e63 100644 --- a/app.js +++ b/app.js @@ -244,10 +244,6 @@ srf.invite((req, res) => { } return session.replaces(req, res); } - if (req.locals.sdp === '') { - logger.info('no sdp in invite'); - return res.send(488, {headers: {'X-Reason': '3pcc INVITEs without SDP are not currently supported'}}); - } const session = new CallSession(logger, req, res); session.connect(); }); diff --git a/lib/call-session.js b/lib/call-session.js index 6ddd567..974dcc4 100644 --- a/lib/call-session.js +++ b/lib/call-session.js @@ -108,7 +108,8 @@ class CallSession extends Emitter { async connect() { const {sdp} = this.req.locals; - this.logger.info('inbound call accepted for routing'); + const is3pcc = this.req.body?.length === 0; + this.logger.info(`inbound ${is3pcc ? '3pcc ' : ''}call accepted for routing`); const engine = this.getRtpEngine(); if (!engine) { this.logger.info('No available rtpengines, rejecting call!'); @@ -183,13 +184,13 @@ class CallSession extends Emitter { else uri = `${scheme}:${host}`; this.logger.info(`uri will be: ${uri}, proxy ${proxy}`); - try { + const sendOfferToRtpEngine = async(remoteSdp) => { const opts = { ...this.rtpEngineOpts.common, ...this.rtpEngineOpts.uac.mediaOpts, 'from-tag': this.rtpEngineOpts.uas.tag, direction: [isPrivateVoipNetwork(this.req.source_address) ? 'private' : 'public', 'private'], - sdp + sdp: remoteSdp }; const startAt = process.hrtime(); const response = await this.offer(opts); @@ -202,7 +203,11 @@ class CallSession extends Emitter { this.logger.error({}, `rtpengine offer failed with ${JSON.stringify(response)}`); throw new Error('rtpengine failed: answer'); } + return response; + }; + try { + const response = await sendOfferToRtpEngine(sdp); let headers = { 'From': createBLegFromHeader(this.req), 'To': this.req.get('To'), @@ -212,9 +217,13 @@ class CallSession extends Emitter { }; if (this.privateSipAddress) headers = {...headers, Contact: `<sip:${this.privateSipAddress}>`}; - const spdOfferB = this.siprec && this.xml ? - createSiprecBody(headers, response.sdp, this.xml.type, this.xml.content) : - response.sdp; + let spdOfferB; + if (this.siprec && this.xml) { + spdOfferB = createSiprecBody(headers, response.sdp, this.xml.type, this.xml.content); + } + else if (!is3pcc) { + spdOfferB = response.sdp; + } if (this.req.locals.carrier) { Object.assign(headers, { @@ -281,7 +290,10 @@ class CallSession extends Emitter { '-X-Authenticated-User' ], proxyResponseHeaders: ['all', '-X-Trace-ID'], - localSdpB: spdOfferB, + localSdpB: spdOfferB ? spdOfferB : async(ackBody) => { + const response = await sendOfferToRtpEngine(ackBody); + return response.sdp; + }, localSdpA: async(sdp, res) => { this.rtpEngineOpts.uac.tag = res.getParsedHeader('To').params.tag; const opts = { @@ -313,7 +325,7 @@ class CallSession extends Emitter { } return response.sdp; - } + }, }); // successfully connected From e40550922009ffc6c7d4559aedef29ee2bfe21b5 Mon Sep 17 00:00:00 2001 From: Dave Horton <daveh@beachdognet.com> Date: Thu, 12 Dec 2024 13:54:42 -0500 Subject: [PATCH 2/4] wip --- lib/call-session.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/call-session.js b/lib/call-session.js index 974dcc4..474a79c 100644 --- a/lib/call-session.js +++ b/lib/call-session.js @@ -304,7 +304,7 @@ class CallSession extends Emitter { sdp }; const startAt = process.hrtime(); - const response = await this.answer(opts); + const response = await this.answer({direction: ['private', 'public'], ...opts}); this.logger.debug({response, opts}, 'response from rtpengine to answer'); const rtt = roundTripTime(startAt); this.stats.histogram('app.rtpengine.response_time', rtt, [ From 0d584a101b66a9d0616f0e113583d3fdbd52104f Mon Sep 17 00:00:00 2001 From: Dave Horton <daveh@beachdognet.com> Date: Thu, 12 Dec 2024 14:07:01 -0500 Subject: [PATCH 3/4] wip --- lib/call-session.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/call-session.js b/lib/call-session.js index 474a79c..8f59818 100644 --- a/lib/call-session.js +++ b/lib/call-session.js @@ -304,8 +304,12 @@ class CallSession extends Emitter { sdp }; const startAt = process.hrtime(); - const response = await this.answer({direction: ['private', 'public'], ...opts}); - this.logger.debug({response, opts}, 'response from rtpengine to answer'); + const aOpts = { + ...opts, + ...(is3pcc && {direction: ['private', 'public']}) + }; + const response = await this.answer(aOpts); + this.logger.debug({response, opts: aOpts}, 'response from rtpengine to answer'); const rtt = roundTripTime(startAt); this.stats.histogram('app.rtpengine.response_time', rtt, [ 'direction:inbound', 'command:answer', `rtpengine:${this.rtpengineIp}`]); From 094a675cd734c2c99b210d4483c8afa69416e8c6 Mon Sep 17 00:00:00 2001 From: Dave Horton <daveh@beachdognet.com> Date: Thu, 12 Dec 2024 14:20:24 -0500 Subject: [PATCH 4/4] add test for late media / 3pcc invite, which should now work --- test/scenarios/uac-late-media.xml | 77 +++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/test/scenarios/uac-late-media.xml b/test/scenarios/uac-late-media.xml index 8b96729..f5226cc 100644 --- a/test/scenarios/uac-late-media.xml +++ b/test/scenarios/uac-late-media.xml @@ -19,22 +19,21 @@ <!-- Sipp 'uac' scenario with pcap (rtp) play --> <!-- --> -<scenario name="UAC with late media"> +<scenario name="UAC with media"> <!-- In client mode (sipp placing calls), the Call-ID MUST be --> <!-- generated by sipp. To do so, use [call_id] keyword. --> <send retrans="500"> <![CDATA[ - INVITE sip:16173333456@[remote_ip]:[remote_port] SIP/2.0 + INVITE sip:+16173333456@jambonz.org SIP/2.0 Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch] - From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number] - To: sut <sip:[service]@[remote_ip]:[remote_port]> + From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number] + To: <sip:16173333456@jambonz.org> Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 - Subject: uac-no-3pcc - Content-Type: application/sdp + Subject: uac-late-media Content-Length: 0 ]]> @@ -43,27 +42,79 @@ <recv response="100" optional="true"> </recv> - <recv response="488"> + <recv response="180" optional="true"> + </recv> + + <!-- By adding rrs="true" (Record Route Sets), the route sets --> + <!-- are saved and used for following messages sent. Useful to test --> + <!-- against stateful SIP proxies/B2BUAs. --> + <recv response="200" rtd="true" crlf="true"> </recv> + <!-- Packet lost can be simulated in any send/recv message by --> + <!-- by adding the 'lost = "10"'. Value can be [1-100] percent. --> <send> <![CDATA[ - ACK sip:16173333456@[remote_ip]:[remote_port] SIP/2.0 - [last_Via] + ACK sip:16173333456@jambonz.org SIP/2.0 + Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch] From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number] - To: [service] <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param] + To: <sip:16173333456@jambonz.org>[peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK - Subject: uac-no-3pcc + Max-Forwards: 70 + Subject: uac-late-media + 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 + + + ]]> + </send> + + <!-- Play a pre-recorded PCAP file (RTP stream) --> + <nop> + <action> + <exec play_pcap_audio="pcap/g711a.pcap"/> + </action> + </nop> + + <!-- Pause briefly --> + <pause milliseconds="3000"/> + + <!-- The 'crlf' option inserts a blank line in the statistics report. --> + <send retrans="500"> + <![CDATA[ + + BYE sip:16173333456@jambonz.org SIP/2.0 + Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch] + From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number] + To: <sip:16173333456@jambonz.org>[peer_tag_param] + Call-ID: [call_id] + CSeq: 2 BYE + Subject: uac-late-media Content-Length: 0 ]]> </send> - <!-- definition of the response time repartition table (unit is ms) --> + <recv response="200" crlf="true"> + </recv> + + <!-- definition of the response time repartition table (unit is ms) --> <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/> <!-- definition of the call length repartition table (unit is ms) --> <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/> -</scenario> \ No newline at end of file + +</scenario> +