view sip-in/mgw_resp.c @ 152:7176dc850d7a

sip-in hold/retr error handling: simply send BYE Because we know that the SIP state is CONNECTED at the time of any such error event, we can call initiate_bye() instead of disconnect_sip(), and thereby get rid of struct gsm_mncc_cause which will never be used in this scenario anyway.
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 11 Oct 2022 16:11:21 -0800
parents 0ecbc3dc8f93
children
line wrap: on
line source

/*
 * In this module we implement our handling of all responses
 * from themwi-mgw.
 */

#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/gsm48_const.h"
#include "../include/tmgw_ctrl.h"
#include "../include/tmgw_const.h"
#include "call.h"

extern struct call *find_call_with_mgw_xact();

static void
handle_crcx_fail(call, msg)
	struct call *call;
	struct tmgw_ctrl_resp *msg;
{
	call->overall_state = OVERALL_STATE_TEARDOWN;
	strcpy(call->invite_fail, "503 Gateway resource allocation failure");
	signal_invite_error(call);
}

static void
crcx_response(call, msg)
	struct call *call;
	struct tmgw_ctrl_resp *msg;
{
	if (msg->res == TMGW_RESP_OK) {
		call->mgw_state = MGW_STATE_ALLOCATED;
		call->mgw_ep_id = msg->ep_id;
		bcopy(&msg->gsm_addr, &call->gsm_rtp_tmgw,
			sizeof(struct sockaddr_storage));
		bcopy(&msg->pstn_addr, &call->pstn_rtp_local,
			sizeof(struct sockaddr_in));
		switch (call->overall_state) {
		case OVERALL_STATE_CRCX:
			proceed_with_call_setup(call);
			return;
		case OVERALL_STATE_TEARDOWN:
			tmgw_send_dlcx(call);
			return;
		default:
		bad_state:
			syslog(LOG_CRIT,
			"FATAL: invalid overall state 0x%x on CRCX response",
				call->overall_state);
			exit(1);
		}
	} else {
		switch (call->overall_state) {
		case OVERALL_STATE_CRCX:
			handle_crcx_fail(call, msg);
			return;
		case OVERALL_STATE_TEARDOWN:
			transition_dead_sip(call);
			return;
		default:
			goto bad_state;
		}
	}
}

static void
handle_mdcx_connect_fail(call, msg)
	struct call *call;
	struct tmgw_ctrl_resp *msg;
{
	call->overall_state = OVERALL_STATE_TEARDOWN;
	switch (msg->res) {
	case TMGW_RESP_ERR_RSRC:
		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
		strcpy(call->invite_fail,
			"503 Gateway resource allocation failure");
		break;
	case TMGW_RESP_ERR_NOTSUP:
		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
		strcpy(call->invite_fail, "502 Gateway internal error");
		break;
	default:
		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_PROTO_ERR);
		strcpy(call->invite_fail, "502 Gateway internal error");
	}
	signal_invite_error(call);
}

static void
mdcx_connect_response(call, msg)
	struct call *call;
	struct tmgw_ctrl_resp *msg;
{
	if (msg->res == TMGW_RESP_OK) {
		call->mgw_state = MGW_STATE_COMPLETE;
		switch (call->overall_state) {
		case OVERALL_STATE_ANSWERED:
			signal_invite_200(call);
			return;
		case OVERALL_STATE_TEARDOWN:
			tmgw_send_dlcx(call);
			return;
		default:
		bad_state:
			syslog(LOG_CRIT,
			"FATAL: invalid overall state 0x%x on MDCX response",
				call->overall_state);
			exit(1);
		}
	} else {
		tmgw_send_dlcx(call);
		switch (call->overall_state) {
		case OVERALL_STATE_ANSWERED:
			handle_mdcx_connect_fail(call, msg);
			return;
		case OVERALL_STATE_TEARDOWN:
			return;
		default:
			goto bad_state;
		}
	}
}

