changeset 146:54c2f271380d

sip-in: implement play-along responses to re-INVITEs
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 08 Oct 2022 19:53:23 -0800
parents 4b685a5d9bd4
children 94b5831c017f
files sip-in/invite_dup.c
diffstat 1 files changed, 84 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- 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 <sys/types.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -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, "<sip:%s:%u;transport=udp>",
+		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;