view mncc/extsock.c @ 97:9aed16c30622

mgw p2g: set M bit when restarting forwarding after no-forward
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 25 Sep 2022 20:05:02 -0800
parents 660126bd5f59
children
line wrap: on
line source

/*
 * In this module we gather functions that deal with external
 * socket connections, both externally-originating MT calls
 * and outbound MO calls.
 */

#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 "struct.h"
#include "gsm_call.h"

extern char *mncc_msg_name();

void
extsock_dec_refcount(conn)
	struct socket_conn *conn;
{
	if (!conn->ncalls) {
		syslog(LOG_CRIT, "FATAL BUG: ncalls=0 on socket call clearing");
		exit(1);
	}
	conn->ncalls--;
}

static void
send_rel_on_broken_socket(call)
	struct gsm_call *call;
{
	struct gsm_mncc msg;

	bzero(&msg, sizeof(struct gsm_mncc));
	msg.msg_type = MNCC_REL_REQ;
	msg.callref = call->callref;
	mncc_set_cause(&msg, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_DEST_OOO);
	send_mncc_to_gsm(&msg, sizeof(struct gsm_mncc));
}

static void
broken_socket_clear_calls(conn)
	struct socket_conn *conn;
{
	extern struct gsm_call *call_list_head;
	struct gsm_call *call;

	for (call = call_list_head; call; call = call->next) {
		if (call->gc_flag)
			continue;
		if (call->socket == conn) {
			syslog(LOG_WARNING,
	"clearing broken socket call: GSM callref 0x%x, socket ref 0x%x",
				call->callref, call->socket_ref);
			send_rel_on_broken_socket(call);
			extsock_dec_refcount(conn);
			call->gc_flag = 1;
		}
	}
	if (conn->ncalls) {
		syslog(LOG_CRIT,
		    "FATAL BUG: ncalls!=0 after broken socket call clearing");
		exit(1);
	}
}

static void
report_runt(msg)
	union mncc_msg *msg;
{
	syslog(LOG_CRIT,
		"MNCC message type 0x%x from ThemWi call socket is too short!",
		msg->msg_type);
}

static void
handle_setup_req(conn, msg, msglen)
	struct socket_conn *conn;
	struct gsm_mncc *msg;
	unsigned msglen;
{
	struct gsm_call *call;

	if (msglen < sizeof(struct gsm_mncc)) {
		report_runt(msg);
		return;
	}
	syslog(LOG_DEBUG, "Rx MNCC_SETUP_REQ from socket, callref=0x%x",
		msg->callref);
	call = find_socket_call(conn, msg->callref);
	if (call) {
		syslog(LOG_ERR,
			"duplicate MNCC_SETUP_REQ from socket for callref 0x%x",
			msg->callref);
		/* drop it like OsmoMSC's mncc_builtin does */
		return;
	}
	/* further processing */
	process_ext_mtcall_setup(conn, msg);
}

static void
handle_signaling_msg(conn, msg, msglen)
	struct socket_conn *conn;
	struct gsm_mncc *msg;
	unsigned msglen;
{
	struct gsm_call *call;

	if (msglen < sizeof(struct gsm_mncc)) {
		report_runt(msg);
		return;
	}
	syslog(LOG_DEBUG, "Rx %s from socket, callref=0x%x",
		mncc_msg_name(msg->msg_type), msg->callref);
	call = find_socket_call(conn, msg->callref);
	if (!call) {
		syslog(LOG_ERR,
		"MNCC message from ThemWi call socket: callref 0x%x not found",
			msg->callref);
		/* drop it like OsmoMSC's mncc_builtin does */
		return;
	}
	/* forward to GSM MNCC interface */
	msg->callref = call->callref;
	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
	if (msg->msg_type == MNCC_REJ_REQ) {
		extsock_dec_refcount(conn);
		call->gc_flag = 1;
	}
}

static void
handle_rtp_msg(conn, msg, msglen)
	struct socket_conn *conn;
	struct gsm_mncc_rtp *msg;
	unsigned msglen;
{
	struct gsm_call *call;

	if (msglen < sizeof(struct gsm_mncc_rtp)) {
		report_runt(msg);
		return;
	}
	syslog(LOG_DEBUG, "Rx %s from socket, callref=0x%x",
		mncc_msg_name(msg->msg_type), msg->callref);
	call = find_socket_call(conn, msg->callref);
	if (!call) {
		syslog(LOG_ERR,
		"MNCC message from ThemWi call socket: callref 0x%x not found",
			msg->callref);
		/* drop it like OsmoMSC's mncc_builtin does */
		return;
	}
	/* forward to GSM MNCC interface */
	msg->callref = call->callref;
	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc_rtp));
}

void
extsock_read_select(conn)
	struct socket_conn *conn;
{
	union mncc_msg msg;
	int rc;

	rc = recv(conn->fd, &msg, sizeof msg, 0);
	if (rc < 4) {
		if (conn->ncalls) {
			syslog(LOG_ERR, "ext socket broken with calls present");
			broken_socket_clear_calls(conn);
		} else
			syslog(LOG_INFO, "normal closing of ext socket");
		close(conn->fd);
		conn->fd = -1;
		return;
	}
	switch (msg.msg_type) {
	case MNCC_SETUP_REQ:
		handle_setup_req(conn, &msg, rc);
		return;
	case MNCC_SETUP_RSP:
	case MNCC_SETUP_COMPL_REQ:
	case MNCC_CALL_PROC_REQ:
	case MNCC_PROGRESS_REQ:
	case MNCC_ALERT_REQ:
	case MNCC_NOTIFY_REQ:
	case MNCC_DISC_REQ:
	case MNCC_REL_REQ:
	case MNCC_FACILITY_REQ:
	case MNCC_START_DTMF_RSP:
	case MNCC_START_DTMF_REJ:
	case MNCC_STOP_DTMF_RSP:
	case MNCC_MODIFY_REQ:
	case MNCC_MODIFY_RSP:
	case MNCC_MODIFY_REJ:
	case MNCC_HOLD_CNF:
	case MNCC_HOLD_REJ:
	case MNCC_RETRIEVE_CNF:
	case MNCC_RETRIEVE_REJ:
	case MNCC_USERINFO_REQ:
	case MNCC_REJ_REQ:
		handle_signaling_msg(conn, &msg, rc);
		return;
	case MNCC_RTP_CREATE:
	case MNCC_RTP_CONNECT:
	case MNCC_RTP_FREE:
		handle_rtp_msg(conn, &msg, rc);
		return;
	default:
		syslog(LOG_CRIT,
		    "unknown MNCC message type 0x%x from ThemWi call socket",
			msg.msg_type);
	}
}

mncc_signal_to_socket(call, msg)
	struct gsm_call *call;
	struct gsm_mncc *msg;
{
	msg->callref = call->socket_ref;
	return send(call->socket->fd, msg, sizeof(struct gsm_mncc), 0);
}

mncc_signal_to_socket_nocall(conn, msg)
	struct socket_conn *conn;
	struct gsm_mncc *msg;
{
	return send(conn->fd, msg, sizeof(struct gsm_mncc), 0);
}

mncc_rtp_to_socket(call, msg)
	struct gsm_call *call;
	struct gsm_mncc_rtp *msg;
{
	msg->callref = call->socket_ref;
	return send(call->socket->fd, msg, sizeof(struct gsm_mncc_rtp), 0);
}