view sip-in/invite_dup.c @ 146:54c2f271380d

sip-in: implement play-along responses to re-INVITEs
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 08 Oct 2022 19:53:23 -0800
parents 4b685a5d9bd4
children
line wrap: on
line source

/*
 * Here we process SIP INVITE retransmissions and/or re-INVITEs
 * for existing calls.  Our new approach is that we shall generate
 * the same stateless response without caring if it's a retransmission
 * or a re-INVITE, using CSeq and Via from the (re-)INVITE packet
 * we are responding to.  Our previous approach was to reject all
 * re-INVITE requests, but it turns out that we have to support them
 * in order to satisfy BulkVS and other calling servers that use
 * session timers.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include "../libsip/parse.h"
#include "../libsip/uas_basic.h"
#include "../libsip/sdp.h"
#include "../libsip/out_msg.h"
#include "call.h"

extern struct in_addr sip_bind_ip;
extern unsigned sip_bind_port;

static
fill_reinvite_resp(msg, call, ess)
	struct sip_msg_out *msg;
	struct call *call;
	struct uas_parse_hdrs *ess;
{
	char cseq_str[32];
	int rc;

	rc = out_msg_add_header(msg, "From", call->invite_from);
	if (rc < 0)
		return rc;
	rc = out_msg_add_header(msg, "To", call->invite_to);
	if (rc < 0)
		return rc;
	rc = out_msg_add_header(msg, "Call-ID", call->sip_call_id);
	if (rc < 0)
		return rc;
	sprintf(cseq_str, "%u INVITE", ess->cseq_num);
	rc = out_msg_add_header(msg, "CSeq", cseq_str);
	if (rc < 0)
		return rc;
	return out_msg_add_header(msg, "Via", ess->via);
}

static
fill_reinvite_resp_200(msg, call, ess)
	struct sip_msg_out *msg;
	struct call *call;
	struct uas_parse_hdrs *ess;
{
	char contact_str[80];
	struct sdp_gen sdp;
	int rc;

	start_response_out_msg(msg, "200 CONNECT");
	rc = fill_reinvite_resp(msg, call, ess);
	if (rc < 0)
		return rc;
	sprintf(contact_str, "<sip:%s:%u;transport=udp>",
		inet_ntoa(sip_bind_ip), sip_bind_port);
	rc = out_msg_add_header(msg, "Contact", contact_str);
	if (rc < 0)
		return rc;
	rc = out_msg_add_header(msg, "Content-Type", "application/sdp");
	if (rc < 0)
		return rc;
	bzero(&sdp, sizeof sdp);
	sdp.conn_ip = call->pstn_rtp_local.sin_addr;
	sdp.conn_port = ntohs(call->pstn_rtp_local.sin_port);
	sdp.codec_mask = call->use_pcma ? SDP_CODEC_MASK_PCMA
					: SDP_CODEC_MASK_PCMU;
	sdp.session_id = (sdp.conn_port << 16) | call->sdp_addend;
	sdp.owner_ip = sip_bind_ip;
	return out_msg_finish_sdp(msg, &sdp);
}

void
invite_existing_call(req, ess, sin, call)
	struct sip_pkt_rx *req;
	struct uas_parse_hdrs *ess;
	struct sockaddr_in *sin;
	struct call *call;
{
	struct sip_msg_out resp;
	int rc;

	switch (call->sip_state) {
	case SIP_STATE_INVITE_PROC:
		start_response_out_msg(&resp, "100 Proceeding");
		rc = fill_reinvite_resp(&resp, call, ess);
		if (rc < 0) {
msg_size_err:		syslog(LOG_ERR,
			"Call in%06u: msg size error on re-INVITE response",
				call->in_tag_num);
			return;
		}
		out_msg_finish(&resp);
		sip_tx_packet(&resp, sin);
		return;
	case SIP_STATE_RINGING:
	case SIP_STATE_RINGING_REL:
		start_response_out_msg(&resp, "180 Ringing");
		rc = fill_reinvite_resp(&resp, call, ess);
		if (rc < 0)
			goto msg_size_err;
		out_msg_finish(&resp);
		sip_tx_packet(&resp, sin);
		return;
	case SIP_STATE_INVITE_200:
	case SIP_STATE_CONNECTED:
		rc = fill_reinvite_resp_200(&resp, call, ess);
		if (rc < 0)
			goto msg_size_err;
		sip_tx_packet(&resp, sin);
		return;
	case SIP_STATE_INVITE_ERR:
		start_response_out_msg(&resp, call->invite_fail);
		rc = fill_reinvite_resp(&resp, call, ess);
		if (rc < 0)
			goto msg_size_err;
		out_msg_finish(&resp);
		sip_tx_packet(&resp, sin);
		return;
	default:
		/* silently discard */
		return;
	}
}