view mncc/call_setup.c @ 167:2ebad02adbe5

themwi-mncc: route outbound calls to themwi-sip-out
author Mychaela Falconia <falcon@freecalypso.org>
date Wed, 12 Oct 2022 18:08:34 -0800
parents 660126bd5f59
children 59a166c50d0e
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);
	else
		outbound_mo_setup(call, msg);
}

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