changeset 5:e7b192a5dee5

mtctest: initial import from old ThemWi
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 09 Jun 2024 00:58:38 +0000
parents ce450869db09
children 33d8b3177540
files mtctest/disconnect.c mtctest/main.c mtctest/setup.c mtctest/sig_handler.c mtctest/sock_conn.c
diffstat 5 files changed, 818 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mtctest/disconnect.c	Sun Jun 09 00:58:38 2024 +0000
@@ -0,0 +1,33 @@
+/*
+ * In this module we implement the sending of MNCC_DISC_REQ,
+ * signaling a caller-requested disconnect aka hang-up.
+ * It is the graceful way of signaling disconnect,
+ * as opposed to simply killing the test call process with ^C
+ * and letting themwi-mncc handle the broken 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 "../include/mncc.h"
+#include "../include/gsm48_const.h"
+
+int disconnect_mode;
+
+send_disconnect_req()
+{
+	struct gsm_mncc msg;
+
+	printf("Sending disconnect request\n");
+	bzero(&msg, sizeof(struct gsm_mncc));
+	msg.msg_type = MNCC_DISC_REQ;
+	msg.callref = 1;
+	mncc_set_cause(&msg, GSM48_CAUSE_LOC_USER,
+			GSM48_CC_CAUSE_NORM_CALL_CLEAR);
+	send_mncc_to_gsm(&msg, sizeof(struct gsm_mncc));
+	disconnect_mode = 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mtctest/main.c	Sun Jun 09 00:58:38 2024 +0000
@@ -0,0 +1,78 @@
+/*
+ * Main module for ThemWi test MT call generator.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+
+extern int mtc_socket;
+extern int disconnect_mode;
+
+struct timeval cur_event_time;
+
+static void
+drain_stdin()
+{
+	char buf[256];
+
+	read(0, buf, sizeof buf);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	extern char *optarg;
+	char *from;
+	fd_set fds;
+	int c;
+
+	from = 0;
+	while ((c = getopt(argc, argv, "f:")) != EOF) {
+		switch (c) {
+		case 'f':
+			from = optarg;
+			continue;
+		default:
+		usage:
+			fprintf(stderr,
+				"usage: %s [-f from-number] to-number\n",
+				argv[0]);
+			exit(1);
+		}
+	}
+	if (argc != optind + 1)
+		goto usage;
+	openlog("themwi-test-mtc", 0, LOG_LOCAL5);
+	init_setup_msg(from, argv[optind]);
+	obtain_dummy_rtp();
+	connect_mtc_socket();
+	send_setup_msg();
+	/* main select loop */
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(mtc_socket, &fds);
+		if (!disconnect_mode)
+			FD_SET(0, &fds);
+		c = select(mtc_socket+1, &fds, 0, 0, 0);
+		if (c < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			exit(1);
+		}
+		gettimeofday(&cur_event_time, 0);
+		if (FD_ISSET(mtc_socket, &fds))
+			mtc_socket_select();
+		if (!disconnect_mode && FD_ISSET(0, &fds)) {
+			drain_stdin();
+			send_disconnect_req();
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mtctest/setup.c	Sun Jun 09 00:58:38 2024 +0000
@@ -0,0 +1,243 @@
+/*
+ * 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));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mtctest/sig_handler.c	Sun Jun 09 00:58:38 2024 +0000
@@ -0,0 +1,403 @@
+/*
+ * In this module we handle all incoming messages from MNCC,
+ * printing all of them and generating protocol-required responses
+ * for some.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.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"
+
+extern int disconnect_mode;
+extern struct sockaddr_storage dummy_rtp_endp;
+extern struct timeval cur_event_time;
+
+static void
+print_bearer_cap(bcap)
+	struct gsm_mncc_bearer_cap *bcap;
+{
+	int i, sv;
+
+	printf("Bearer cap: itcap=%d tmod=%d coding=%d rrq=%d\n",
+		bcap->transfer, bcap->mode, bcap->coding, bcap->radio);
+	printf("  speech: CTM=%d sv", bcap->speech_ctm);
+	for (i = 0; i < 8; i++) {
+		sv = bcap->speech_ver[i];
+		if (sv < 0)
+			break;
+		printf(" %d", sv);
+	}
+	putchar('\n');
+	printf("  data: ra=%d sig=%d async=%d nstop=%d ndata=%d\n",
+		bcap->data.rate_adaption, bcap->data.sig_access,
+		bcap->data.async, bcap->data.nr_stop_bits,
+		bcap->data.nr_data_bits);
+	printf("  urate=%d par=%d irate=%d transp=%d mtype=%d\n",
+		bcap->data.user_rate, bcap->data.parity,
+		bcap->data.interm_rate, bcap->data.transp,
+		bcap->data.modem_type);
+}
+
+static void
+print_cc_cap(cc)
+	struct gsm_mncc_cccap *cc;
+{
+	printf("CC capabilities: DTMF=%d PCP=%d\n", cc->dtmf, cc->pcp);
+}
+
+static void
+print_cause(cause)
+	struct gsm_mncc_cause *cause;
+{
+	int i;
+
+	printf("Cause: loc=%d coding=%d value=%d", cause->location,
+		cause->coding, cause->value);
+	if (cause->rec)
+		printf(" rec=0x%02X", cause->rec_val);
+	for (i = 0; i < cause->diag_len; i++) {
+		if (!(i & 15)) {
+			putchar('\n');
+			putchar(' ');
+		}
+		printf(" %02X", cause->diag[i] & 0xFF);
+	}
+	putchar('\n');
+}
+
+static void
+print_progress(prog)
+	struct gsm_mncc_progress *prog;
+{
+	printf("Progress: loc=%d coding=%d descr=0x%02X", prog->location,
+		prog->coding, prog->descr);
+}
+
+static void
+print_useruser(uu)
+	struct gsm_mncc_useruser *uu;
+{
+	printf("User-User IE: proto=0x%02X\n", uu->proto);
+	/* dump to be implemented if and when we actually get a UU somewhere */
+}
+
+static void
+print_keypad(kp)
+	int kp;
+{
+	if (kp >= '!' && kp <= '~')
+		printf("Keypad code: %c\n", kp);
+	else
+		printf("Keypad code: 0x%02X\n", kp);
+}
+
+static void
+print_facility(fac)
+	struct gsm_mncc_facility *fac;
+{
+	int i;
+
+	printf("Facility IE: %d byte(s)", fac->len);
+	for (i = 0; i < fac->len; i++) {
+		if (!(i & 15)) {
+			putchar('\n');
+			putchar(' ');
+		}
+		printf(" %02X", fac->info[i] & 0xFF);
+	}
+	putchar('\n');
+}
+
+static void
+print_ssver(ssv)
+	struct gsm_mncc_ssversion *ssv;
+{
+	int i;
+
+	printf("SS version IE: %d byte(s)", ssv->len);
+	for (i = 0; i < ssv->len; i++) {
+		if (!(i & 15)) {
+			putchar('\n');
+			putchar(' ');
+		}
+		printf(" %02X", ssv->info[i] & 0xFF);
+	}
+	putchar('\n');
+}
+
+static void
+print_fields(msg)
+	struct gsm_mncc *msg;
+{
+	if (msg->fields & MNCC_F_BEARER_CAP)
+		print_bearer_cap(&msg->bearer_cap);
+	if (msg->fields & MNCC_F_CCCAP)
+		print_cc_cap(&msg->cccap);
+	if (msg->fields & MNCC_F_CAUSE)
+		print_cause(&msg->cause);
+	if (msg->fields & MNCC_F_PROGRESS)
+		print_progress(&msg->progress);
+	if (msg->fields & MNCC_F_USERUSER)
+		print_useruser(&msg->useruser);
+	if (msg->more)
+		printf("More data flag set\n");
+	if (msg->fields & MNCC_F_KEYPAD)
+		print_keypad(msg->keypad);
+	if (msg->fields & MNCC_F_FACILITY)
+		print_facility(&msg->facility);
+	if (msg->fields & MNCC_F_SSVERSION)
+		print_ssver(&msg->ssversion);
+}
+
+static void
+print_sdp(sdp)
+	char *sdp;
+{
+	char *cp, *ep, *np;
+
+	for (cp = sdp; *cp; cp = np) {
+		ep = index(cp, '\n');
+		if (ep) {
+			*ep = '\0';
+			np = ep + 1;
+		} else
+			np = 0;
+		ep = index(cp, '\r');
+		if (ep)
+			*ep = '\0';
+		printf("%s %s\n", cp == sdp ? "SDP:" : "    ", cp);
+	}
+}
+
+static void
+send_connect_ack()
+{
+	struct gsm_mncc ack;
+
+	printf("Sending connect ack\n");
+	bzero(&ack, sizeof(struct gsm_mncc));
+	ack.msg_type = MNCC_SETUP_COMPL_REQ;
+	ack.callref = 1;
+	send_mncc_to_gsm(&ack, sizeof(struct gsm_mncc));
+}
+
+static void
+send_rtp_connect()
+{
+	struct gsm_mncc_rtp rtp;
+
+	printf("Sending MNCC_RTP_CONNECT\n");
+	bzero(&rtp, sizeof(struct gsm_mncc_rtp));
+	rtp.msg_type = MNCC_RTP_CONNECT;
+	rtp.callref = 1;
+	bcopy(&dummy_rtp_endp, &rtp.addr, sizeof(struct sockaddr_storage));
+	send_mncc_to_gsm(&rtp, sizeof(struct gsm_mncc_rtp));
+}
+
+static void
+handle_dtmf_time()
+{
+	static struct timeval last_dtmf_time;
+	struct timeval delta;
+	unsigned ms;
+
+	if (timerisset(&last_dtmf_time) &&
+	    timercmp(&cur_event_time, &last_dtmf_time, >)) {
+		timersub(&cur_event_time, &last_dtmf_time, &delta);
+		if (delta.tv_sec >= 2)
+			printf("Time since last DTMF event: %u s\n",
+				(unsigned) delta.tv_sec);
+		else {
+			ms = delta.tv_sec * 1000 + delta.tv_usec / 1000;
+			printf("Time since last DTMF event: %u ms\n", ms);
+		}
+	}
+	bcopy(&cur_event_time, &last_dtmf_time, sizeof(struct timeval));
+}
+
+static void
+handle_signaling_msg(msg, msglen)
+	struct gsm_mncc *msg;
+	unsigned msglen;
+{
+	if (msglen != sizeof(struct gsm_mncc)) {
+		fprintf(stderr,
+			"error: Rx MNCC message type 0x%x has wrong length\n",
+			msg->msg_type);
+		exit(1);
+	}
+	if (msg->callref != 1) {
+		fprintf(stderr,
+	"error: Rx MNCC message type 0x%x has unexpected callref 0x%x\n",
+			msg->msg_type, msg->callref);
+		exit(1);
+	}
+	switch (msg->msg_type) {
+	case MNCC_SETUP_CNF:
+		printf("MNCC_SETUP_CNF: call is answered\n");
+		print_fields(msg);
+		print_sdp(msg->sdp);
+		send_rtp_connect();
+		send_connect_ack();
+		return;
+	case MNCC_CALL_CONF_IND:
+		printf("MNCC_CALL_CONF_IND: call is confirmed\n");
+		print_fields(msg);
+		print_sdp(msg->sdp);
+		return;
+	case MNCC_ALERT_IND:
+		printf("MNCC_ALERT_IND: call is alerting\n");
+		print_fields(msg);
+		print_sdp(msg->sdp);
+		return;
+	case MNCC_NOTIFY_IND:
+		printf("NNCC_NOTIFY_IND: NOTIFY byte from MS: 0x%02X\n",
+			msg->notify);
+		return;
+	case MNCC_DISC_IND:
+		printf("MNCC_DISC_IND: MS initiates disconnect\n");
+		print_fields(msg);
+		disconnect_mode = 1;
+		printf("Responding with release request\n");
+		msg->msg_type = MNCC_REL_REQ;
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	case MNCC_FACILITY_IND:
+		printf("MNCC_FACILITY_IND: call-related SS from MS\n");
+		print_fields(msg);
+		return;
+	case MNCC_START_DTMF_IND:
+		printf("MNCC_START_DTMF_IND: MS sending DTMF start\n");
+		print_fields(msg);
+		handle_dtmf_time();
+		if (msg->fields & MNCC_F_KEYPAD &&
+		    is_valid_dtmf_digit(msg->keypad)) {
+			printf("Responding with ACK\n");
+			msg->msg_type = MNCC_START_DTMF_RSP;
+		} else {
+			printf("Responding with Reject\n");
+			msg->msg_type = MNCC_START_DTMF_REJ;
+			mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
+					GSM48_CC_CAUSE_INVAL_MAND_INF);
+		}
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	case MNCC_STOP_DTMF_IND:
+		printf("MNCC_STOP_DTMF_IND: MS sending DTMF stop\n");
+		handle_dtmf_time();
+		msg->msg_type = MNCC_STOP_DTMF_RSP;
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	case MNCC_MODIFY_IND:
+		printf("MNCC_MODIFY_IND: MS requests call modification\n");
+		print_fields(msg);
+		msg->msg_type = MNCC_MODIFY_REJ;
+		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	case MNCC_HOLD_IND:
+		printf("MNCC_HOLD_IND: MS requests call hold\n");
+		msg->msg_type = MNCC_HOLD_CNF;
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	case MNCC_RETRIEVE_IND:
+		printf("MNCC_RETRIEVE_IND: MS requests call retrieve\n");
+		send_rtp_connect();
+		msg->msg_type = MNCC_RETRIEVE_CNF;
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	case MNCC_USERINFO_IND:
+		printf("MNCC_USERINFO_IND: user-user info\n");
+		print_fields(msg);
+		return;
+	case MNCC_REL_IND:
+		printf("MNCC_REL_IND: final release\n");
+		print_fields(msg);
+		exit(0);
+	case MNCC_REL_CNF:
+		printf("MNCC_REL_CNF: final release in response to request\n");
+		print_fields(msg);
+		exit(0);
+	case MNCC_REJ_IND:
+		printf("MNCC_REJ_IND: MT call rejected\n");
+		print_fields(msg);
+		exit(0);
+	}
+}
+
+static void
+handle_rtp_msg(msg, msglen)
+	struct gsm_mncc_rtp *msg;
+	unsigned msglen;
+{
+	if (msglen != sizeof(struct gsm_mncc_rtp)) {
+		fprintf(stderr,
+			"error: Rx MNCC message type 0x%x has wrong length\n",
+			msg->msg_type);
+		exit(1);
+	}
+	if (msg->callref != 1) {
+		fprintf(stderr,
+	"error: Rx MNCC message type 0x%x has unexpected callref 0x%x\n",
+			msg->msg_type, msg->callref);
+		exit(1);
+	}
+	switch (msg->msg_type) {
+	case MNCC_RTP_CREATE:
+		printf("MNCC_RTP_CREATE: RTP info from MSC\n");
+		printf("payload_type=0x%x payload_msg_type=0x%x\n",
+			msg->payload_type, msg->payload_msg_type);
+		print_sdp(msg->sdp);
+		return;
+	case MNCC_RTP_CONNECT:
+		printf("MNCC_RTP_CONNECT: error response\n");
+		return;
+	case MNCC_RTP_FREE:
+		printf("MNCC_RTP_FREE: bogon\n");
+		return;
+	}
+}
+
+void
+msg_from_mncc(msg, msglen)
+	union mncc_msg *msg;
+	unsigned msglen;
+{
+	switch (msg->msg_type) {
+	case MNCC_SETUP_CNF:
+	case MNCC_CALL_CONF_IND:
+	case MNCC_ALERT_IND:
+	case MNCC_NOTIFY_IND:
+	case MNCC_DISC_IND:
+	case MNCC_FACILITY_IND:
+	case MNCC_START_DTMF_IND:
+	case MNCC_STOP_DTMF_IND:
+	case MNCC_MODIFY_IND:
+	case MNCC_HOLD_IND:
+	case MNCC_RETRIEVE_IND:
+	case MNCC_USERINFO_IND:
+	case MNCC_REL_IND:
+	case MNCC_REL_CNF:
+	case MNCC_REJ_IND:
+		handle_signaling_msg(msg, msglen);
+		return;
+	case MNCC_RTP_CREATE:
+	case MNCC_RTP_CONNECT:
+	case MNCC_RTP_FREE:
+		handle_rtp_msg(msg, msglen);
+		return;
+	default:
+		fprintf(stderr,
+			"error: received unexpected MNCC message type 0x%x\n",
+			msg->msg_type);
+		exit(1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mtctest/sock_conn.c	Sun Jun 09 00:58:38 2024 +0000
@@ -0,0 +1,61 @@
+/*
+ * In this module we implement our connection to the MNCC daemon's
+ * mtcall socket.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "../include/mncc.h"
+
+static char mtcall_socket_pathname[] = "/var/gsm/mtcall_socket";
+
+int mtc_socket;
+
+connect_mtc_socket()
+{
+	struct sockaddr_un sa;
+	unsigned sa_len;
+	int rc;
+
+	mtc_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (mtc_socket < 0) {
+		perror("socket(AF_UNIX, SOCK_SEQPACKET, 0)");
+		exit(1);
+	}
+	fill_sockaddr_un(mtcall_socket_pathname, &sa, &sa_len);
+	rc = connect(mtc_socket, (struct sockaddr *) &sa, sa_len);
+	if (rc < 0) {
+		perror(mtcall_socket_pathname);
+		exit(1);
+	}
+	return(0);
+}
+
+void
+mtc_socket_select()
+{
+	union mncc_msg msg;
+	int rc;
+
+	rc = recv(mtc_socket, &msg, sizeof msg, 0);
+	if (rc < 0) {
+		perror("read from socket");
+		exit(1);
+	}
+	if (rc < 4) {
+		fprintf(stderr, "short read from socket: %d bytes\n", rc);
+		exit(1);
+	}
+	msg_from_mncc(&msg, rc);
+}
+
+send_mncc_to_gsm(msg, msglen)
+	union mncc_msg *msg;
+	unsigned msglen;
+{
+	return send(mtc_socket, msg, msglen, 0);
+}