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);
+}