diff sip-in/invite.c @ 60:02761f1ae5e5

sip-in INVITE processing: got as far as CRCX completion
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 11 Sep 2022 15:42:54 -0800
parents d61d0136f6a5
children 7c0309df59f8
line wrap: on
line diff
--- a/sip-in/invite.c	Thu Sep 08 14:53:18 2022 -0800
+++ b/sip-in/invite.c	Sun Sep 11 15:42:54 2022 -0800
@@ -5,26 +5,84 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 #include <stdio.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
 #include <syslog.h>
-#include <unistd.h>
 #include "../libsip/parse.h"
 #include "../libsip/uas_basic.h"
 #include "../libsip/grok_from.h"
 #include "../libsip/req_supp.h"
 #include "../libsip/sdp.h"
 #include "../libsip/out_msg.h"
+#include "call.h"
 
 extern struct in_addr sip_bind_ip;
+extern unsigned sip_bind_port;
 extern int cfg_use_100rel;
+extern struct call *call_list;
 
+extern struct call *find_call_by_sip_id();
 extern char *get_single_header();
 
-void
-handle_sip_invite(req, ess, sin)
+fill_invite_resp_from_call(msg, call)
+	struct sip_msg_out *msg;
+	struct call *call;
+{
+	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", call->invite_cseq);
+	rc = out_msg_add_header(msg, "CSeq", cseq_str);
+	if (rc < 0)
+		return rc;
+	return out_msg_add_header(msg, "Via", call->invite_via);
+}
+
+fill_invite_200_resp(msg, call)
+	struct sip_msg_out *msg;
+	struct call *call;
+{
+	char contact_str[80];
+	struct sdp_gen sdp;
+	int rc;
+
+	start_response_out_msg(msg, "200 CONNECT");
+	rc = fill_invite_resp_from_call(msg, call);
+	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);
+}
+
+static void
+invite_new_call(req, ess, sin)
 	struct sip_pkt_rx *req;
 	struct uas_parse_hdrs *ess;
 	struct sockaddr_in *sin;
@@ -37,9 +95,11 @@
 	int ext_100rel_req, ext_100rel_sup, use_100rel, use_pcma;
 	struct sdp_parse sdp_parse;
 	struct sdp_gen sdp_gen;
+	struct call *call;
+	char *dp;
+	unsigned copylen;
 	int rc;
 
-	/* check for existing Call-ID will go here */
 	/* extract called number from Request-URI */
 	rc = user_from_sip_uri(req->req_uri, uri_user, 12);
 	if (rc < 0) {
@@ -48,12 +108,12 @@
 error_resp:	rc = add_resp_basic_headers(&resp, ess, req->req_method);
 		if (rc < 0) {
 error_resp_toolong:	syslog(LOG_ERR,
-				"INVITE error response length exceeded");
+				"INVITE early error response length exceeded");
 			return;
 		}
 		out_msg_finish(&resp);
 		sip_tx_packet(&resp, sin);
-			return;
+		return;
 	}
 	if (uri_user[0] == '+') {
 		if (grok_number_string(uri_user+1, 0) != 11 ||
@@ -156,7 +216,7 @@
 		if (rc < 0)
 			goto error_resp_toolong;
 		sip_tx_packet(&resp, sin);
-			return;
+		return;
 	}
 	/* SIP INVITE validation done - check if GSM service is up */
 	rc = connect_gsm_mtcall();
@@ -165,4 +225,149 @@
 		goto error_resp;
 	}
 	/* stateful processing begins */
