FreeCalypso > hg > themwi-system-sw
diff sip-out/invite.c @ 156:0bacca1f2f7b
sip-out: handle all INVITE responses, except errors
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Wed, 12 Oct 2022 07:13:55 -0800 |
parents | |
children | 7643b779dbea |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/invite.c Wed Oct 12 07:13:55 2022 -0800 @@ -0,0 +1,270 @@ +/* + * 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_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_ENDED; + 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_ENDED; + 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: + return; + default: + syslog(LOG_CRIT, + "FATAL: invalid SIP state 0x%x on INVITE %03u response", + call->sip_state, msg->status_code); + exit(1); + } +} + +static void +handle_error(call, msg, tag, sin) + struct call *call; + struct sip_pkt_rx *msg; + char *tag; + struct sockaddr_in *sin; +{ + +} + +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); +}