view mncc/call_setup.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 660126bd5f59
children 2ebad02adbe5
line wrap: on
line source

/*
 * In this module we implement setup of new calls: either new MO calls
 * coming from GSM or new MT calls coming from a ThemWi call socket.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include "../include/mncc.h"
#include "../include/gsm48_const.h"
#include "struct.h"
#include "gsm_call.h"

preen_msc_provided_number(nums)
	struct gsm_mncc_number *nums;
{
	int len;

	len = grok_number_string(nums->number, 0);
	switch (len) {
	case 4:
		nums->type = GSM48_TON_NET_SPEC;
		nums->plan = GSM48_NPI_PRIVATE;
		break;
	case 11:
		if (nums->number[0] != '1')
			return(0);
		nums->type = GSM48_TON_INTERNATIONAL;
		nums->plan = GSM48_NPI_ISDN_E164;
		break;
	default:
		return(0);
	}
	nums->screen = GSM48_SCRN_NETWORK;
	return(1);
}

void
reject_mo_call(callref, cause_loc, cause_val)
	uint32_t callref;
{
	struct gsm_mncc msg;

	bzero(&msg, sizeof(struct gsm_mncc));
	msg.msg_type = MNCC_REJ_REQ;
	msg.callref = callref;
	mncc_set_cause(&msg, cause_loc, cause_val);
	send_mncc_to_gsm(&msg, sizeof(struct gsm_mncc));
}

void
process_mo_call_setup(msg)
	struct gsm_mncc *msg;
{
	struct gsm_call *call;
	char nanp[11];
	int res, is_nanp, is_itn, is_local;

	if (preen_msc_provided_number(&msg->calling))
		msg->fields |= MNCC_F_CALLING;
	else
		msg->fields &= ~MNCC_F_CALLING;
	if (!(msg->fields & MNCC_F_CALLED)) {
		syslog(LOG_ERR, "rejecting MO call 0x%x: no called number",
			msg->callref);
		reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_INVAL_MAND_INF);
		return;
	}
	call = create_gsm_call(msg->callref);
	if (!call) {
		syslog(LOG_ERR, "rejecting MO call 0x%x: no memory for call",
			msg->callref);
		reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
		return;
	}
	/* route based on destination address */
	refresh_number_db();
	is_nanp = is_itn = 0;
	switch (grok_number_string(msg->called.number, 0)) {
	case 4:
		if (msg->called.type != GSM48_TON_UNKNOWN &&
		    msg->called.type != GSM48_TON_NET_SPEC &&
		    msg->called.type != GSM48_TON_SHORT_CODE)
			break;
		if (msg->called.plan != GSM48_NPI_UNKNOWN &&
		    msg->called.plan != GSM48_NPI_ISDN_E164 &&
		    msg->called.plan != GSM48_NPI_PRIVATE)
			break;
		res = lookup_short_dial_number(msg->called.number, nanp);
		if (!res) {
			syslog(LOG_ERR,
				"rejecting MO call 0x%x: invalid short number",
				msg->callref);
			reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
					GSM48_CC_CAUSE_UNASSIGNED_NR);
			call->gc_flag = 1;
			return;
		}
		if (nanp[0]) {
			is_nanp = 1;
			msg->called.type = GSM48_TON_INTERNATIONAL;
			msg->called.plan = GSM48_NPI_ISDN_E164;
			msg->called.number[0] = '1';
			strcpy(msg->called.number+1, nanp);
		} else {
			is_itn = 1;
			msg->called.type = GSM48_TON_NET_SPEC;
			msg->called.plan = GSM48_NPI_PRIVATE;
		}
		break;
	case 10:
		if (msg->called.type != GSM48_TON_UNKNOWN &&
		    msg->called.type != GSM48_TON_NATIONAL)
			break;
		if (msg->called.plan != GSM48_NPI_UNKNOWN &&
		    msg->called.plan != GSM48_NPI_ISDN_E164 &&
		    msg->called.plan != GSM48_NPI_NATIONAL)
			break;
		if (!is_nanp_valid_prefix(msg->called.number)) {
			syslog(LOG_ERR,
				"rejecting MO call 0x%x: invalid NANP number",
				msg->callref);
			reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
					GSM48_CC_CAUSE_INV_NR_FORMAT);
			call->gc_flag = 1;
			return;
		}
		is_nanp = 1;
		/* canonicalize to international format */
		bcopy(msg->called.number, msg->called.number+1, 11);
		msg->called.number[0] = '1';
		msg->called.type = GSM48_TON_INTERNATIONAL;
		msg->called.plan = GSM48_NPI_ISDN_E164;
		break;
	case 11:
		if (msg->called.type != GSM48_TON_UNKNOWN &&
		    msg->called.type != GSM48_TON_INTERNATIONAL)
			break;
		if (msg->called.plan != GSM48_NPI_UNKNOWN &&
		    msg->called.plan != GSM48_NPI_ISDN_E164)
			break;
		if (msg->called.number[0] != '1')
			break;
		if (!is_nanp_valid_prefix(msg->called.number+1)) {
			syslog(LOG_ERR,
				"rejecting MO call 0x%x: invalid NANP number",
				msg->callref);
			reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
					GSM48_CC_CAUSE_INV_NR_FORMAT);
			call->gc_flag = 1;
			return;
		}
		is_nanp = 1;
		/* canonicalize to international format */
		msg->called.type = GSM48_TON_INTERNATIONAL;
		msg->called.plan = GSM48_NPI_ISDN_E164;
		break;
	}
	is_local = is_itn;
	if (is_nanp && is_nanp_locally_owned(msg->called.number+1))
		is_local = 1;
	/* weed out attempts to call yourself */
	if (is_local && !strcmp(msg->calling.number, msg->called.number)) {
		syslog(LOG_ERR, "rejecting MO call 0x%x: call to self",
			msg->callref);
		reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_INCOMPAT_DEST);
		call->gc_flag = 1;
		return;
	}
	/* actually route the call */
	if (is_local) {
		internal_switch_mo_setup(call, msg);
		return;
	}
	/* outbound calls remain to be implemented */
	syslog(LOG_ERR, "rejecting MO call 0x%x: outbound not implemented",
		msg->callref);
	reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
			GSM48_CC_CAUSE_NO_ROUTE);
	call->gc_flag = 1;
}

