diff mncc/call_setup.c @ 2:053f04687106

mncc: initial import from old ThemWi
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 08 Jun 2024 23:12:12 +0000
parents
children 847690ea7f4a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mncc/call_setup.c	Sat Jun 08 23:12:12 2024 +0000
@@ -0,0 +1,276 @@
+/*
+ * 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 "../include/number_db_v2.h"
+#include "../libnumdb2/lookup_func.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;
+	struct owned_number_rec *own;
+	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, 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;
+		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;
+}