FreeCalypso > hg > themwi-system-sw
view sip-out/invite.c @ 209:e80f158333c5
mgw: correct PCMU decoding table
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 07 May 2023 21:43:02 -0800 |
parents | bfa9f0c0f0ac |
children |
line wrap: on
line source
/* * In this module we handle responses to INVITE. */ #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/out_routes.h" #include "../libsip/parse.h" #include "../libsip/sdp.h" #include "../libsip/out_msg.h" #include "call.h" extern char *get_single_header(); extern char *extract_to_tag(); extern unsigned sip_linger_invite_err; extern unsigned sip_linger_response_err; static check_sdp_present(msg) struct sip_pkt_rx *msg; { char *hval; if (!msg->msg_body_len) return 0; hval = get_single_header(msg, "Content-Type", "c", (int *) 0); if (!hval) return 0; if (!strcasecmp(hval, "application/sdp")) return 1; else return 0; } static extract_sdp(call, msg) struct call *call; struct sip_pkt_rx *msg; { struct sdp_parse sdp_parse; int rc, use_pcma; rc = parse_incoming_sdp(msg->msg_body, msg->msg_body_len, &sdp_parse); if (rc < 0) return rc; switch (sdp_parse.codec_mask) { case SDP_CODEC_MASK_PCMU: case SDP_CODEC_MASK_BOTH: use_pcma = 0; break; case SDP_CODEC_MASK_PCMA: case SDP_CODEC_MASK_BOTH | SDP_CODEC_MASK_PCMA_PREF: use_pcma = 1; break; default: return -2; } call->pstn_rtp_remote.sin_family = AF_INET; call->pstn_rtp_remote.sin_addr = sdp_parse.ip_addr; call->pstn_rtp_remote.sin_port = htons(sdp_parse.audio_port); call->use_pcma = use_pcma; return 0; } static void handle_1xx(call, msg, tag, sin) struct call *call; struct sip_pkt_rx *msg; char *tag; struct sockaddr_in *sin; { int rc; switch (call->sip_state) { case SIP_STATE_INV_SENT: call->sip_state = SIP_STATE_100_RCVD; /* FALL THRU */ case SIP_STATE_100_RCVD: if (msg->status_code == 180) call->overall_state = OVERALL_STATE_RINGING; if (check_sdp_present(msg) && call->mgw_state == MGW_STATE_ALLOCATED) { rc = extract_sdp(call, msg); if (rc < 0) { syslog(LOG_ERR, "bad SDP in %03u response", msg->status_code); call->overall_state = OVERALL_STATE_TEARDOWN; disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); disconnect_tmgw(call); initiate_sip_cancel(call); return; } tmgw_send_mdcx_connect(call, 1); } else if (msg->status_code == 180) mncc_signal_alerting(call); return; case SIP_STATE_ACCEPT_100: initiate_sip_cancel(call); return; } } static send_ack(call, tag, sin) struct call *call; char *tag; struct sockaddr_in *sin; { struct sip_msg_out msg; int rc; rc = start_request_out_msg(&msg, "ACK", call->to_uri); if (rc < 0) return rc; rc = add_req_boilerplate(&msg, call, "1 ACK", tag); if (rc < 0) return rc; out_msg_finish(&msg); sip_tx_packet(&msg, sin); return 0; } static void handle_200(call, msg, tag, sin) struct call *call; struct sip_pkt_rx *msg; char *tag; struct sockaddr_in *sin; { int rc; switch (call->sip_state) { case SIP_STATE_INV_SENT: case SIP_STATE_100_RCVD: rc = send_ack(call, tag, sin); if (rc < 0) { syslog(LOG_CRIT, "ACK to %03u response exceeds msg size!", msg->status_code); call->overall_state = OVERALL_STATE_TEARDOWN; disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_DEST_OOO); disconnect_tmgw(call); call->sip_state = SIP_STATE_MSG_SIZE_ERR; sip_mark_end_time(call, sip_linger_response_err); return; } if (tag) strcpy(call->to_tag, tag); call->sip_state = SIP_STATE_CONNECTED; if (!check_sdp_present(msg)) { syslog(LOG_ERR, "error: %03u response has no SDP", msg->status_code); call->overall_state = OVERALL_STATE_TEARDOWN; disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_DEST_OOO); disconnect_tmgw(call); initiate_bye(call); return; } rc = extract_sdp(call, msg); if (rc < 0) { syslog(LOG_ERR, "bad SDP in %03u response", msg->status_code); call->overall_state = OVERALL_STATE_TEARDOWN; disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); disconnect_tmgw(call); initiate_bye(call); return; } call->overall_state = OVERALL_STATE_CONNECTED; switch (call->mgw_state) { case MGW_STATE_ALLOCATED: case MGW_STATE_IBT_CONN: tmgw_send_mdcx_connect(call, 0); return; case MGW_STATE_MDCX_IBT: return; default: syslog(LOG_CRIT, "FATAL: invalid MGW state 0x%x on INVITE %03u response", call->mgw_state, msg->status_code); exit(1); } case SIP_STATE_CONNECTED: case SIP_STATE_BYE_SENT: if (tag && call->to_tag[0] && strcmp(call->to_tag, tag)) { syslog(LOG_ERR, "received %u response with different To tag, ignoring", msg->status_code); return; } send_ack(call, call->to_tag, sin); return; case SIP_STATE_CANCEL_SENT: case SIP_STATE_ACCEPT_100: case SIP_STATE_ACCEPT_200: rc = send_ack(call, tag, sin); if (rc < 0) { syslog(LOG_CRIT, "ACK to %03u response exceeds msg size!", msg->status_code); call->sip_state = SIP_STATE_MSG_SIZE_ERR; sip_mark_end_time(call, sip_linger_response_err); return; } if (tag) strcpy(call->to_tag, tag); initiate_bye(call); return; case SIP_STATE_ENDED: case SIP_STATE_MSG_SIZE_ERR: return; default: syslog(LOG_CRIT, "FATAL: invalid SIP state 0x%x on INVITE %03u response", call->sip_state, msg->status_code); exit(1); } } static int sip_error_to_gsm_cause(sip_status_code) unsigned sip_status_code; { /* mapping taken from osmo-sip-connector */ switch (sip_status_code) { case 400: return GSM48_CC_CAUSE_TEMP_FAILURE; case 401: case 402: case 403: return GSM48_CC_CAUSE_CALL_REJECTED; case 404: return GSM48_CC_CAUSE_UNASSIGNED_NR; case 405: return GSM48_CC_CAUSE_SERV_OPT_UNAVAIL; case 406: return GSM48_CC_CAUSE_CHAN_UNACCEPT; case 407: return GSM48_CC_CAUSE_CALL_REJECTED; case 408: return GSM48_CC_CAUSE_RECOVERY_TIMER; case 410: return GSM48_CC_CAUSE_NUMBER_CHANGED; case 413: case 414: return GSM48_CC_CAUSE_INTERWORKING; case 415: return GSM48_CC_CAUSE_SERV_OPT_UNIMPL; case 416: return GSM48_CC_CAUSE_INVAL_TRANS_ID; case 420: case 421: case 423: return GSM48_CC_CAUSE_INTERWORKING; case 480: return GSM48_CC_CAUSE_USER_NOTRESPOND; case 481: return GSM48_CC_CAUSE_TEMP_FAILURE; case 482: case 483: return GSM48_CC_CAUSE_PRE_EMPTION; case 484: return GSM48_CC_CAUSE_INV_NR_FORMAT; case 485: return GSM48_CC_CAUSE_NO_ROUTE; case 486: return GSM48_CC_CAUSE_USER_BUSY; case 488: return GSM48_CC_CAUSE_INCOMPAT_DEST; case 500: return GSM48_CC_CAUSE_TEMP_FAILURE; case 501: return GSM48_CC_CAUSE_SERV_OPT_UNIMPL; case 502: return GSM48_CC_CAUSE_DEST_OOO; case 503: return GSM48_CC_CAUSE_RESOURCE_UNAVAIL; case 504: return GSM48_CC_CAUSE_RECOVERY_TIMER; case 505: case 513: return GSM48_CC_CAUSE_INTERWORKING; case 600: return GSM48_CC_CAUSE_USER_BUSY; case 603: return GSM48_CC_CAUSE_CALL_REJECTED; case 604: return GSM48_CC_CAUSE_NO_ROUTE; case 606: return GSM48_CC_CAUSE_INCOMPAT_DEST; default: return GSM48_CC_CAUSE_NORMAL_UNSPEC; } } static void handle_error(call, msg, tag, sin) struct call *call; struct sip_pkt_rx *msg; char *tag; struct sockaddr_in *sin; { int rc; if (call->sip_state == SIP_STATE_MSG_SIZE_ERR) return; rc = send_ack(call, tag, sin); if (rc < 0) syslog(LOG_CRIT, "ACK to %03u response exceeds msg size!", msg->status_code); switch (call->sip_state) { case SIP_STATE_INV_SENT: case SIP_STATE_100_RCVD: call->overall_state = OVERALL_STATE_TEARDOWN; disconnect_mncc(call, GSM48_CAUSE_LOC_NET_BEYOND, sip_error_to_gsm_cause(msg->status_code)); disconnect_tmgw(call); call->sip_state = SIP_STATE_ENDED; sip_mark_end_time(call, sip_linger_invite_err); return; case SIP_STATE_CANCEL_SENT: case SIP_STATE_ACCEPT_100: case SIP_STATE_ACCEPT_200: call->sip_state = SIP_STATE_ENDED; sip_mark_end_time(call, sip_linger_invite_err); return; case SIP_STATE_CONNECTED: case SIP_STATE_BYE_SENT: case SIP_STATE_ENDED: return; default: syslog(LOG_CRIT, "FATAL: invalid SIP state 0x%x on INVITE %03u response", call->sip_state, msg->status_code); exit(1); } } void handle_invite_response(call, msg, sin) struct call *call; struct sip_pkt_rx *msg; struct sockaddr_in *sin; { char *tag; tag = extract_to_tag(msg, call->to_uri); if (tag) { if (!*tag) { syslog(LOG_ERR, "To tag in INVITE %03u response is null", msg->status_code); tag = 0; } else if (strlen(tag) > MAX_SIP_TO_TAG) { syslog(LOG_ERR, "To tag in INVITE %03u response exceeds length limit", msg->status_code); tag = 0; } } if (msg->status_code >= 100 && msg->status_code <= 199) handle_1xx(call, msg, tag, sin); else if (msg->status_code >= 200 && msg->status_code <= 299) handle_200(call, msg, tag, sin); else handle_error(call, msg, tag, sin); }