changeset 145:4b685a5d9bd4

sip-in code: split invite.c into 3 separate C modules
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 08 Oct 2022 19:31:05 -0800
parents 4e16aeafbfbf
children 54c2f271380d
files sip-in/Makefile sip-in/invite.c sip-in/invite_dup.c sip-in/invite_init.c sip-in/invite_resp.c
diffstat 5 files changed, 543 insertions(+), 504 deletions(-) [+]
line wrap: on
line diff
--- a/sip-in/Makefile	Sat Oct 08 19:06:24 2022 -0800
+++ b/sip-in/Makefile	Sat Oct 08 19:31:05 2022 -0800
@@ -2,9 +2,9 @@
 CFLAGS=	-O2
 PROG=	themwi-sip-in
 OBJS=	bye_in.o bye_out.o call_clear.o call_list.o call_setup.o cancel.o \
-	disconnect.o invite.o main.o mgw_ops.o mgw_sock.o mncc_handle.o \
-	mncc_sock.o prack.o readconf.o retrans.o shutdown.o sip_ack.o sip_log.o\
-	sip_uas.o sip_udp.o
+	disconnect.o invite_dup.o invite_init.o invite_resp.o main.o mgw_ops.o \
+	mgw_sock.o mncc_handle.o mncc_sock.o prack.o readconf.o retrans.o \
+	shutdown.o sip_ack.o sip_log.o sip_uas.o sip_udp.o
 LIBS=	../libnumdb/libnumdb.a ../libsip/libsip.a ../libutil/libutil.a
 INSTBIN=/usr/local/bin
 
