view mncc/call_setup.c @ 6:33d8b3177540

mtctest compiles in the new environment
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 09 Jun 2024 01:55:28 +0000
parents 847690ea7f4a
children
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 <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>

#include <themwi/nanp/number_db_v2.h>
#include <themwi/nanp/number_lookup.h>
#include <themwi/nanp/number_utils.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, false);
	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;
	const struct owned_number_rec *own;
	const struct short_number_rec *snum;
	int 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, false)) {
	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;
		snum = numdb_lookup_short(msg->called.number);
		if (!snum) {
			syslog(LOG_ERR,
			"rejecting MO call 0x%x: unassigned 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;
		}
		switch (snum->short_num_type) {
		case SHORT_NUM_TYPE_ABBREV:
			is_nanp = 1;
			msg->called.type = GSM48_TON_INTERNATIONAL;
			msg->called.plan = GSM48_NPI_ISDN_E164;
			sprintf(msg->called.number, "1%03u%03u%04u",
				snum->fullnum_prefix[0],
				snum->fullnum_prefix[1], snum->short_num);
			break;
		case SHORT_NUM_TYPE_ITN:
			is_itn = 1;
			msg->called.type = GSM48_TON_NET_SPEC;
			msg->called.plan = GSM48_NPI_PRIVATE;
			break;
		case SHORT_NUM_TYPE_TEST_SINK:
			syslog(LOG_ERR,
			"rejecting MO call 0x%x: test sink not implemented",
				msg->callref);
			reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
					GSM48_CC_CAUSE_DEST_OOO);
			call->gc_flag = 1;
			return;
		default:
			syslog(LOG_ERR,
		"rejecting MO call 0x%x: unknown short number type 0x%02X",
				msg->callref, snum->short_num_type);
			reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
					GSM48_CC_CAUSE_DEST_OOO);
			call->gc_flag = 1;
			return;
		}
		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 && (own = numdb_lookup_nanp(msg->called.number+1))) {
		is_local = 1;
		switch (own->usage & NUMBER_USAGE_MASK) {
		case NUMBER_USAGE_TYPE_GSM_SUB:
			break;
		case NUMBER_USAGE_TYPE_ALIAS:
			sprintf(msg->called.number, "1%03u%03u%04u",
				own->remap[0], own->remap[1], own->remap[2]);
			break;
		default:
			syslog(LOG_ERR,
				"rejecting MO call 0x%x: unassigned owned NANP",
				msg->callref);
			reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
					GSM48_CC_CAUSE_UNASSIGNED_NR);
			call->gc_flag = 1;
			return;
		}
	}
	/* 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;
}