view utils/sip-out-test.c @ 124:7e04d28fae8b

sip-in: default use-100rel to no BulkVS servers act badly when we send a reliable 180 Ringing response to an incoming call, even though they advertise 100rel support in the Supported header in the INVITE packet, and we probably won't be implementing 100rel for outbound because doing per-the-spec PRACK as a UAC is just too burdensome. Therefore, we need to consider 100rel extension as not-really-supported in themwi-system-sw.
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 01 Oct 2022 15:54:50 -0800
parents ffb563a17f23
children
line wrap: on
line source

/*
 * This program is a contraption for testing manually constructed
 * outgoing SIP calls to BulkVS.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

#define	MAX_SIP_TX_PACKET	1472

static struct in_addr local_ip, remote_ip;
static unsigned local_sip_port, local_rtp_port, remote_port;
static char *invite_filename, *log_filename;
static char invite_packet[MAX_SIP_TX_PACKET];
static unsigned invite_packet_len;
static FILE *logF;
static struct timeval curtime;

static void
read_invite_file()
{
	FILE *inf;
	char linebuf[128], *cp, *dp;
	int lineno;
	unsigned size_accum, linelen;

	inf = fopen(invite_filename, "r");
	if (!inf) {
		perror(invite_filename);
		exit(1);
	}
	size_accum = 0;
	dp = invite_packet;
	for (lineno = 1; fgets(linebuf, sizeof(linebuf), inf); lineno++) {
		cp = index(linebuf, '\n');
		if (!cp) {
			fprintf(stderr,
				"%s line %d: too long or missing newline\n",
				invite_filename, lineno);
			exit(1);
		}
		*cp = '\0';
		linelen = cp - linebuf;
		if (size_accum + linelen + 2 > MAX_SIP_TX_PACKET) {
			fprintf(stderr, "%s line %d: packet overflow\n",
				invite_filename, lineno);
			exit(1);
		}
		bcopy(linebuf, dp, linelen);
		dp += linelen;
		*dp++ = '\r';
		*dp++ = '\n';
		size_accum += linelen + 2;
	}
	fclose(inf);
	if (!size_accum) {
		fprintf(stderr, "error: %s is empty\n", invite_filename);
		exit(1);
	}
	invite_packet_len = size_accum;
}

static void
log_common(msg, msglen, sin, dir, outf)
	char *msg, *dir;
	unsigned msglen;
	struct sockaddr_in *sin;
	FILE *outf;
{
	unsigned sec, ms;

	sec = curtime.tv_sec % 86400;
	ms = curtime.tv_usec / 1000;
	fprintf(outf, "Msg %s %s:%u %u bytes %02u:%02u:%02u.%03u\n", dir,
		inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), msglen,
		sec / 3600, (sec / 60) % 60, sec % 60, ms);
	fwrite(msg, 1, msglen, outf);
	putc('\n', outf);
	fflush(outf);
}

static void
log_sip_msg_rx(msg, msglen, sin)
	char *msg;
	unsigned msglen;
	struct sockaddr_in *sin;
{
	log_common(msg, msglen, sin, "from", stdout);
	if (logF)
		log_common(msg, msglen, sin, "from", logF);
}

static void
log_sip_msg_tx(msg, msglen, sin)
	char *msg;
	unsigned msglen;
	struct sockaddr_in *sin;
{
	log_common(msg, msglen, sin, "to", stdout);
	if (logF)
		log_common(msg, msglen, sin, "to", logF);
}

main(argc, argv)
	char **argv;
{
	struct sockaddr_in sin_local, sin_rtp, sin_rtcp, sin_remote, sin_rx;
	int sock_sip, sock_rtp, sock_rtcp, max_fd;
	socklen_t addrlen;
	fd_set fds;
	char recv_buf[4096];
	int rc;

	/* grok command line arguments */
	if (argc < 7 || argc > 8) {
		fprintf(stderr,
"usage: %s local-ip local-sip local-rtp remote-ip remote-sip inv-file [logfile]\n",
			argv[0]);
		exit(1);
	}
	local_ip.s_addr = inet_addr(argv[1]);
	if (local_ip.s_addr == INADDR_NONE) {
		fprintf(stderr, "error: invalid IP address \"%s\"\n", argv[1]);
		exit(1);
	}
	local_sip_port = atoi(argv[2]);
	local_rtp_port = atoi(argv[3]);
	remote_ip.s_addr = inet_addr(argv[4]);
	if (remote_ip.s_addr == INADDR_NONE) {
		fprintf(stderr, "error: invalid IP address \"%s\"\n", argv[4]);
		exit(1);
	}
	remote_port = atoi(argv[5]);
	invite_filename = argv[6];
	log_filename = argv[7];
	/* fill sin structures */
	sin_local.sin_family = AF_INET;
	sin_local.sin_addr = local_ip;
	sin_local.sin_port = htons(local_sip_port);
	sin_rtp.sin_family = AF_INET;
	sin_rtp.sin_addr = local_ip;
	sin_rtp.sin_port = htons(local_rtp_port);
	sin_rtcp.sin_family = AF_INET;
	sin_rtcp.sin_addr = local_ip;
	sin_rtcp.sin_port = htons(local_rtp_port+1);
	sin_remote.sin_family = AF_INET;
	sin_remote.sin_addr = remote_ip;
	sin_remote.sin_port = htons(remote_port);
	/* create and bind sockets */
	sock_sip = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock_sip < 0) {
		perror("socket");
		exit(1);
	}
	rc = bind(sock_sip, (struct sockaddr *) &sin_local,
		  sizeof(struct sockaddr_in));
	if (rc < 0) {
		perror("bind");
		exit(1);
	}
	sock_rtp = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock_rtp < 0) {
		perror("socket");
		exit(1);
	}
	rc = bind(sock_rtp, (struct sockaddr *) &sin_rtp,
		  sizeof(struct sockaddr_in));
	if (rc < 0) {
		perror("bind");
		exit(1);
	}
	sock_rtcp = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock_rtcp < 0) {
		perror("socket");
		exit(1);
	}
	rc = bind(sock_rtcp, (struct sockaddr *) &sin_rtcp,
		  sizeof(struct sockaddr_in));
	if (rc < 0) {
		perror("bind");
		exit(1);
	}
	/* read the INVITE packet */
	read_invite_file();
	/* open the log file, if we have one */
	if (log_filename) {
		logF = fopen(log_filename, "a");
		if (!logF) {
			perror(log_filename);
			exit(1);
		}
	}
	/* now get down to business */
	max_fd = sock_sip;
	if (sock_rtp > max_fd)
		max_fd = sock_rtp;
	if (sock_rtcp > max_fd)
		max_fd = sock_rtcp;
	addrlen = sizeof(struct sockaddr_in);
	rc = sendto(sock_sip, invite_packet, invite_packet_len, 0,
			(struct sockaddr *) &sin_remote, addrlen);
	if (rc < 0) {
		perror("sendto");
		exit(1);
	}
	gettimeofday(&curtime, 0);
	log_sip_msg_tx(invite_packet, invite_packet_len, &sin_remote);
	/* main select loop */
	for (;;) {
		FD_ZERO(&fds);
		FD_SET(sock_sip, &fds);
		FD_SET(sock_rtp, &fds);
		FD_SET(sock_rtcp, &fds);
		rc = select(max_fd+1, &fds, 0, 0, 0);
		if (rc < 0) {
			if (errno == EINTR)
				continue;
			perror("select");
			exit(1);
		}
		gettimeofday(&curtime, 0);
		if (FD_ISSET(sock_sip, &fds)) {
			addrlen = sizeof(struct sockaddr_in);
			rc = recvfrom(sock_sip, recv_buf, sizeof recv_buf, 0,
					(struct sockaddr *) &sin_rx, &addrlen);
			if (rc < 0) {
				perror("recvfrom");
				exit(1);
			}
			log_sip_msg_rx(recv_buf, rc, &sin_rx);
		}
		if (FD_ISSET(sock_rtp, &fds)) {
			addrlen = sizeof(struct sockaddr_in);
			rc = recvfrom(sock_rtp, recv_buf, sizeof recv_buf, 0,
					(struct sockaddr *) &sin_rx, &addrlen);
			if (rc < 0) {
				perror("recvfrom");
				exit(1);
			}
		}
		if (FD_ISSET(sock_rtcp, &fds)) {
			addrlen = sizeof(struct sockaddr_in);
			rc = recvfrom(sock_rtp, recv_buf, sizeof recv_buf, 0,
					(struct sockaddr *) &sin_rx, &addrlen);
			if (rc < 0) {
				perror("recvfrom");
				exit(1);
			}
		}
	}
}