# HG changeset patch # User Mychaela Falconia # Date 1665287603 28800 # Node ID 54c2f271380d940529efb967f34422f6c5bca69e # Parent 4b685a5d9bd410cc299adae8d1e1a7fc2e3d4274 sip-in: implement play-along responses to re-INVITEs diff -r 4b685a5d9bd4 -r 54c2f271380d sip-in/invite_dup.c --- a/sip-in/invite_dup.c Sat Oct 08 19:31:05 2022 -0800 +++ b/sip-in/invite_dup.c Sat Oct 08 19:53:23 2022 -0800 @@ -1,12 +1,19 @@ /* * Here we process SIP INVITE retransmissions and/or re-INVITEs - * for existing calls. + * for existing calls. Our new approach is that we shall generate + * the same stateless response without caring if it's a retransmission + * or a re-INVITE, using CSeq and Via from the (re-)INVITE packet + * we are responding to. Our previous approach was to reject all + * re-INVITE requests, but it turns out that we have to support them + * in order to satisfy BulkVS and other calling servers that use + * session timers. */ #include #include #include #include +#include #include #include #include @@ -19,6 +26,66 @@ #include "../libsip/out_msg.h" #include "call.h" +extern struct in_addr sip_bind_ip; +extern unsigned sip_bind_port; + +static +fill_reinvite_resp(msg, call, ess) + struct sip_msg_out *msg; + struct call *call; + struct uas_parse_hdrs *ess; +{ + char cseq_str[32]; + int rc; + + rc = out_msg_add_header(msg, "From", call->invite_from); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "To", call->invite_to); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "Call-ID", call->sip_call_id); + if (rc < 0) + return rc; + sprintf(cseq_str, "%u INVITE", ess->cseq_num); + rc = out_msg_add_header(msg, "CSeq", cseq_str); + if (rc < 0) + return rc; + return out_msg_add_header(msg, "Via", ess->via); +} + +static +fill_reinvite_resp_200(msg, call, ess) + struct sip_msg_out *msg; + struct call *call; + struct uas_parse_hdrs *ess; +{ + char contact_str[80]; + struct sdp_gen sdp; + int rc; + + start_response_out_msg(msg, "200 CONNECT"); + rc = fill_reinvite_resp(msg, call, ess); + if (rc < 0) + return rc; + sprintf(contact_str, "", + inet_ntoa(sip_bind_ip), sip_bind_port); + rc = out_msg_add_header(msg, "Contact", contact_str); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "Content-Type", "application/sdp"); + if (rc < 0) + return rc; + bzero(&sdp, sizeof sdp); + sdp.conn_ip = call->pstn_rtp_local.sin_addr; + sdp.conn_port = ntohs(call->pstn_rtp_local.sin_port); + sdp.codec_mask = call->use_pcma ? SDP_CODEC_MASK_PCMA + : SDP_CODEC_MASK_PCMU; + sdp.session_id = (sdp.conn_port << 16) | call->sdp_addend; + sdp.owner_ip = sip_bind_ip; + return out_msg_finish_sdp(msg, &sdp); +} + void invite_existing_call(req, ess, sin, call) struct sip_pkt_rx *req; @@ -29,48 +96,40 @@ struct sip_msg_out resp; int rc; - if (ess->cseq_num != call->invite_cseq) { - start_response_out_msg(&resp, "501 Re-INVITE not supported"); - rc = add_resp_basic_headers(&resp, ess, req->req_method); + switch (call->sip_state) { + case SIP_STATE_INVITE_PROC: + start_response_out_msg(&resp, "100 Proceeding"); + rc = fill_reinvite_resp(&resp, call, ess); if (rc < 0) { - syslog(LOG_ERR, - "sending 501 Re-INVITE error: response length exceeded"); +msg_size_err: syslog(LOG_ERR, + "Call in%06u: msg size error on re-INVITE response", + call->in_tag_num); return; } out_msg_finish(&resp); sip_tx_packet(&resp, sin); return; - } - /* it's a retransmission, not a re-INVITE */ - switch (call->sip_state) { - case SIP_STATE_INVITE_PROC: - start_response_out_msg(&resp, "100 Proceeding"); - fill_invite_resp_from_call(&resp, call); - out_msg_finish(&resp); - sip_tx_packet(&resp, sin); - return; case SIP_STATE_RINGING: - start_response_out_msg(&resp, "180 Ringing"); - fill_invite_resp_from_call(&resp, call); - out_msg_finish(&resp); - sip_tx_packet(&resp, sin); - return; case SIP_STATE_RINGING_REL: start_response_out_msg(&resp, "180 Ringing"); - fill_invite_resp_from_call(&resp, call); - out_msg_add_header(&resp, "Require", "100rel"); - out_msg_add_header(&resp, "RSeq", "1"); + rc = fill_reinvite_resp(&resp, call, ess); + if (rc < 0) + goto msg_size_err; out_msg_finish(&resp); sip_tx_packet(&resp, sin); return; case SIP_STATE_INVITE_200: case SIP_STATE_CONNECTED: - fill_invite_200_resp(&resp, call); + rc = fill_reinvite_resp_200(&resp, call, ess); + if (rc < 0) + goto msg_size_err; sip_tx_packet(&resp, sin); return; case SIP_STATE_INVITE_ERR: start_response_out_msg(&resp, call->invite_fail); - fill_invite_resp_from_call(&resp, call); + rc = fill_reinvite_resp(&resp, call, ess); + if (rc < 0) + goto msg_size_err; out_msg_finish(&resp); sip_tx_packet(&resp, sin); return;