static void
mdcx_hold_response(call, msg)
	struct call *call;
	struct tmgw_ctrl_resp *msg;
{
	if (call->overall_state == OVERALL_STATE_TEARDOWN) {
		tmgw_send_dlcx(call);
		return;
	}
	if (msg->res == TMGW_RESP_OK) {
		call->mgw_state = MGW_STATE_HELD;
		mncc_send_hold_ack(call);
	} else {
		call->overall_state = OVERALL_STATE_TEARDOWN;
		tmgw_send_dlcx(call);
		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_NETWORK_OOO);
		initiate_bye(call);
	}
}

static void
mdcx_retrieve_response(call, msg)
	struct call *call;
	struct tmgw_ctrl_resp *msg;
{
	if (call->overall_state == OVERALL_STATE_TEARDOWN) {
		tmgw_send_dlcx(call);
		return;
	}
	if (msg->res == TMGW_RESP_OK) {
		call->mgw_state = MGW_STATE_COMPLETE;
		mncc_send_retrieve_ack(call);
	} else {
		call->overall_state = OVERALL_STATE_TEARDOWN;
		tmgw_send_dlcx(call);
		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_NETWORK_OOO);
		initiate_bye(call);
	}
}

static void
mdcx_response(call, msg)
	struct call *call;
	struct tmgw_ctrl_resp *msg;
{
	switch (call->mgw_state) {
	case MGW_STATE_CONNECTING:
		mdcx_connect_response(call, msg);
		return;
	case MGW_STATE_HOLD_OP:
		mdcx_hold_response(call, msg);
		return;
	case MGW_STATE_RETRIEVE_OP:
		mdcx_retrieve_response(call, msg);
		return;
	default:
		syslog(LOG_CRIT,
			"FATAL: invalid MGW state 0x%x on MDCX response",
			call->mgw_state);
		exit(1);
	}
}

static void
dlcx_response(call, msg)
	struct call *call;
	struct tmgw_ctrl_resp *msg;
{
	if (msg->res != TMGW_RESP_OK) {
		syslog(LOG_CRIT, "FATAL: TMGW DLCX failed with code 0x%x",
			msg->res);
		exit(1);
	}
	call->mgw_state = MGW_STATE_NO_EXIST;
	transition_dead_sip(call);
}

static void
dtmf_start_response(call, msg)
	struct call *call;
	struct tmgw_ctrl_resp *msg;
{
	if (call->overall_state == OVERALL_STATE_TEARDOWN) {
		tmgw_send_dlcx(call);
		return;
	}
	if (msg->res == TMGW_RESP_OK)
		mncc_dtmf_start_ok(call);
	else
		mncc_dtmf_start_err(call);
	if (call->dtmf_pending_stop)
		tmgw_send_dtmf_stop(call);
	else
		call->mgw_state = MGW_STATE_COMPLETE;
}

static void
dtmf_stop_response(call, msg)
	struct call *call;
	struct tmgw_ctrl_resp *msg;
{
	if (call->overall_state == OVERALL_STATE_TEARDOWN) {
		tmgw_send_dlcx(call);
		return;
	}
	mncc_dtmf_stop_ok(call);
	call->mgw_state = MGW_STATE_COMPLETE;
	call->dtmf_pending_stop = 0;
}

void
process_tmgw_response(msg)
	struct tmgw_ctrl_resp *msg;
{
	struct call *call;
	unsigned opc;

	call = find_call_with_mgw_xact(msg->transact_ref);
	if (!call) {
		syslog(LOG_CRIT,
		"FATAL: response from TMGW xact 0x%x does not match any call",
			msg->transact_ref);
		exit(1);
	}
	opc = call->mgw_xact;
	call->mgw_xact = 0;
	switch (opc) {
	case TMGW_CTRL_OP_CRCX:
		crcx_response(call, msg);
		return;
	case TMGW_CTRL_OP_MDCX:
		mdcx_response(call, msg);
		return;
	case TMGW_CTRL_OP_DLCX:
		dlcx_response(call, msg);
		return;
	case TMGW_CTRL_OP_DTMF_START:
		dtmf_start_response(call, msg);
		return;
	case TMGW_CTRL_OP_DTMF_STOP:
		dtmf_stop_response(call, msg);
		return;
	default:
		syslog(LOG_CRIT,
			"FATAL: invalid opcode 0x%x in call->msg_xact", opc);
		exit(1);
	}
}