static void
reject_mt_call(conn, callref, cause_loc, cause_val)
	struct socket_conn *conn;
	uint32_t callref;
{
	struct gsm_mncc msg;

	bzero(&msg, sizeof(struct gsm_mncc));
	msg.msg_type = MNCC_REJ_REQ;
	msg.callref = callref;
	mncc_set_cause(&msg, cause_loc, cause_val);
	mncc_signal_to_socket_nocall(conn, &msg);
}

void
process_ext_mtcall_setup(conn, msg)
	struct socket_conn *conn;
	struct gsm_mncc *msg;
{
	struct gsm_call *call;

	if (!(msg->fields & MNCC_F_CALLED) && !msg->imsi[0]) {
		syslog(LOG_ERR, "rejecting ext MT: no called number");
		reject_mt_call(conn, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_INVAL_MAND_INF);
		return;
	}
	call = create_new_mt_call();
	if (!call) {
		syslog(LOG_ERR, "rejecting ext MT: no memory for call");
		reject_mt_call(conn, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
		return;
	}
	call->socket = conn;
	call->socket_ref = msg->callref;
	conn->ncalls++;
	syslog(LOG_DEBUG, "mapped socket callref 0x%x to GSM callref 0x%x",
		msg->callref, call->callref);
	/* forward to GSM MNCC interface */
	msg->callref = call->callref;
	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
}

preen_connected_number(msg)
	struct gsm_mncc *msg;
{
	if (preen_msc_provided_number(&msg->connected))
		msg->fields |= MNCC_F_CONNECTED;
	else
		msg->fields &= ~MNCC_F_CONNECTED;
}