view sip-out/call_setup.c @ 267:81958b35f74d

NANP validation: allow made-up area codes of N9X form
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 13 Nov 2023 15:28:09 -0800
parents 556cd78f750a
children
line wrap: on
line source

/*
 * In this module we implement our initial handling of MNCC_SETUP_IND.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.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/out_routes.h"
#include "call.h"

extern struct call *find_call_by_mncc_callref();

extern int block_1900_numbers;
extern struct call *call_list;

static void
reject_mo_call(mncc, callref, cause_loc, cause_val)
	struct mncc_conn *mncc;
	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_sock(mncc, &msg, sizeof(struct gsm_mncc));
}

static void
send_call_proceeding(call)
	struct call *call;
{
	struct gsm_mncc msg;

	bzero(&msg, sizeof(struct gsm_mncc));
	msg.msg_type = MNCC_CALL_PROC_REQ;
	msg.callref = call->mncc_callref;
	send_mncc_to_sock(call->mncc, &msg, sizeof(struct gsm_mncc));
}

void
handle_setup_ind(mncc, msg, msglen)
	struct mncc_conn *mncc;
	struct gsm_mncc *msg;
	unsigned msglen;
{
	struct call *call;
	struct sip_out_dest *dest;
	struct special_num_route *special_rt;
	char to_sip_user[MAX_SIP_USER_PART+1];
	int rc, prefix_len;

	if (msglen != sizeof(struct gsm_mncc)) {
		syslog(LOG_CRIT,
			"FATAL: Rx MNCC_SETUP_IND has wrong length");
		exit(1);
	}
	/* check for duplicates */
	call = find_call_by_mncc_callref(mncc, msg->callref);
	if (call) {
		syslog(LOG_ERR, "duplicate MNCC_SETUP_IND for callref 0x%x",
			msg->callref);
		/* drop it like OsmoMSC's mncc_builtin does */
		return;
	}
	if (!(msg->fields & MNCC_F_CALLED) ||
	    !(msg->fields & MNCC_F_BEARER_CAP)) {
		syslog(LOG_ERR, "rejecting MO call 0x%x: missing mandatory IE",
			msg->callref);
		reject_mo_call(mncc, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_INVAL_MAND_INF);
		return;
	}
	/* route based on destination address */
	refresh_out_routes_db();
	if (msg->called.type == GSM48_TON_INTERNATIONAL) {
		rc = grok_number_string(msg->called.number, 0);
		if (rc < 7 || rc > MAX_E164_NUMBER) {
inv_nr_format:		reject_mo_call(mncc, msg->callref,
					GSM48_CAUSE_LOC_PRN_S_LU,
					GSM48_CC_CAUSE_INV_NR_FORMAT);
			return;
		}
		if (msg->called.number[0] == '0')
			goto inv_nr_format;
		if (msg->called.number[0] == '1') {
			if (rc != 11)
				goto inv_nr_format;
			if (!is_nanp_valid_prefix(msg->called.number+1))
				goto inv_nr_format;
			if (msg->called.number[1] == '9' &&
			    msg->called.number[2] == '0' &&
			    msg->called.number[3] == '0' &&
			    block_1900_numbers) {
call_barred:			reject_mo_call(mncc, msg->callref,
						GSM48_CAUSE_LOC_PRN_S_LU,
						GSM48_CC_CAUSE_OP_DET_BARRING);
				return;
			}
		}
		rc = route_e164_number(msg->called.number, &dest, &prefix_len);
		if (!rc) {
no_route_to_dest:	reject_mo_call(mncc, msg->callref,
					GSM48_CAUSE_LOC_PRN_S_LU,
					GSM48_CC_CAUSE_NO_ROUTE);
			return;
		}
		/* made-up N9X area codes require explicit routes */
		if (msg->called.number[0] == '1' &&
		    msg->called.number[2] == '9' && prefix_len < 4)
			goto no_route_to_dest;
		to_sip_user[0] = '+';
		strcpy(to_sip_user+1, msg->called.number);
		special_rt = 0;
	} else {
		rc = route_special_number(msg->called.number, &dest,
					  &special_rt);
		if (!rc)
			goto no_route_to_dest;
		strcpy(to_sip_user, special_rt->sip_user);
	}
	/* validate From number */
	if (!(msg->fields & MNCC_F_CALLING))
		goto call_barred;
	if (msg->calling.type != GSM48_TON_INTERNATIONAL)
		goto call_barred;
	if (grok_number_string(msg->calling.number, 0) != 11 ||
	    msg->calling.number[0] != '1')
		goto call_barred;
	/* speech-only restriction */
	if (msg->bearer_cap.transfer != GSM48_BCAP_ITCAP_SPEECH ||
	    msg->bearer_cap.mode != GSM48_BCAP_TMOD_CIRCUIT) {
		syslog(LOG_ERR, "rejecting MO call 0x%x: bad bearer cap",
			msg->callref);
		reject_mo_call(mncc, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
		return;
	}
	/* E911 special handling */
	if (special_rt && special_rt->flags & SPECIAL_NUM_FLAG_E911) {
		rc = e911_call_preen(msg);
		if (!rc)
			goto call_barred;
	}
	/* TMGW must be up and running */
	rc = connect_tmgw_socket();
	if (rc < 0) {
		reject_mo_call(mncc, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_NETWORK_OOO);
		return;
	}
	/* allocate struct call and being stateful processing */
	call = malloc(sizeof(struct call));
	if (!call) {
		syslog(LOG_CRIT, "failed malloc for outbound call!");
		reject_mo_call(mncc, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
		return;
	}
	bzero(call, sizeof(struct call));
	call->mncc = mncc;
	call->mncc_callref = msg->callref;
	call->from_user[0] = '+';
	strcpy(call->from_user+1, msg->calling.number);
	sprintf(call->to_uri, "sip:%s@%s", to_sip_user, dest->domain);
	bcopy(&dest->sin, &call->udp_sin, sizeof(struct sockaddr_in));
	call->next = call_list;
	call_list = call;
	send_call_proceeding(call);
	call->overall_state = OVERALL_STATE_GSM_RTP;
	call->mncc_state = MNCC_STATE_MO_PROC;
	syslog(LOG_INFO, "Outbound call from %s to %s", call->from_user,
		call->to_uri);
}