view sip-in/invite.c @ 124:7e04d28fae8b

sip-in: default use-100rel to no BulkVS servers act badly when we send a reliable 180 Ringing response to an incoming call, even though they advertise 100rel support in the Supported header in the INVITE packet, and we probably won't be implementing 100rel for outbound because doing per-the-spec PRACK as a UAC is just too burdensome. Therefore, we need to consider 100rel extension as not-really-supported in themwi-system-sw.
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 01 Oct 2022 15:54:50 -0800
parents c93c339271a7
children
line wrap: on
line source

/*
 * 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;
}