view mtctest/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 74dae755f85a
children
line wrap: on
line source

/*
 * In this module we compose the MNCC_SETUP_REQ message
 * initiating our test MT call.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <ctype.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "../include/mncc.h"
#include "../include/gsm48_const.h"
#include "../include/number_db_v2.h"
#include "../libnumdb2/lookup_func.h"

struct gsm_mncc setup_msg;

static void
set_called_number(arg)
	char *arg;
{
	int rc, ndig;
	char short_num[5], long_num[12];
	struct short_number_rec *snum;

	if (!strncmp(arg, "imsi:", 5)) {
		rc = grok_imsi_user_arg(arg, setup_msg.imsi);
		if (rc < 0) {
			fprintf(stderr,
			"error: call-by-IMSI destination \"%s\" is invalid\n",
				arg);
			exit(1);
		}
		return;
	}
	if (arg[0] == '+') {
		if (arg[1] != '1') {
			fprintf(stderr,
	"error: plus-format call destination number must begin with 1\n");
			exit(1);
		}
		if (grok_number_string(arg+1, 1) != 11) {
bad_plus1:		fprintf(stderr,
			"error: malformed +1 call destination number\n");
			exit(1);
		}
		dehyphen_number_string(arg+1, setup_msg.called.number);
		if (!is_nanp_valid_prefix(setup_msg.called.number+1))
			goto bad_plus1;
		setup_msg.called.type = GSM48_TON_INTERNATIONAL;
		setup_msg.called.plan = GSM48_NPI_ISDN_E164;
		setup_msg.fields |= MNCC_F_CALLED;
		return;
	}
	ndig = grok_number_string(arg, 1);
	switch (ndig) {
	case 4:
		dehyphen_number_string(arg, short_num);
		if (read_number_db() < 0) {
			fprintf(stderr, "error reading number database\n");
			exit(1);
		}
		snum = numdb_lookup_short(short_num);
		if (!snum) {
			fprintf(stderr,
				"error: short dial number %s is not valid\n",
				short_num);
			exit(1);
		}
		switch (snum->short_num_type) {
		case SHORT_NUM_TYPE_ABBREV:
			setup_msg.called.type = GSM48_TON_INTERNATIONAL;
			setup_msg.called.plan = GSM48_NPI_ISDN_E164;
			sprintf(setup_msg.called.number, "1%03u%03u%04u",
				snum->fullnum_prefix[0],
				snum->fullnum_prefix[1], snum->short_num);
			break;
		case SHORT_NUM_TYPE_ITN:
			setup_msg.called.type = GSM48_TON_NET_SPEC;
			setup_msg.called.plan = GSM48_NPI_PRIVATE;
			strcpy(setup_msg.called.number, short_num);
			break;
		default:
			fprintf(stderr,
			"error: short dial number %s is not abbrev or ITN\n",
				short_num);
			exit(1);
		}
		setup_msg.fields |= MNCC_F_CALLED;
		return;
	case 10:
		dehyphen_number_string(arg, long_num);
		if (!is_nanp_valid_prefix(long_num))
			break;
		setup_msg.called.type = GSM48_TON_INTERNATIONAL;
		setup_msg.called.plan = GSM48_NPI_ISDN_E164;
		setup_msg.called.number[0] = '1';
		strcpy(setup_msg.called.number+1, long_num);
		setup_msg.fields |= MNCC_F_CALLED;
		return;
	case 11:
		dehyphen_number_string(arg, long_num);
		if (long_num[0] != '1')
			break;
		if (!is_nanp_valid_prefix(long_num+1))
			break;
		setup_msg.called.type = GSM48_TON_INTERNATIONAL;
		setup_msg.called.plan = GSM48_NPI_ISDN_E164;
		strcpy(setup_msg.called.number, long_num);
		setup_msg.fields |= MNCC_F_CALLED;
		return;
	}
	fprintf(stderr, "error: call destination number \"%s\" is invalid\n",
		arg);
	exit(1);
}

static void
set_calling_number(arg)
	char *arg;
{
	unsigned ndig;
	int c;

	if (!strcmp(arg, "unavail")) {
		setup_msg.calling.present = GSM48_PRES_UNAVAIL;
		return;
	}
	if (!strcmp(arg, "blocked")) {
		setup_msg.calling.present = GSM48_PRES_RESTR;
		return;
	}
	if (*arg == '+') {
		setup_msg.calling.type = GSM48_TON_INTERNATIONAL;
		arg++;
	}
	for (ndig = 0; *arg; ) {
		c = *arg++;
		if (c == ',')
			break;
		if (c == '-')
			continue;
		if (!is_valid_ext_digit(c)) {
			fprintf(stderr,
	"error: calling number argument contains invalid digit \'%c\'\n",
				c);
			exit(1);
		}
		if (ndig >= 32) {
			fprintf(stderr,
				"error: calling number argument is too long\n");
			exit(1);
		}
		setup_msg.calling.number[ndig] = c;
		ndig++;
	}
	if (!ndig) {
		fprintf(stderr,
			"error: calling number argument has no digits\n");
		exit(1);
	}
	setup_msg.calling.plan = GSM48_NPI_ISDN_E164;
	for (;;) {
		while (*arg == ',')
			arg++;
		if (!*arg)
			return;
		if (!strncmp(arg, "ton=", 4)) {
			arg += 4;
			if (arg[0] >= '0' && arg[0] <= '7' && !isdigit(arg[1])){
				setup_msg.calling.type = *arg - '0';
				arg++;
			} else {
				fprintf(stderr,
		"error: calling number argument contains invalid ton= part\n");
				exit(1);
			}
		} else if (!strncmp(arg, "npi=", 4)) {
			arg += 4;
			if (arg[0] >= '0' && arg[0] <= '9' && !isdigit(arg[1])){
				setup_msg.calling.plan = *arg - '0';
				arg++;
			} else if (arg[0] == '1' && arg[1] >= '0' &&
				   arg[1] <= '5' && !isdigit(arg[2])) {
				setup_msg.calling.plan = atoi(arg);
				arg += 2;
			} else {
				fprintf(stderr,
		"error: calling number argument contains invalid npi= part\n");
				exit(1);
			}
		} else if (!strncmp(arg, "restr", 5)) {
			arg += 5;
			setup_msg.calling.present = GSM48_PRES_RESTR;
		} else if (!strncmp(arg, "vp", 2)) {
			arg += 2;
			setup_msg.calling.screen = GSM48_SCRN_USER_PASS;
		} else if (!strncmp(arg, "vf", 2)) {
			arg += 2;
			setup_msg.calling.screen = GSM48_SCRN_USER_FAIL;
		} else if (!strncmp(arg, "net", 3)) {
			arg += 3;
			setup_msg.calling.screen = GSM48_SCRN_NETWORK;
		} else {
inv_qual:		fprintf(stderr,
		"error: calling number argument contains invalid qualifier\n");
			exit(1);
		}
		if (!*arg)
			return;
		if (*arg != ',')
			goto inv_qual;
	}
}

void
init_setup_msg(from, to)
	char *from, *to;
{
	setup_msg.msg_type = MNCC_SETUP_REQ;
	setup_msg.callref = 1;
	set_called_number(to);
	if (from) {
		set_calling_number(from);
		setup_msg.fields |= MNCC_F_CALLING;
	}
}

void
send_setup_msg()
{
	if (setup_msg.imsi[0])
		printf("Calling IMSI %s\n", setup_msg.imsi);
	else
		printf("Calling %s%s\n",
			setup_msg.called.type == GSM48_TON_INTERNATIONAL ? "+"
									 : "",
			setup_msg.called.number);
	send_mncc_to_gsm(&setup_msg, sizeof(struct gsm_mncc));
}