+	call = malloc(sizeof(struct call) + strlen(ess->call_id) +
+			strlen(ess->from) + strlen(ess->to) + strlen(ess->via)
+			+ 4);
+	if (!call) {
+		syslog(LOG_CRIT, "failed malloc for incoming call!");
+		start_response_out_msg(&resp,
+			"503 Gateway resource allocation failure");
+		goto error_resp;
+	}
+	bzero(call, sizeof(struct call));
+	dp = (char *)(call + 1);
+	copylen = strlen(ess->call_id) + 1;
+	call->sip_call_id = dp;
+	bcopy(ess->call_id, dp, copylen);
+	dp += copylen;
+	copylen = strlen(ess->from) + 1;
+	call->invite_from = dp;
+	bcopy(ess->from, dp, copylen);
+	dp += copylen;
+	copylen = strlen(ess->to) + 1;
+	call->invite_to = dp;
+	bcopy(ess->to, dp, copylen);
+	dp += copylen;
+	copylen = strlen(ess->via) + 1;
+	call->invite_via = dp;
+	bcopy(ess->via, dp, copylen);
+	call->invite_cseq = ess->cseq_num;
+	bcopy(sin, &call->udp_sin, sizeof(struct sockaddr_in));
+	bcopy(called_nanp, call->called_nanp, 11);
+	call->from_uri = call->invite_from + (gfrom.uri - ess->from);
+	call->from_uri_len = gfrom.uri_len;
+	call->from_user = call->invite_from + (gfrom.user - ess->from);
+	call->from_user_len = gfrom.user_len;
+	call->use_100rel = use_100rel;
+	call->pstn_rtp_remote.sin_family = AF_INET;
+	call->pstn_rtp_remote.sin_addr = sdp_parse.ip_addr;
+	call->pstn_rtp_remote.sin_port = htons(sdp_parse.audio_port);
+	call->use_pcma = use_pcma;
+	/* generate 100 response */
+	start_response_out_msg(&resp, "100 Proceeding");
+	rc = fill_invite_resp_from_call(&resp, call);
+	if (rc < 0) {
+		syslog(LOG_ERR, "INVITE 100 response length exceeded");
+		free(call);
+		return;
+	}
+	out_msg_finish(&resp);
+	sip_tx_packet(&resp, sin);
+	/* add to call list */
+	call->next = call_list;
+	call_list = call;
+	/* send CRCX to TMGW */
+	tmgw_send_crcx(call);
+	call->overall_state = OVERALL_STATE_CRCX;
+	call->sip_state = SIP_STATE_INVITE_PROC;
 }
+
+static void
+invite_existing_call(req, ess, sin, call)
+	struct sip_pkt_rx *req;
+	struct uas_parse_hdrs *ess;
+	struct sockaddr_in *sin;
+	struct call *call;
+{
+	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);
+		if (rc < 0) {
+			syslog(LOG_ERR,
+		"sending 501 Re-INVITE error: response length exceeded");
+			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:
+	case SIP_STATE_RINGING_PRACK:
+		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_INVITE_200:
+	case SIP_STATE_CONNECTED:
+		fill_invite_200_resp(&resp, call);
+		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);
+		out_msg_finish(&resp);
+		sip_tx_packet(&resp, sin);
+		return;
+	default:
+		/* silently discard */
+		return;
+	}
+}
+
+void
+handle_sip_invite(req, ess, sin)
+	struct sip_pkt_rx *req;
+	struct uas_parse_hdrs *ess;
+	struct sockaddr_in *sin;
+{
+	struct call *call;
+
+	call = find_call_by_sip_id(ess->call_id);
+	if (call)
+		invite_existing_call(req, ess, sin, call);
+	else
+		invite_new_call(req, ess, sin);
+}
+
+void
+signal_invite_error(call)
+	struct call *call;
+{
+	struct sip_msg_out resp;
+	int rc;
+
+	start_response_out_msg(&resp, call->invite_fail);
+	rc = fill_invite_resp_from_call(&resp, call);
+	if (rc < 0) {
+		syslog(LOG_ERR, "INVITE late error response length exceeded");
+		call->sip_state = SIP_STATE_MSG_SIZE_ERR;
+		/* TODO: transition from TEARDOWN to DEAD_SIP */
+		return;
+	}
+	out_msg_finish(&resp);
+	sip_tx_packet(&resp, &call->udp_sin);
+	call->sip_state = SIP_STATE_INVITE_ERR;
+	call->sip_tx_count = 1;
+}