FreeCalypso > hg > themwi-system-sw
view sip-in/mncc_handle.c @ 275:def9f6e4f49e default tip
doc/Use-outside-USA: Fake-NANP-numbers article is here
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Mon, 27 Nov 2023 21:49:19 -0800 |
parents | 99fd4ae573ae |
children |
line wrap: on
line source
/* * In this module we implement our handling of call control messages * from OsmoMSC relayed to us via themwi-mncc. */ #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/mncc.h" #include "../include/gsm48_const.h" #include "call.h" extern struct call *find_call_by_mncc_callref(); static struct gsm_mncc_cause default_cause = { .coding = GSM48_CAUSE_CODING_GSM, .location = GSM48_CAUSE_LOC_PRN_S_LU, .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, }; static void send_rtp_connect(call) struct call *call; { struct gsm_mncc_rtp rtp; bzero(&rtp, sizeof(struct gsm_mncc_rtp)); rtp.msg_type = MNCC_RTP_CONNECT; rtp.callref = call->mncc_callref; bcopy(&call->gsm_rtp_tmgw, &rtp.addr, sizeof(struct sockaddr_storage)); send_mncc_to_gsm(&rtp, sizeof(struct gsm_mncc_rtp)); } static void handle_alerting(call, msg) struct call *call; struct gsm_mncc *msg; { if (call->mncc_state != MNCC_STATE_STARTED) { syslog(LOG_ERR, "MNCC_ALERT_IND in wrong MNCC state 0x%x", call->mncc_state); return; } call->mncc_state = MNCC_STATE_ALERTING; call->overall_state = OVERALL_STATE_ALERTING; signal_invite_ringing(call); } static void handle_answer(call, msg) struct call *call; struct gsm_mncc *msg; { if (call->mncc_state != MNCC_STATE_STARTED && call->mncc_state != MNCC_STATE_ALERTING) { syslog(LOG_ERR, "MNCC_SETUP_CNF in wrong MNCC state 0x%x", call->mncc_state); return; } call->mncc_state = MNCC_STATE_ANSWERED; call->overall_state = OVERALL_STATE_ANSWERED; /* right now we require MNCC_RTP_CREATE to have come first */ if (!call->gsm_payload_msg_type) { call->overall_state = OVERALL_STATE_TEARDOWN; disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_PROTO_ERR); disconnect_tmgw(call); strcpy(call->invite_fail, "502 Internal protocol error"); signal_invite_error(call); return; } send_rtp_connect(call); tmgw_send_mdcx_connect(call); } static void handle_disconnect_ind(call, msg) struct call *call; struct gsm_mncc *msg; { struct gsm_mncc_cause *cause; /* release back to MNCC */ msg->msg_type = MNCC_REL_REQ; send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); call->mncc_state = MNCC_STATE_RELEASE; /* signal disconnect to SIP */ call->overall_state = OVERALL_STATE_TEARDOWN; if (msg->fields & MNCC_F_CAUSE) cause = &msg->cause; else cause = &default_cause; disconnect_sip(call, cause); disconnect_tmgw(call); } static void handle_final_release(call, msg) struct call *call; struct gsm_mncc *msg; { struct gsm_mncc_cause *cause; /* MNCC call leg is gone */ call->mncc_state = MNCC_STATE_NO_EXIST; /* signal disconnect to SIP */ call->overall_state = OVERALL_STATE_TEARDOWN; if (msg->fields & MNCC_F_CAUSE) cause = &msg->cause; else cause = &default_cause; disconnect_sip(call, cause); disconnect_tmgw(call); transition_dead_sip(call); } static void handle_dtmf_start(call, msg) struct call *call; struct gsm_mncc *msg; { if (!(msg->fields & MNCC_F_KEYPAD) || !is_valid_dtmf_digit(msg->keypad)) { 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; } if (call->overall_state != OVERALL_STATE_CONNECTED || call->mgw_state != MGW_STATE_COMPLETE) { msg->msg_type = MNCC_START_DTMF_REJ; mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_MSGTYPE_INCOMPAT); send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); return; } call->dtmf_digit = msg->keypad; tmgw_send_dtmf_start(call); } static void handle_dtmf_stop(call, msg) struct call *call; struct gsm_mncc *msg; { if (call->overall_state != OVERALL_STATE_CONNECTED) return; if (call->mgw_state == MGW_STATE_COMPLETE) tmgw_send_dtmf_stop(call); else if (call->mgw_state == MGW_STATE_DTMF_OP) call->dtmf_pending_stop = 1; else { /* dummy OK response */ msg->msg_type = MNCC_STOP_DTMF_RSP; send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); } } static void handle_call_hold(call, msg) struct call *call; struct gsm_mncc *msg; { if (call->overall_state != OVERALL_STATE_CONNECTED || call->mgw_state != MGW_STATE_COMPLETE) { msg->msg_type = MNCC_HOLD_REJ; mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_MSGTYPE_INCOMPAT); send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); return; } tmgw_send_mdcx_hold(call); } static void handle_call_retrieve(call, msg) struct call *call; struct gsm_mncc *msg; { if (call->overall_state != OVERALL_STATE_CONNECTED || call->mgw_state != MGW_STATE_HELD) { msg->msg_type = MNCC_RETRIEVE_REJ; mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_MSGTYPE_INCOMPAT); send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); return; } send_rtp_connect(call); tmgw_send_mdcx_retrieve(call); } static void handle_signaling_msg(msg, msglen) struct gsm_mncc *msg; unsigned msglen; { struct call *call; if (msglen != sizeof(struct gsm_mncc)) { syslog(LOG_CRIT, "FATAL: Rx MNCC message type 0x%x has wrong length", msg->msg_type); exit(1); } call = find_call_by_mncc_callref(msg->callref); if (!call) { syslog(LOG_CRIT, "error: Rx MNCC message type 0x%x has invalid callref 0x%x", msg->msg_type, msg->callref); exit(1); } switch (msg->msg_type) { case MNCC_SETUP_CNF: handle_answer(call, msg); return; case MNCC_ALERT_IND: handle_alerting(call, msg); return; case MNCC_DISC_IND: handle_disconnect_ind(call, msg); return; case MNCC_REL_IND: case MNCC_REL_CNF: case MNCC_REJ_IND: handle_final_release(call, msg); return; case MNCC_START_DTMF_IND: handle_dtmf_start(call, msg); return; case MNCC_STOP_DTMF_IND: handle_dtmf_stop(call, msg); return; case MNCC_MODIFY_IND: 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: handle_call_hold(call, msg); return; case MNCC_RETRIEVE_IND: handle_call_retrieve(call, msg); return; } } static void handle_rtp_create(msg, msglen) struct gsm_mncc_rtp *msg; unsigned msglen; { struct call *call; if (msglen != sizeof(struct gsm_mncc_rtp)) { syslog(LOG_CRIT, "FATAL: Rx MNCC message type 0x%x has wrong length", msg->msg_type); exit(1); } call = find_call_by_mncc_callref(msg->callref); if (!call) { syslog(LOG_CRIT, "error: Rx MNCC message type 0x%x has invalid callref 0x%x", msg->msg_type, msg->callref); exit(1); } /* save Osmocom network RTP information */ bcopy(&msg->addr, &call->gsm_rtp_osmo, sizeof(struct sockaddr_storage)); call->gsm_payload_type = msg->payload_type; call->gsm_payload_msg_type = msg->payload_msg_type; } 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: handle_rtp_create(msg, msglen); return; case MNCC_RTP_CONNECT: syslog(LOG_ERR, "MNCC_RTP_CONNECT error from OsmoMSC"); return; case MNCC_RTP_FREE: syslog(LOG_ERR, "MNCC_RTP_FREE bogon from OsmoMSC"); return; default: syslog(LOG_CRIT, "FATAL: received unexpected MNCC message type 0x%x", msg->msg_type); exit(1); } }