--- a/sip-in/invite.c	Sat Oct 08 19:06:24 2022 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,501 +0,0 @@
-/*
- * Here we implement our handling of SIP INVITE method.
- */
-
-#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>
-#include <string.h>
-#include <strings.h>
-#include <syslog.h>
-#include "../include/gsm48_const.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 int cfg_force_pcma;
-extern struct call *call_list;
-extern unsigned sip_linger_error;
-
-extern struct call *find_call_by_sip_id();
-extern char *get_single_header();
-
-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;
-{
-	static unsigned cycle_tag_num, cycle_sdp_addend;
-	char uri_user[13], *called_nanp;
-	struct sip_msg_out resp;
-	struct grok_from gfrom;
-	struct supported_ext supp_ext;
-	char *hval, *unsup_ext;
-	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, cdr_str[80];
-	unsigned cdr_num_len, cdr_cnam_len;
-	unsigned req_uri_len, to_hdr_len, copylen;
-	int rc;
-
-	/* extract called number from Request-URI */
-	rc = user_from_sip_uri(req->req_uri, uri_user, 12);
-	if (rc < 0) {
-not_nanp:	start_response_out_msg(&resp,
-			"416 Request-URI is not a NANP number");
-error_resp:	rc = add_resp_basic_headers(&resp, ess, req->req_method);
-		if (rc < 0) {
-error_resp_toolong:	syslog(LOG_ERR,
-				"INVITE early error response length exceeded");
-			return;
-		}
-		out_msg_finish(&resp);
-		sip_tx_packet(&resp, sin);
-		return;
-	}
-	if (uri_user[0] == '+') {
-		if (grok_number_string(uri_user+1, 0) != 11 ||
-		    uri_user[1] != '1')
-			goto not_nanp;
-		called_nanp = uri_user + 2;
-	} else switch (grok_number_string(uri_user)) {
-	case 10:
-		called_nanp = uri_user;
-		break;
-	case 11:
-		if (uri_user[0] != '1')
-			goto not_nanp;
-		called_nanp = uri_user + 1;
-		break;
-	default:
-		goto not_nanp;
-	}
-	if (!is_nanp_valid_prefix(called_nanp))
-		goto not_nanp;
-	/* it is valid NANP - but is it one of ours? */
-	refresh_number_db();
-	if (!is_nanp_locally_owned(called_nanp)) {
-		start_response_out_msg(&resp,
-			"404 Called number does not belong here");
-		goto error_resp;
-	}
-	/* parse and validate From header */
-	rc = grok_from_header(ess->from, &gfrom);
-	if (rc < 0) {
-		start_response_out_msg(&resp, "400 Malformed From header");
-		goto error_resp;
-	}
-	/* validate To header for the purpose of tag addition */
-	req_uri_len = strlen(req->req_uri);
-	to_hdr_len = strlen(ess->to);
-	if (to_hdr_len == req_uri_len) {
-		if (strcasecmp(ess->to, req->req_uri)) {
-bad_to_header:		start_response_out_msg(&resp, "400 Bad To header");
-			goto error_resp;
-		}
-	} else if (to_hdr_len == req_uri_len + 2) {
-		if (ess->to[0] != '<')
-			goto bad_to_header;
-		if (strncasecmp(ess->to+1, req->req_uri, req_uri_len))
-			goto bad_to_header;
-		if (ess->to[req_uri_len+1] != '>')
-			goto bad_to_header;
-	} else
-		goto bad_to_header;
-	/* check 100rel and catch any unsupported requirements */
-	supp_ext.name = "100rel";
-	supp_ext.req_flag = &ext_100rel_req;
-	supp_ext.sup_flag = &ext_100rel_sup;
-	ext_100rel_req = ext_100rel_sup = 0;
-	rc = parse_require_supported(req, &supp_ext, 1, &unsup_ext);
-	if (rc < 0) {
-		start_response_out_msg(&resp, "420 Extension not supported");
-		rc = out_msg_add_header(&resp, "Unsupported", unsup_ext);
-		if (rc < 0)
-			goto error_resp_toolong;
-		goto error_resp;
-	}
-	if (ext_100rel_req)
-		use_100rel = 1;
-	else if (ext_100rel_sup)
-		use_100rel = cfg_use_100rel;
-	else
-		use_100rel = 0;
-	/* did the caller send an SDP message body? */
-	if (!req->msg_body_len) {
-		start_response_out_msg(&resp, "415 Missing SDP body");
-error_415:	rc = out_msg_add_header(&resp, "Accept", "application/sdp");
-		if (rc < 0)
-			goto error_resp_toolong;
-		goto error_resp;
-	}
-	hval = get_single_header(req, "Content-Type", "c", (int *) 0);
-	if (!hval) {
-		start_response_out_msg(&resp,
-			"415 Missing Content-Type header");
-		goto error_415;
-	}
-	if (strcasecmp(hval, "application/sdp")) {
-		start_response_out_msg(&resp, "415 Unsupported Content-Type");
-		goto error_415;
-	}
-	rc = parse_incoming_sdp(req->msg_body, req->msg_body_len, &sdp_parse);
-	if (rc < 0) {
-		start_response_out_msg(&resp, "488 Malformed SDP body");
-		goto error_resp;
-	}
-	switch (sdp_parse.codec_mask) {
-	case SDP_CODEC_MASK_PCMU:
-		use_pcma = 0;
-		break;
-	case SDP_CODEC_MASK_PCMA:
-	case SDP_CODEC_MASK_BOTH | SDP_CODEC_MASK_PCMA_PREF:
-		use_pcma = 1;
-		break;
-	case SDP_CODEC_MASK_BOTH:
-		use_pcma = cfg_force_pcma;
-		break;
-	default:
-		start_response_out_msg(&resp,
-			"488 Unsupported codec selection");
-		rc = add_resp_basic_headers(&resp, ess, req->req_method);
-		if (rc < 0)
-			goto error_resp_toolong;
-		rc = out_msg_add_header(&resp, "Content-Type",
-					"application/sdp");
-		if (rc < 0)
-			goto error_resp_toolong;
-		bzero(&sdp_gen, sizeof sdp_gen);
-		sdp_gen.owner_ip = sip_bind_ip;
-		sdp_gen.conn_ip = sip_bind_ip;
-		sdp_gen.codec_mask = SDP_CODEC_MASK_BOTH;
-		rc = out_msg_finish_sdp(&resp, &sdp_gen);
-		if (rc < 0)
-			goto error_resp_toolong;
-		sip_tx_packet(&resp, sin);
-		return;
-	}
-	/* SIP INVITE validation done - gather CDR info */
-	cdr_num_len = gfrom.user_len;
-	if (cdr_num_len > 33)
-		cdr_num_len = 33;
-	cdr_cnam_len = gfrom.cnam_len;
-	if (cdr_cnam_len > 33)
-		cdr_cnam_len = 33;
-	if (cdr_cnam_len)
-		sprintf(cdr_str, "%.*s (%s%.*s%s)", cdr_num_len, gfrom.user,
-			gfrom.cnam_quoted ? "\"" : "", cdr_cnam_len, gfrom.cnam,
-			gfrom.cnam_quoted ? "\"" : "");
-	else
-		sprintf(cdr_str, "%.*s", cdr_num_len, gfrom.user);
-	hval = get_single_header(req, "P-Asserted-Identity", (char *) 0,
-				 (int *) 0);
-	/* check if GSM service is up */
-	rc = connect_gsm_mtcall();
-	if (rc < 0) {
-gsm_offline:	syslog(LOG_INFO, "Down-call from %s to %s", cdr_str, uri_user);
-		if (hval)
-			syslog(LOG_INFO, "Down-call PAI: %.100s", hval);
-		start_response_out_msg(&resp, "480 GSM service is offline");
-		goto error_resp;
-	}
-	rc = connect_tmgw_socket();
-	if (rc < 0)
-		goto gsm_offline;
-	/* stateful processing begins */
-	call = malloc(sizeof(struct call) + strlen(ess->call_id) +
-		      strlen(ess->from) + req_uri_len + strlen(ess->via) + 19);
-	if (!call) {
-		syslog(LOG_CRIT, "failed malloc for incoming call!");
-		start_response_out_msg(&resp,
-			"503 Gateway resource allocation failure");
-		goto error_resp;
-	}
-	cycle_tag_num++;
-	if (cycle_tag_num >= 1000000)
-		cycle_tag_num = 0;
-	cycle_sdp_addend++;
-	if (cycle_sdp_addend >= 0x10000)
-		cycle_sdp_addend = 0;
-	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;
-	call->invite_to = dp;
-	*dp++ = '<';
-	bcopy(req->req_uri, dp, req_uri_len);
-	dp += req_uri_len;
-	*dp++ = '>';
-	sprintf(dp, ";tag=in%06u", cycle_tag_num);
-	dp += 14;
-	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;
-	call->in_tag_num = cycle_tag_num;
-	call->sdp_addend = cycle_sdp_addend;
-	/* 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;
-	syslog(LOG_INFO, "Call in%06u from %s to %s", call->in_tag_num,
-		cdr_str, uri_user);
-	if (hval)
-		syslog(LOG_INFO, "Call in%06u PAI: %.100s", call->in_tag_num,
-			hval);
-	/* 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:
-		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");
-		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_ringing(call)
-	struct call *call;
-{
-	struct sip_msg_out resp;
-	int rc;
-
-	start_response_out_msg(&resp, "180 Ringing");
-	rc = fill_invite_resp_from_call(&resp, call);
-	if (rc < 0) {
-msg_size_err:	syslog(LOG_ERR, "INVITE 180 response length exceeded");
-		call->sip_state = SIP_STATE_MSG_SIZE_ERR;
-		call->overall_state = OVERALL_STATE_TEARDOWN;
-		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
-				GSM48_CC_CAUSE_INTERWORKING);
-		disconnect_tmgw(call);
-		sip_mark_end_time(call, sip_linger_error);
-		return;
-	}
-	if (call->use_100rel) {
-		rc = out_msg_add_header(&resp, "Require", "100rel");
-		if (rc < 0)
-			goto msg_size_err;
-		rc = out_msg_add_header(&resp, "RSeq", "1");
-		if (rc < 0)
-			goto msg_size_err;
-	}
-	out_msg_finish(&resp);
-	sip_tx_packet(&resp, &call->udp_sin);
-	if (call->use_100rel) {
-		call->sip_state = SIP_STATE_RINGING_REL;
-		call->sip_tx_count = 1;
-	} else
-		call->sip_state = SIP_STATE_RINGING;
-}
-
-void
-signal_invite_200(call)
-	struct call *call;
-{
-	struct sip_msg_out resp;
-	int rc;
-
-	rc = fill_invite_200_resp(&resp, call);
-	if (rc < 0) {
-		syslog(LOG_ERR, "INVITE 200 response length exceeded");
-		call->sip_state = SIP_STATE_MSG_SIZE_ERR;
-		call->overall_state = OVERALL_STATE_TEARDOWN;
-		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
-				GSM48_CC_CAUSE_INTERWORKING);
-		disconnect_tmgw(call);
-		sip_mark_end_time(call, sip_linger_error);
-		return;
-	}
-	sip_tx_packet(&resp, &call->udp_sin);
-	call->sip_state = SIP_STATE_INVITE_200;
-	call->sip_tx_count = 1;
-}
-
-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;
-		sip_mark_end_time(call, sip_linger_error);
-		transition_dead_sip(call);
-		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/invite_dup.c	Sat Oct 08 19:31:05 2022 -0800
@@ -0,0 +1,81 @@
+/*
+ * Here we process SIP INVITE retransmissions and/or re-INVITEs
+ * for existing calls.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include "../libsip/parse.h"
+#include "../libsip/uas_basic.h"
+#include "../libsip/sdp.h"
+#include "../libsip/out_msg.h"
+#include "call.h"
+
+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:
+		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");
+		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;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-in/invite_init.c	Sat Oct 08 19:31:05 2022 -0800
@@ -0,0 +1,302 @@
+/*
+ * Here we implement our initial processing of SIP INVITE requests.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.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 int cfg_use_100rel;
+extern int cfg_force_pcma;
+extern struct call *call_list;
+
+extern struct call *find_call_by_sip_id();
+extern char *get_single_header();
+
+void
+invite_new_call(req, ess, sin)
+	struct sip_pkt_rx *req;
+	struct uas_parse_hdrs *ess;
+	struct sockaddr_in *sin;
+{
+	static unsigned cycle_tag_num, cycle_sdp_addend;
+	char uri_user[13], *called_nanp;
+	struct sip_msg_out resp;
+	struct grok_from gfrom;
+	struct supported_ext supp_ext;
+	char *hval, *unsup_ext;
+	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, cdr_str[80];
+	unsigned cdr_num_len, cdr_cnam_len;
+	unsigned req_uri_len, to_hdr_len, copylen;
+	int rc;
+
+	/* extract called number from Request-URI */
+	rc = user_from_sip_uri(req->req_uri, uri_user, 12);
+	if (rc < 0) {
+not_nanp:	start_response_out_msg(&resp,
+			"416 Request-URI is not a NANP number");
+error_resp:	rc = add_resp_basic_headers(&resp, ess, req->req_method);
+		if (rc < 0) {
+error_resp_toolong:	syslog(LOG_ERR,
+				"INVITE early error response length exceeded");
+			return;
+		}
+		out_msg_finish(&resp);
+		sip_tx_packet(&resp, sin);
+		return;
+	}
+	if (uri_user[0] == '+') {
+		if (grok_number_string(uri_user+1, 0) != 11 ||
+		    uri_user[1] != '1')
+			goto not_nanp;
+		called_nanp = uri_user + 2;
+	} else switch (grok_number_string(uri_user)) {
+	case 10:
+		called_nanp = uri_user;
+		break;
+	case 11:
+		if (uri_user[0] != '1')
+			goto not_nanp;
+		called_nanp = uri_user + 1;
+		break;
+	default:
+		goto not_nanp;
+	}
+	if (!is_nanp_valid_prefix(called_nanp))
+		goto not_nanp;
+	/* it is valid NANP - but is it one of ours? */
+	refresh_number_db();
+	if (!is_nanp_locally_owned(called_nanp)) {
+		start_response_out_msg(&resp,
+			"404 Called number does not belong here");
+		goto error_resp;
+	}
+	/* parse and validate From header */
+	rc = grok_from_header(ess->from, &gfrom);
+	if (rc < 0) {
+		start_response_out_msg(&resp, "400 Malformed From header");
+		goto error_resp;
+	}
+	/* validate To header for the purpose of tag addition */
+	req_uri_len = strlen(req->req_uri);
+	to_hdr_len = strlen(ess->to);
+	if (to_hdr_len == req_uri_len) {
+		if (strcasecmp(ess->to, req->req_uri)) {
+bad_to_header:		start_response_out_msg(&resp, "400 Bad To header");
+			goto error_resp;
+		}
+	} else if (to_hdr_len == req_uri_len + 2) {
+		if (ess->to[0] != '<')
+			goto bad_to_header;
+		if (strncasecmp(ess->to+1, req->req_uri, req_uri_len))
+			goto bad_to_header;
+		if (ess->to[req_uri_len+1] != '>')
+			goto bad_to_header;
+	} else
+		goto bad_to_header;
+	/* check 100rel and catch any unsupported requirements */
+	supp_ext.name = "100rel";
+	supp_ext.req_flag = &ext_100rel_req;
+	supp_ext.sup_flag = &ext_100rel_sup;
+	ext_100rel_req = ext_100rel_sup = 0;
+	rc = parse_require_supported(req, &supp_ext, 1, &unsup_ext);
+	if (rc < 0) {
+		start_response_out_msg(&resp, "420 Extension not supported");
+		rc = out_msg_add_header(&resp, "Unsupported", unsup_ext);
+		if (rc < 0)
+			goto error_resp_toolong;
+		goto error_resp;
+	}
+	if (ext_100rel_req)
+		use_100rel = 1;
+	else if (ext_100rel_sup)
+		use_100rel = cfg_use_100rel;
+	else
+		use_100rel = 0;
+	/* did the caller send an SDP message body? */
+	if (!req->msg_body_len) {
+		start_response_out_msg(&resp, "415 Missing SDP body");
+error_415:	rc = out_msg_add_header(&resp, "Accept", "application/sdp");
+		if (rc < 0)
+			goto error_resp_toolong;
+		goto error_resp;
+	}
+	hval = get_single_header(req, "Content-Type", "c", (int *) 0);
+	if (!hval) {
+		start_response_out_msg(&resp,
+			"415 Missing Content-Type header");
+		goto error_415;
+	}
+	if (strcasecmp(hval, "application/sdp")) {
+		start_response_out_msg(&resp, "415 Unsupported Content-Type");
+		goto error_415;
+	}
+	rc = parse_incoming_sdp(req->msg_body, req->msg_body_len, &sdp_parse);
+	if (rc < 0) {
+		start_response_out_msg(&resp, "488 Malformed SDP body");
+		goto error_resp;
+	}
+	switch (sdp_parse.codec_mask) {
+	case SDP_CODEC_MASK_PCMU:
+		use_pcma = 0;
+		break;
+	case SDP_CODEC_MASK_PCMA:
+	case SDP_CODEC_MASK_BOTH | SDP_CODEC_MASK_PCMA_PREF:
+		use_pcma = 1;
+		break;
+	case SDP_CODEC_MASK_BOTH:
+		use_pcma = cfg_force_pcma;
+		break;
+	default:
+		start_response_out_msg(&resp,
+			"488 Unsupported codec selection");
+		rc = add_resp_basic_headers(&resp, ess, req->req_method);
+		if (rc < 0)
+			goto error_resp_toolong;
+		rc = out_msg_add_header(&resp, "Content-Type",
+					"application/sdp");
+		if (rc < 0)
+			goto error_resp_toolong;
+		bzero(&sdp_gen, sizeof sdp_gen);
+		sdp_gen.owner_ip = sip_bind_ip;
+		sdp_gen.conn_ip = sip_bind_ip;
+		sdp_gen.codec_mask = SDP_CODEC_MASK_BOTH;
+		rc = out_msg_finish_sdp(&resp, &sdp_gen);
+		if (rc < 0)
+			goto error_resp_toolong;
+		sip_tx_packet(&resp, sin);
+		return;
+	}
+	/* SIP INVITE validation done - gather CDR info */
+	cdr_num_len = gfrom.user_len;
+	if (cdr_num_len > 33)
+		cdr_num_len = 33;
+	cdr_cnam_len = gfrom.cnam_len;
+	if (cdr_cnam_len > 33)
+		cdr_cnam_len = 33;
+	if (cdr_cnam_len)
+		sprintf(cdr_str, "%.*s (%s%.*s%s)", cdr_num_len, gfrom.user,
+			gfrom.cnam_quoted ? "\"" : "", cdr_cnam_len, gfrom.cnam,
+			gfrom.cnam_quoted ? "\"" : "");
+	else
+		sprintf(cdr_str, "%.*s", cdr_num_len, gfrom.user);
+	hval = get_single_header(req, "P-Asserted-Identity", (char *) 0,
+				 (int *) 0);
+	/* check if GSM service is up */
+	rc = connect_gsm_mtcall();
+	if (rc < 0) {
+gsm_offline:	syslog(LOG_INFO, "Down-call from %s to %s", cdr_str, uri_user);
+		if (hval)
+			syslog(LOG_INFO, "Down-call PAI: %.100s", hval);
+		start_response_out_msg(&resp, "480 GSM service is offline");
+		goto error_resp;
+	}
+	rc = connect_tmgw_socket();
+	if (rc < 0)
+		goto gsm_offline;
+	/* stateful processing begins */
+	call = malloc(sizeof(struct call) + strlen(ess->call_id) +
+		      strlen(ess->from) + req_uri_len + strlen(ess->via) + 19);
+	if (!call) {
+		syslog(LOG_CRIT, "failed malloc for incoming call!");
+		start_response_out_msg(&resp,
+			"503 Gateway resource allocation failure");
+		goto error_resp;
+	}
+	cycle_tag_num++;
+	if (cycle_tag_num >= 1000000)
+		cycle_tag_num = 0;
+	cycle_sdp_addend++;
+	if (cycle_sdp_addend >= 0x10000)
+		cycle_sdp_addend = 0;
+	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;
+	call->invite_to = dp;
+	*dp++ = '<';
+	bcopy(req->req_uri, dp, req_uri_len);
+	dp += req_uri_len;
+	*dp++ = '>';
+	sprintf(dp, ";tag=in%06u", cycle_tag_num);
+	dp += 14;
+	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;
+	call->in_tag_num = cycle_tag_num;
+	call->sdp_addend = cycle_sdp_addend;
+	/* 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;
+	syslog(LOG_INFO, "Call in%06u from %s to %s", call->in_tag_num,
+		cdr_str, uri_user);
+	if (hval)
+		syslog(LOG_INFO, "Call in%06u PAI: %.100s", call->in_tag_num,
+			hval);
+	/* send CRCX to TMGW */
+	tmgw_send_crcx(call);
+	call->overall_state = OVERALL_STATE_CRCX;
+	call->sip_state = SIP_STATE_INVITE_PROC;
+}
+
+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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-in/invite_resp.c	Sat Oct 08 19:31:05 2022 -0800
@@ -0,0 +1,157 @@
+/*
+ * Here we implement SIP INVITE response generation.
+ */
+
+#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>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include "../include/gsm48_const.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 unsigned sip_linger_error;
+
+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);
+}
+
+void
+signal_invite_ringing(call)
+	struct call *call;
+{
+	struct sip_msg_out resp;
+	int rc;
+
+	start_response_out_msg(&resp, "180 Ringing");
+	rc = fill_invite_resp_from_call(&resp, call);
+	if (rc < 0) {
+msg_size_err:	syslog(LOG_ERR, "INVITE 180 response length exceeded");
+		call->sip_state = SIP_STATE_MSG_SIZE_ERR;
+		call->overall_state = OVERALL_STATE_TEARDOWN;
+		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_INTERWORKING);
+		disconnect_tmgw(call);
+		sip_mark_end_time(call, sip_linger_error);
+		return;
+	}
+	if (call->use_100rel) {
+		rc = out_msg_add_header(&resp, "Require", "100rel");
+		if (rc < 0)
+			goto msg_size_err;
+		rc = out_msg_add_header(&resp, "RSeq", "1");
+		if (rc < 0)
+			goto msg_size_err;
+	}
+	out_msg_finish(&resp);
+	sip_tx_packet(&resp, &call->udp_sin);
+	if (call->use_100rel) {
+		call->sip_state = SIP_STATE_RINGING_REL;
+		call->sip_tx_count = 1;
+	} else
+		call->sip_state = SIP_STATE_RINGING;
+}
+
+void
+signal_invite_200(call)
+	struct call *call;
+{
+	struct sip_msg_out resp;
+	int rc;
+
+	rc = fill_invite_200_resp(&resp, call);
+	if (rc < 0) {
+		syslog(LOG_ERR, "INVITE 200 response length exceeded");
+		call->sip_state = SIP_STATE_MSG_SIZE_ERR;
+		call->overall_state = OVERALL_STATE_TEARDOWN;
+		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_INTERWORKING);
+		disconnect_tmgw(call);
+		sip_mark_end_time(call, sip_linger_error);
+		return;
+	}
+	sip_tx_packet(&resp, &call->udp_sin);
+	call->sip_state = SIP_STATE_INVITE_200;
+	call->sip_tx_count = 1;
+}
+
+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;
+		sip_mark_end_time(call, sip_linger_error);
+		transition_dead_sip(call);
+		return;
+	}
+	out_msg_finish(&resp);
+	sip_tx_packet(&resp, &call->udp_sin);
+	call->sip_state = SIP_STATE_INVITE_ERR;
+	call->sip_tx_count = 1;
+}