view sip-out/mgw_resp.c @ 191:6ac96217c442

sip-manual-out: add SDP response parsing
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 17 Mar 2023 12:07:17 -0800
parents e54b0a9e322f
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 "../include/out_routes.h"
#include "call.h"

extern struct call *find_call_with_mgw_xact();

static void
crcx_response(call, msg)
	struct call *call;
	struct tmgw_ctrl_resp *msg;
{
	int cause;

	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:
			send_rtp_connect(call);
			start_sip_out(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:
			call->overall_state = OVERALL_STATE_TEARDOWN;
			switch (msg->res) {
			case TMGW_RESP_ERR_RSRC:
				cause = GSM48_CC_CAUSE_RESOURCE_UNAVAIL;
				break;
			case TMGW_RESP_ERR_NOTSUP:
				cause = GSM48_CC_CAUSE_BEARER_CA_UNAVAIL;
				break;
			default:
				cause = GSM48_CC_CAUSE_PROTO_ERR;
			}
			disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, cause);
			return;
		case OVERALL_STATE_TEARDOWN:
			check_dead_call(call);
			return;
		default:
			goto bad_state;
		}
	}
}

static void
mdcx_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->overall_state = OVERALL_STATE_TEARDOWN;
		tmgw_send_dlcx(call);
		if (msg->res == TMGW_RESP_ERR_RSRC &&
		    (call->mgw_state == MGW_STATE_MDCX_IBT ||
		     call->mgw_state == MGW_STATE_MDCX_CONN))
			disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
					GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
		else
			disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
					GSM48_CC_CAUSE_NETWORK_OOO);
		disconnect_sip(call);
		return;
	}
	switch (call->mgw_state) {
	case MGW_STATE_MDCX_IBT:
		call->mgw_state = MGW_STATE_IBT_CONN;
		mncc_signal_ibt_ring(call);
		if (call->sip_state == SIP_STATE_CONNECTED)
			tmgw_send_mdcx_connect(call, 0);
		return;
	case MGW_STATE_MDCX_CONN:
		call->mgw_state = MGW_STATE_COMPLETE;
		mncc_signal_answer_sup(call);
		return;
	case MGW_STATE_MDCX_HOLD:
		call->mgw_state = MGW_STATE_HELD;
		mncc_send_hold_ack(call);
		return;
	case MGW_STATE_MDCX_RETR:
		call->mgw_state = MGW_STATE_COMPLETE;
		mncc_send_retrieve_ack(call);
		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;
	check_dead_call(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);
	}
}