diff sip-in/invite_init.c @ 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 sip-in/invite.c@c93c339271a7
children 257da5474e77
line wrap: on
line diff
--- /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);
+}