changeset 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 bea761629c5b
children e12036337412
files sip-in/Makefile sip-in/call.h sip-in/call_list.c sip-in/invite.c sip-in/mgw_ops.c sip-in/mgw_sock.c sip-in/sip_uas.c
diffstat 7 files changed, 518 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/sip-in/Makefile	Thu Sep 08 14:53:18 2022 -0800
+++ b/sip-in/Makefile	Sun Sep 11 15:42:54 2022 -0800
@@ -1,8 +1,8 @@
 CC=	gcc
 CFLAGS=	-O2
 PROG=	themwi-sip-in
-OBJS=	invite.o main.o mgw_sock.o mncc_sock.o readconf.o sip_log.o sip_uas.o \
-	sip_udp.o
+OBJS=	call_list.o invite.o main.o mgw_ops.o mgw_sock.o mncc_sock.o readconf.o\
+	sip_log.o sip_uas.o sip_udp.o
 LIBS=	../libnumdb/libnumdb.a ../libsip/libsip.a ../libutil/libutil.a
 INSTBIN=/usr/local/bin
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-in/call.h	Sun Sep 11 15:42:54 2022 -0800
@@ -0,0 +1,65 @@
+/*
+ * struct call defined in this header file is the big daddy:
+ * it is the main call state structure for themwi-sip-in.
+ */
+
+struct call {
+	/* call list management */
+	char		*sip_call_id;
+	struct call	*next;
+	/* filled from initial INVITE */
+	struct sockaddr_in udp_sin;
+	char		*invite_from;
+	char		*invite_to;
+	char		*invite_via;
+	unsigned	invite_cseq;
+	char		called_nanp[11];
+	char		*from_uri;
+	unsigned	from_uri_len;
+	char		*from_user;
+	unsigned	from_user_len;
+	int		use_100rel;
+	/* PSTN side RTP info */
+	struct sockaddr_in pstn_rtp_local;
+	struct sockaddr_in pstn_rtp_remote;
+	int		use_pcma;
+	/* GSM side RTP info */
+	struct sockaddr_storage gsm_rtp_osmo;
+	struct sockaddr_storage gsm_rtp_tmgw;
+	uint32_t	gsm_payload_type;
+	uint32_t	gsm_payload_msg_type;
+	/* state machines */
+	uint32_t	overall_state;
+	uint32_t	sip_state;
+	uint32_t	mgw_state;
+	uint32_t	mgw_ep_id;
+	uint32_t	mgw_xact;
+	uint32_t	mgw_xact_id;
+	uint32_t	sdp_addend;
+	char		invite_fail[80];
+	unsigned	sip_tx_count;
+};
+
+#define	OVERALL_STATE_CRCX		1
+#define	OVERALL_STATE_CALL_GSM		2
+#define	OVERALL_STATE_ALERTING		3
+#define	OVERALL_STATE_ANSWERED		4
+#define	OVERALL_STATE_CONNECTED		5
+#define	OVERALL_STATE_TEARDOWN		6
+#define	OVERALL_STATE_DEAD_SIP		7
+
+#define	SIP_STATE_INVITE_PROC		1
+#define	SIP_STATE_RINGING		2
+#define	SIP_STATE_RINGING_PRACK		3
+#define	SIP_STATE_INVITE_200		4
+#define	SIP_STATE_CONNECTED		5
+#define	SIP_STATE_BYE_SENT		6
+#define	SIP_STATE_INVITE_ERR		7
+#define	SIP_STATE_ENDED			8
+#define	SIP_STATE_MSG_SIZE_ERR		9
+
+#define	MGW_STATE_NO_EXIST		0
+#define	MGW_STATE_ALLOCATED		1
+#define	MGW_STATE_CONNECTING		2
+#define	MGW_STATE_COMPLETE		3
+#define	MGW_STATE_DELETING		4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-in/call_list.c	Sun Sep 11 15:42:54 2022 -0800
@@ -0,0 +1,28 @@
+/*
+ * In this module we implement call list management for themwi-sip-in.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include "call.h"
+
+struct call *call_list;
+
+struct call *
+find_call_by_sip_id(sought_id)
+	char *sought_id;
+{
+	struct call *call;
+
+	for (call = call_list; call; call = call->next)
+		if (!strcmp(call->sip_call_id, sought_id))
+			return call;
+	return 0;
+}
--- 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-in/mgw_ops.c	Sun Sep 11 15:42:54 2022 -0800
@@ -0,0 +1,204 @@
+/*
+ * In this module we implement all transactions from themwi-sip-in
+ * toward themwi-mgw.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include "../include/tmgw_ctrl.h"
+#include "../include/tmgw_const.h"
+#include "call.h"
+
+extern struct call *call_list;
+
+struct call *
+find_call_with_mgw_xact(xact_id)
+	uint32_t xact_id;
+{
+	struct call *call;
+
+	for (call = call_list; call; call = call->next)
+		if (call->mgw_xact && call->mgw_xact_id == xact_id)
+			return call;
+	return 0;
+}
+
+uint32_t
+get_new_tmgw_xact_id()
+{
+	static uint32_t next_xact_id;
+
+	for (;;) {
+		next_xact_id++;
+		if (!find_call_with_mgw_xact(next_xact_id))
+			return next_xact_id;
+	}
+}
+
+void
+tmgw_send_crcx(call)
+	struct call *call;
+{
+	struct tmgw_ctrl_req req;
+
+	bzero(&req, sizeof req);
+	req.opcode = TMGW_CTRL_OP_CRCX;
+	req.transact_ref = get_new_tmgw_xact_id();
+	req.ep_id = TMGW_EP_TYPE_GATEWAY;
+	req.setup_mask = TMGW_CTRL_MASK_PSTN_CONN;
+	bcopy(&call->pstn_rtp_remote, &req.pstn_addr,
+		sizeof(struct sockaddr_in));
+	req.pstn_payload_type =
+		call->use_pcma ? PSTN_CODEC_PCMA : PSTN_CODEC_PCMU;
+	send_req_to_tmgw(&req);
+	call->mgw_xact = TMGW_CTRL_OP_CRCX;
+	call->mgw_xact_id = req.transact_ref;
+}
+
+void
+tmgw_send_mdcx_gsm_rtp(call)
+	struct call *call;
+{
+	struct tmgw_ctrl_req req;
+
+	bzero(&req, sizeof req);
+	req.opcode = TMGW_CTRL_OP_MDCX;
+	req.transact_ref = get_new_tmgw_xact_id();
+	req.ep_id = call->mgw_ep_id;;
+	req.setup_mask = TMGW_CTRL_MASK_GSM_CONN;
+	bcopy(&call->gsm_rtp_osmo, &req.gsm_addr,
+		sizeof(struct sockaddr_storage));
+	req.gsm_payload_type = call->gsm_payload_type;
+	req.gsm_payload_msg_type = call->gsm_payload_msg_type;
+	send_req_to_tmgw(&req);
+	call->mgw_state = MGW_STATE_CONNECTING;
+	call->mgw_xact = TMGW_CTRL_OP_MDCX;
+	call->mgw_xact_id = req.transact_ref;
+}
+
+void
+tmgw_send_dlcx(call)
+	struct call *call;
+{
+	struct tmgw_ctrl_req req;
+
+	bzero(&req, sizeof req);
+	req.opcode = TMGW_CTRL_OP_DLCX;
+	req.transact_ref = get_new_tmgw_xact_id();
+	req.ep_id = call->mgw_ep_id;;
+	send_req_to_tmgw(&req);
+	call->mgw_state = MGW_STATE_DELETING;
+	call->mgw_xact = TMGW_CTRL_OP_DLCX;
+	call->mgw_xact_id = req.transact_ref;
+}
+
+static void
+handle_crcx_fail(call, msg)
+	struct call *call;
+	struct tmgw_ctrl_resp *msg;
+{
+	call->overall_state = OVERALL_STATE_TEARDOWN;
+	strcpy(call->invite_fail, "503 Gateway resource allocation failure");
+	signal_invite_error(call);
+}
+
+static void
+crcx_response(call, msg)
+	struct call *call;
+	struct tmgw_ctrl_resp *msg;
+{
+	if (msg->res == TMGW_RESP_OK) {
+		call->mgw_state = MGW_STATE_ALLOCATED;
+		call->mgw_ep_id = msg->ep_id;
+		bcopy(&msg->gsm_addr, &call->gsm_rtp_tmgw,
+			sizeof(struct sockaddr_storage));
+		bcopy(&msg->pstn_addr, &call->pstn_rtp_local,
+			sizeof(struct sockaddr_in));
+		switch (call->overall_state) {
+		case OVERALL_STATE_CRCX:
+			/* proceed_with_call_setup(call); */
+			return;
+		case OVERALL_STATE_TEARDOWN:
+			tmgw_send_dlcx(call);
+			return;
+		default:
+		bad_state:
+			syslog(LOG_CRIT,
+			"FATAL: invalid overall state 0x%x on CRCX response",
+				call->overall_state);
+			exit(1);
+		}
+	} else {
+		switch (call->overall_state) {
+		case OVERALL_STATE_CRCX:
+			handle_crcx_fail(call, msg);
+			return;
+		case OVERALL_STATE_TEARDOWN:
+			return;
+		default:
+			goto bad_state;
+		}
+	}
+}
+
+static void
+mdcx_response(call, msg)
+	struct call *call;
+	struct tmgw_ctrl_resp *msg;
+{
+	/* will be handled later */
+}
+
+static void
+dlcx_response(call, msg)
+	struct call *call;
+	struct tmgw_ctrl_resp *msg;
+{
+	if (msg->res != TMGW_RESP_OK) {
+		syslog(LOG_CRIT, "FATAL: TMGW DLCX failed with code 0x%x",
+			msg->res);
+		exit(1);
+	}
+	call->mgw_state = MGW_STATE_NO_EXIST;
+	/* TODO: transition from TEARDOWN to DEAD_SIP */
+}
+
+void
+process_tmgw_response(msg)
+	struct tmgw_ctrl_resp *msg;
+{
+	struct call *call;
+	unsigned opc;
+
+	call = find_call_with_mgw_xact(msg->transact_ref);
+	if (!call) {
+		syslog(LOG_CRIT,
+		"FATAL: response from TMGW xact 0x%x does not match any call",
+			msg->transact_ref);
+		exit(1);
+	}
+	opc = call->mgw_xact;
+	call->mgw_xact = 0;
+	switch (opc) {
+	case TMGW_CTRL_OP_CRCX:
+		crcx_response(call, msg);
+		return;
+	case TMGW_CTRL_OP_MDCX:
+		mdcx_response(call, msg);
+		return;
+	case TMGW_CTRL_OP_DLCX:
+		dlcx_response(call, msg);
+		return;
+	default:
+		syslog(LOG_CRIT,
+			"FATAL: invalid opcode 0x%x in call->msg_xact", opc);
+		exit(1);
+	}
+}
--- a/sip-in/mgw_sock.c	Thu Sep 08 14:53:18 2022 -0800
+++ b/sip-in/mgw_sock.c	Sun Sep 11 15:42:54 2022 -0800
@@ -53,5 +53,11 @@
 			rc);
 		exit(1);
 	}
-	/* processing to be implemented */
+	process_tmgw_response(&msg);
 }
+
+send_req_to_tmgw(msg)
+	struct tmgw_ctrl_req *msg;
+{
+	return send(mgw_socket, msg, sizeof(struct tmgw_ctrl_req), 0);
+}
--- a/sip-in/sip_uas.c	Thu Sep 08 14:53:18 2022 -0800
+++ b/sip-in/sip_uas.c	Sun Sep 11 15:42:54 2022 -0800
@@ -10,7 +10,6 @@
 #include <string.h>
 #include <strings.h>
 #include <syslog.h>
-#include <unistd.h>
 #include "../libsip/parse.h"
 #include "../libsip/uas_basic.h"
 #include "../libsip/out_msg.h"