changeset 0:35c0d9f03c0a

beginning with sipout-test-voice, a copy of sip-manual-out from themwi-system-sw
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 03 Mar 2024 23:20:19 -0800
parents
children 38c4d09882f6
files .hgignore include/rtp_alloc.h include/rtp_defs.h include/tmgw_const.h librtpalloc/Makefile librtpalloc/rtp_alloc_simple.c librtpalloc/rtp_alloc_simple.h librtpalloc/rtpmgr_resp.c librtpalloc/rtpmgr_resp.h libsip/Makefile libsip/get_header.c libsip/grok_from.c libsip/grok_from.h libsip/out_msg.c libsip/out_msg.h libsip/parse.h libsip/primary_parse.c libsip/req_supp.c libsip/req_supp.h libsip/resp_ident.c libsip/resp_ident.h libsip/sdp.h libsip/sdp_gen.c libsip/sdp_parse.c libsip/to_tag.c libsip/uas_basic.c libsip/uas_basic.h libsip/uri_utils.c libutil/Makefile libutil/bitfunc.c libutil/crc8gen.c libutil/osmo_bits.h libutil/sockinit.c libutil/tfo_msg_enc.c test-voice/Makefile test-voice/bye_in.c test-voice/disc_cmd.c test-voice/main.c test-voice/readconf.c test-voice/reinvite.c test-voice/rtp_rx.c test-voice/rtp_tx.c test-voice/sdp_in.c test-voice/sip_log.c test-voice/sip_udp.c test-voice/uac.c test-voice/uas.c test-voice/user_cmd.c
diffstat 48 files changed, 3112 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,5 @@
+syntax: regexp
+
+\.[oa]$
+
+^test-voice/sipout-test-voice$
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/rtp_alloc.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,20 @@
+/*
+ * This header file defines the ad hoc control interface
+ * to themwi-rtp-mgr over a dedicated local socket.
+ */
+
+struct rtp_alloc_req {
+	uint32_t	transact_ref;
+	uint32_t	ep_type;
+};
+
+struct rtp_alloc_resp {
+	uint32_t	transact_ref;
+	uint32_t	res;
+	struct sockaddr_storage gsm_addr;
+	struct sockaddr_storage pstn_addr;
+};
+
+#define	RTP_ALLOC_OK		0
+#define	RTP_ALLOC_ERR_PARAM	1
+#define	RTP_ALLOC_ERR_RSRC	2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/rtp_defs.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,17 @@
+/*
+ * This header file holds some definitions for RTP, as this protocol
+ * functions in our GSM and PSTN environment.
+ */
+
+#define	RTP_PACKET_HDR_SIZE	12
+#define	RTP_PACKET_SIZE_PSTN	172
+#define	RTP_MAX_PAYLOAD		160
+
+struct rtp_packet {
+	uint8_t		v_p_x_cc;
+	uint8_t		m_pt;
+	uint16_t	seq;
+	uint32_t	tstamp;
+	uint32_t	ssrc;
+	uint8_t		payload[RTP_MAX_PAYLOAD];
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/tmgw_const.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,37 @@
+/*
+ * This header file defines some constants for themwi-mgw,
+ * used in the implementation, in the control interface and
+ * throughout the architecture.
+ *
+ * Some of these constants (specifically those dealing with
+ * RTP endpoint types) are now also used in themwi-rtp-mgr
+ * and other RTP-touching tools beyond the original themwi-mgw
+ * design.
+ */
+
+#define	TMGW_EP_TYPE_GSM_ONLY		1
+#define	TMGW_EP_TYPE_PSTN_ONLY		2
+#define	TMGW_EP_TYPE_GATEWAY		3
+
+/* backward compatibility, from themwi-mgw perspective */
+#define	TMGW_EP_TYPE_DUMMY_GSM		TMGW_EP_TYPE_GSM_ONLY
+#define	TMGW_EP_TYPE_DUMMY_PSTN		TMGW_EP_TYPE_PSTN_ONLY
+
+#define	TMGW_EP_HAS_GSM_SOCK		1
+#define	TMGW_EP_HAS_PSTN_SOCK		2
+
+#define	TMGW_FWD_MODE_INACTIVE		0
+#define	TMGW_FWD_MODE_RECVONLY		1
+#define	TMGW_FWD_MODE_SENDONLY		2
+#define	TMGW_FWD_MODE_SENDRECV		3
+
+#define	TMGW_FWD_ENABLE_PSTN2GSM	1
+#define	TMGW_FWD_ENABLE_GSM2PSTN	2
+
+#define	GSM_TCHF_FRAME			0x0300
+#define	GSM_TCHF_FRAME_EFR		0x0301
+#define	GSM_TCHH_FRAME			0x0302
+#define	GSM_TCH_FRAME_AMR		0x0303
+
+#define	PSTN_CODEC_PCMU			0
+#define	PSTN_CODEC_PCMA			8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librtpalloc/Makefile	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,13 @@
+CC=	gcc
+CFLAGS=	-O2
+OBJS=	rtp_alloc_simple.o rtpmgr_resp.o
+LIB=	librtpalloc.a
+
+all:	${LIB}
+
+${LIB}:	${OBJS}
+	ar rcu $@ ${OBJS}
+	ranlib $@
+
+clean:
+	rm -f *.[oa] errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librtpalloc/rtp_alloc_simple.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,124 @@
+/*
+ * The library function implemented in this C module provides a
+ * simple interface for obtaining a single RTP endpoint from
+ * themwi-rtp-mgr.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "../include/tmgw_const.h"
+#include "../include/rtp_alloc.h"
+#include "rtpmgr_resp.h"
+#include "rtp_alloc_simple.h"
+
+static char ctrl_socket_pathname[] = "/var/gsm/rtp_alloc_socket";
+
+static void
+close_fds(resp)
+	struct rtp_alloc_resp_wrap *resp;
+{
+	unsigned n;
+
+	for (n = 0; n < resp->num_fd; n++)
+		close(resp->fd_buf[n]);
+}
+
+rtp_alloc_simple(ep_type, out)
+	int ep_type;
+	struct rtp_alloc_simple *out;
+{
+	struct sockaddr_un sa;
+	unsigned sa_len;
+	int ctrl_fd, rc;
+	struct rtp_alloc_req req;
+	struct rtp_alloc_resp_wrap resp;
+	unsigned expect_num_fd;
+
+	switch (ep_type) {
+	case TMGW_EP_TYPE_GSM_ONLY:
+	case TMGW_EP_TYPE_PSTN_ONLY:
+		expect_num_fd = 2;
+		break;
+	case TMGW_EP_TYPE_GATEWAY:
+		expect_num_fd = 4;
+		break;
+	default:
+		fprintf(stderr,
+			"rtp_alloc_simple() error: unknown EP type %d\n",
+			ep_type);
+		return(-1);
+	}
+	ctrl_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (ctrl_fd < 0) {
+		perror("socket(AF_UNIX, SOCK_SEQPACKET, 0)");
+		return(-1);
+	}
+	fill_sockaddr_un(ctrl_socket_pathname, &sa, &sa_len);
+	rc = connect(ctrl_fd, (struct sockaddr *) &sa, sa_len);
+	if (rc < 0) {
+		perror(ctrl_socket_pathname);
+		close(ctrl_fd);
+		return(-1);
+	}
+	bzero(&req, sizeof req);
+	req.ep_type = ep_type;
+	rc = send(ctrl_fd, &req, sizeof req, 0);
+	if (rc < 0) {
+		perror("send to RTP allocator socket");
+		close(ctrl_fd);
+		return(-1);
+	}
+	rc = collect_rtpmgr_resp(ctrl_fd, 0, &resp);
+	if (rc < 0) {
+		perror("recvmsg from RTP allocator socket");
+		close(ctrl_fd);
+		return(-1);
+	}
+	close(ctrl_fd);
+	if (resp.resp_len != sizeof(struct rtp_alloc_resp)) {
+		fprintf(stderr,
+"error: response packet from themwi-rtp-mgr has wrong length (%u bytes)\n",
+			resp.resp_len);
+		close_fds(&resp);
+		return(-1);
+	}
+	if (resp.resp.res != RTP_ALLOC_OK) {
+		fprintf(stderr, "themwi-rtp-mgr returned error %u\n",
+			resp.resp.res);
+		close_fds(&resp);
+		return(-1);
+	}
+	if (resp.num_fd != expect_num_fd) {
+		fprintf(stderr,
+"error: themwi-rtp-mgr returned %u descriptors instead of expected %u\n",
+			resp.num_fd, expect_num_fd);
+		close_fds(&resp);
+		return(-1);
+	}
+	switch (ep_type) {
+	case TMGW_EP_TYPE_GSM_ONLY:
+		out->gsm_rtp_fd = resp.fd_buf[0];
+		out->gsm_rtcp_fd = resp.fd_buf[1];
+		break;
+	case TMGW_EP_TYPE_PSTN_ONLY:
+		out->pstn_rtp_fd = resp.fd_buf[0];
+		out->pstn_rtcp_fd = resp.fd_buf[1];
+		break;
+	case TMGW_EP_TYPE_GATEWAY:
+		out->gsm_rtp_fd = resp.fd_buf[0];
+		out->gsm_rtcp_fd = resp.fd_buf[1];
+		out->pstn_rtp_fd = resp.fd_buf[2];
+		out->pstn_rtcp_fd = resp.fd_buf[3];
+	}
+	bcopy(&resp.resp.gsm_addr, &out->gsm_addr,
+		sizeof(struct sockaddr_storage));
+	bcopy(&resp.resp.pstn_addr, &out->pstn_addr,
+		sizeof(struct sockaddr_storage));
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librtpalloc/rtp_alloc_simple.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,13 @@
+/*
+ * This header file defines the library interface for "simple"
+ * RTP endpoint allocation.
+ */
+
+struct rtp_alloc_simple {
+	struct sockaddr_storage gsm_addr;
+	int	gsm_rtp_fd;
+	int	gsm_rtcp_fd;
+	struct sockaddr_storage pstn_addr;
+	int	pstn_rtp_fd;
+	int	pstn_rtcp_fd;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librtpalloc/rtpmgr_resp.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,66 @@
+/*
+ * Here we implement the collect_rtpmgr_resp() function,
+ * which is a wrapper around the mess of recvmsg
+ * with file descriptor passing.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include "../include/rtp_alloc.h"
+#include "rtpmgr_resp.h"
+
+collect_rtpmgr_resp(ctrl_fd, recv_flags, out)
+	struct rtp_alloc_resp_wrap *out;
+{
+	int rc;
+	struct iovec iov;
+	struct msghdr msg;
+	union {
+		char buf[CMSG_SPACE(sizeof(int) * 4)];
+		struct cmsghdr align;
+	} cmsgu;
+	struct cmsghdr *cmsg;
+
+	iov.iov_base = &out->resp;
+	iov.iov_len = sizeof(struct rtp_alloc_resp);
+	bzero(&msg, sizeof msg);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = cmsgu.buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(int) * 4);
+	rc = recvmsg(ctrl_fd, &msg, recv_flags);
+	if (rc < 0)
+		return rc;
+	out->resp_len = rc;
+	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+		if (cmsg->cmsg_level == SOL_SOCKET &&
+		    cmsg->cmsg_type == SCM_RIGHTS)
+			break;
+	}
+	if (cmsg) {
+		switch (cmsg->cmsg_len) {
+		case CMSG_LEN(sizeof(int)):
+			out->num_fd = 1;
+			break;
+		case CMSG_LEN(sizeof(int) * 2):
+			out->num_fd = 2;
+			break;
+		case CMSG_LEN(sizeof(int) * 3):
+			out->num_fd = 3;
+			break;
+		case CMSG_LEN(sizeof(int) * 4):
+			out->num_fd = 4;
+			break;
+		default:
+			out->num_fd = 0;
+		}
+		if (out->num_fd)
+			bcopy(CMSG_DATA(cmsg), out->fd_buf,
+				sizeof(int) * out->num_fd);
+	} else
+		out->num_fd = 0;
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librtpalloc/rtpmgr_resp.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,12 @@
+/*
+ * The structure defined in this header file is returned by the
+ * collect_rtpmgr_resp() function, which is a wrapper around
+ * the mess of recvmsg with file descriptor passing.
+ */
+
+struct rtp_alloc_resp_wrap {
+	struct rtp_alloc_resp resp;
+	unsigned	resp_len;
+	int		fd_buf[4];
+	unsigned	num_fd;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/Makefile	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,14 @@
+CC=	gcc
+CFLAGS=	-O2
+OBJS=	get_header.o grok_from.o out_msg.o primary_parse.o req_supp.o \
+	resp_ident.o sdp_gen.o sdp_parse.o to_tag.o uas_basic.o uri_utils.o
+LIB=	libsip.a
+
+all:	${LIB}
+
+${LIB}:	${OBJS}
+	ar rcu $@ ${OBJS}
+	ranlib $@
+
+clean:
+	rm -f *.[oa] errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/get_header.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,39 @@
+/*
+ * In this module we implement functions for retrieving individual
+ * header fields from struct sip_pkt_rx.
+ */
+
+#include <string.h>
+#include <strings.h>
+#include "parse.h"
+
+char *
+get_single_header(msg, name, altname, dupp)
+	struct sip_pkt_rx *msg;
+	char *name, *altname;
+	int *dupp;
+{
+	unsigned n;
+	char *ret;
+
+	for (n = 0; n < msg->num_hdr_fields; n++) {
+		if (!strcasecmp(msg->hdr_fields[n].field_name, name))
+			break;
+		if (altname &&
+		    !strcasecmp(msg->hdr_fields[n].field_name, altname))
+			break;
+	}
+	if (n >= msg->num_hdr_fields)
+		return 0;
+	ret = msg->hdr_fields[n].field_value;
+	if (!dupp)
+		return ret;
+	for (n++; n < msg->num_hdr_fields; n++) {
+		if (!strcasecmp(msg->hdr_fields[n].field_name, name))
+			*dupp = 1;
+		if (altname &&
+		    !strcasecmp(msg->hdr_fields[n].field_name, altname))
+			*dupp = 1;
+	}
+	return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/grok_from.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,103 @@
+/*
+ * Here we implement further parsing of the From header,
+ * beyond the preliminary step of uas_get_basic_headers().
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include "grok_from.h"
+
+grok_from_header(from_hdr, gfr)
+	char *from_hdr;
+	struct grok_from *gfr;
+{
+	char *cp = from_hdr, *bp;
+	int bracketed;
+
+	if (*cp == '<' || !strncasecmp(cp, "sip:", 4)) {
+		gfr->cnam = 0;
+		gfr->cnam_len = 0;
+		gfr->cnam_quoted = 0;
+	} else {
+		if (*cp == '"') {
+			cp++;
+			gfr->cnam = cp;
+			for (;;) {
+				if (!*cp)
+					return(-1);
+				if (*cp == '"')
+					break;
+				if (*cp++ == '\\') {
+					if (!*cp)
+						return(-1);
+					cp++;
+				}
+			}
+			gfr->cnam_len = cp - gfr->cnam;
+			gfr->cnam_quoted = 1;
+			cp++;
+			while (isspace(*cp))
+				cp++;
+			if (*cp != '<')
+				return(-1);
+		} else {
+			gfr->cnam = cp;
+			for (;;) {
+				if (!*cp)
+					return(-1);
+				if (*cp == '<')
+					break;
+				cp++;
+			}
+			for (bp = cp; bp > gfr->cnam && isspace(bp[-1]); bp--)
+				;
+			gfr->cnam_len = bp - gfr->cnam;
+			gfr->cnam_quoted = 0;
+		}
+	}
+	if (*cp == '<') {
+		cp++;
+		bracketed = 1;
+	} else
+		bracketed = 0;
+	gfr->uri = cp;
+	if (strncasecmp(cp, "sip:", 4))
+		return(-1);
+	cp += 4;
+	gfr->user = cp;
+	for (;;) {
+		if (!*cp || *cp == '<' || *cp == '>')
+			return(-1);
+		if (*cp == '@')
+			break;
+		cp++;
+	}
+	if (cp == gfr->user)
+		return(-1);
+	gfr->user_len = cp - gfr->user;
+	cp++;
+	for (;;) {
+		switch (*cp) {
+		case '\0':
+		case ';':
+			if (bracketed)
+				return(-1);
+			else
+				break;
+		case '>':
+			if (bracketed)
+				break;
+			else
+				return(-1);
+		case '<':
+			return(-1);
+		default:
+			cp++;
+			continue;
+		}
+		break;
+	}
+	gfr->uri_len = cp - gfr->uri;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/grok_from.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,14 @@
+/*
+ * Here we define the structure used to capture the result
+ * of From header field parsing.
+ */
+
+struct grok_from {
+	char		*uri;
+	unsigned	uri_len;
+	char		*user;
+	unsigned	user_len;
+	char		*cnam;
+	unsigned	cnam_len;
+	int		cnam_quoted;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/out_msg.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,88 @@
+/*
+ * Functions for constructing outgoing SIP messages.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include "out_msg.h"
+
+start_request_out_msg(msg, method, uri)
+	struct sip_msg_out *msg;
+	char *method, *uri;
+{
+	unsigned len;
+
+	len = strlen(method) + strlen(uri) + (2 + 7 + 2);
+	if (len + 2 > MAX_SIP_TX_PACKET)
+		return(-1);
+	sprintf(msg->buf, "%s %s SIP/2.0\r\n", method, uri);
+	msg->msg_len = len;
+	return(0);
+}
+
+start_request_out_msg_urilen(msg, method, uri, uri_len)
+	struct sip_msg_out *msg;
+	char *method, *uri;
+	unsigned uri_len;
+{
+	unsigned len;
+
+	len = strlen(method) + uri_len + (2 + 7 + 2);
+	if (len + 2 > MAX_SIP_TX_PACKET)
+		return(-1);
+	sprintf(msg->buf, "%s %.*s SIP/2.0\r\n", method, uri_len, uri);
+	msg->msg_len = len;
+	return(0);
+}
+
+start_response_out_msg(msg, status)
+	struct sip_msg_out *msg;
+	char *status;
+{
+	unsigned len;
+
+	len = strlen(status) + (8 + 2);
+	if (len + 2 > MAX_SIP_TX_PACKET)
+		return(-1);
+	sprintf(msg->buf, "SIP/2.0 %s\r\n", status);
+	msg->msg_len = len;
+	return(0);
+}
+
+out_msg_add_header(msg, name, value)
+	struct sip_msg_out *msg;
+	char *name, *value;
+{
+	unsigned len;
+
+	len = strlen(name) + strlen(value) + 4;
+	if (msg->msg_len + len + 2 > MAX_SIP_TX_PACKET)
+		return(-1);
+	sprintf(msg->buf + msg->msg_len, "%s: %s\r\n", name, value);
+	msg->msg_len += len;
+	return(0);
+}
+
+out_msg_finish(msg)
+	struct sip_msg_out *msg;
+{
+	msg->buf[msg->msg_len++] = '\r';
+	msg->buf[msg->msg_len++] = '\n';
+	return(0);
+}
+
+out_msg_finish_body(msg, body, bodylen)
+	struct sip_msg_out *msg;
+	char *body;
+	unsigned bodylen;
+{
+	msg->buf[msg->msg_len++] = '\r';
+	msg->buf[msg->msg_len++] = '\n';
+	if (msg->msg_len + bodylen > MAX_SIP_TX_PACKET)
+		return(-1);
+	bcopy(body, msg->buf + msg->msg_len, bodylen);
+	msg->msg_len += bodylen;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/out_msg.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,11 @@
+/*
+ * Here we define the structure we are going to use for constructing
+ * outgoing SIP messages (UDP packets).
+ */
+
+#define	MAX_SIP_TX_PACKET	1472
+
+struct sip_msg_out {
+	char		buf[MAX_SIP_TX_PACKET];
+	unsigned	msg_len;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/parse.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,33 @@
+/*
+ * Here we define the structure we are going to use for receiving
+ * and parsing SIP UDP packets.
+ */
+
+#define	MAX_SIP_RX_PACKET	3072
+#define	MAX_HEADER_FIELDS	64
+
+struct sip_parse_hdr {
+	char	*field_name;
+	char	*field_value;
+};
+
+struct sip_pkt_rx {
+	/* recvfrom on UDP socket, input to parser */
+	char		pkt_buffer[MAX_SIP_RX_PACKET];
+	unsigned	pkt_length;
+	/* filled by parser */
+	int		parse_msgtype;
+	char		*req_method;
+	char		*req_uri;
+	unsigned	status_code;
+	char		*status_str;
+	/* header fields */
+	struct sip_parse_hdr hdr_fields[MAX_HEADER_FIELDS];
+	unsigned	num_hdr_fields;
+	/* optional message body */
+	char		*msg_body;
+	unsigned	msg_body_len;
+};
+
+#define	SIP_MSG_TYPE_REQ	1
+#define	SIP_MSG_TYPE_RESP	2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/primary_parse.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,165 @@
+/*
+ * In this module we are going to implement the first stage of
+ * parsing for incoming SIP UDP packets, using struct sip_pkt_rx
+ * defined in parse.h.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "parse.h"
+
+static
+find_crlf(ptr, msg_end, endp, nextp)
+	char *ptr, *msg_end, **endp, **nextp;
+{
+	for (;;) {
+		if (ptr >= msg_end)
+			return(0);
+		switch (*ptr) {
+		case '\0':
+			return(0);
+		case '\n':
+			*endp = ptr;
+			*nextp = ptr + 1;
+			return(1);
+		case '\r':
+			*endp = ptr;
+			ptr++;
+			if (ptr >= msg_end)
+				return(0);
+			if (*ptr != '\n')
+				return(0);
+			*nextp = ptr + 1;
+			return(1);
+		}
+		ptr++;
+	}
+}
+
+static
+try_status_line(msg)
+	struct sip_pkt_rx *msg;
+{
+	if (strncasecmp(msg->pkt_buffer, "SIP/2.0 ", 8))
+		return(0);
+	if (!isdigit(msg->pkt_buffer[8]))
+		return(0);
+	if (!isdigit(msg->pkt_buffer[9]))
+		return(0);
+	if (!isdigit(msg->pkt_buffer[10]))
+		return(0);
+	if (msg->pkt_buffer[11] != ' ')
+		return(0);
+	msg->status_code = atoi(msg->pkt_buffer + 8);
+	msg->status_str = msg->pkt_buffer + 8;
+	return(1);
+}
+
+static
+try_request_line(msg)
+	struct sip_pkt_rx *msg;
+{
+	char *cp;
+
+	cp = msg->pkt_buffer;
+	if (!isupper(*cp))
+		return(0);
+	while (isalnum(*cp))
+		cp++;
+	if (*cp != ' ')
+		return(0);
+	msg->req_method = msg->pkt_buffer;
+	*cp++ = '\0';
+	msg->req_uri = cp;
+	while (*cp && !isspace(*cp))
+		cp++;
+	if (*cp != ' ')
+		return(0);
+	*cp++ = '\0';
+	if (strcasecmp(cp, "SIP/2.0"))
+		return(0);
+	else
+		return(1);
+}
+
+static void
+trim_trailing_spaces(sp)
+	char *sp;
+{
+	char *ep;
+
+	ep = index(sp, '\0');
+	while (ep > sp && isspace(ep[-1]))
+		ep--;
+	*ep = '\0';
+}
+
+parse_incoming_sip_msg(msg)
+	struct sip_pkt_rx *msg;
+{
+	char *msg_end = msg->pkt_buffer + msg->pkt_length;
+	char *cp, *endp, *nextp, *sp;
+	unsigned hdr_cnt;
+	int rc;
+
+	/* begin by isolating the Request-Line or Status-Line */
+	rc = find_crlf(msg->pkt_buffer, msg_end, &endp, &nextp);
+	if (!rc)
+		return(-1);
+	*endp = '\0';
+	if (try_status_line(msg))
+		msg->parse_msgtype = SIP_MSG_TYPE_RESP;
+	else if (try_request_line(msg))
+		msg->parse_msgtype = SIP_MSG_TYPE_REQ;
+	else
+		return(-1);
+	/* now preparse header fields */
+	cp = nextp;
+	for (hdr_cnt = 0; ; ) {
+		rc = find_crlf(cp, msg_end, &endp, &nextp);
+		if (!rc)
+			return(-1);
+		if (endp == cp)		/* final CRLF? */
+			break;
+		if (!isalpha(*cp))
+			return(-1);
+		if (hdr_cnt >= MAX_HEADER_FIELDS)
+			return(-2);
+		msg->hdr_fields[hdr_cnt].field_name = cp;
+		while (cp < endp && (isalnum(*cp) || *cp == '-'))
+			cp++;
+		if (cp >= endp)
+			return(-1);
+		if (*cp == ':')
+			*cp++ = '\0';
+		else if (isspace(*cp)) {
+			*cp++ = '\0';
+			while (cp < endp && isspace(*cp))
+				cp++;
+			if (cp >= endp)
+				return(-1);
+			if (*cp++ != ':')
+				return(-1);
+		} else
+			return(-1);
+		sp = cp;
+		cp = nextp;
+		while (cp < msg_end && (*cp == ' ' || *cp == '\t')) {
+			rc = find_crlf(cp, msg_end, &endp, &nextp);
+			if (!rc)
+				return(-1);
+			cp = nextp;
+		}
+		*endp = '\0';
+		while (isspace(*sp))
+			sp++;
+		trim_trailing_spaces(sp);
+		msg->hdr_fields[hdr_cnt++].field_value = sp;
+	}
+	msg->num_hdr_fields = hdr_cnt;
+	msg->msg_body = nextp;
+	msg->msg_body_len = msg_end - nextp;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/req_supp.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,77 @@
+/*
+ * Here we parse Require and Supported headers in a SIP request,
+ * checking whether our supported extensions are also supported
+ * or required by the client, and catching any client requirements
+ * which we don't support.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include "parse.h"
+#include "req_supp.h"
+
+parse_require_supported(msg, lsupp, nlsupp, unsupp)
+	struct sip_pkt_rx *msg;
+	struct supported_ext *lsupp;
+	unsigned nlsupp;
+	char **unsupp;
+{
+	struct sip_parse_hdr *hdr, *endhdr;
+	char *cp, *np;
+	unsigned n;
+
+	endhdr = msg->hdr_fields + msg->num_hdr_fields;
+	/* check Require first */
+	for (hdr = msg->hdr_fields; hdr < endhdr; hdr++) {
+		if (strcasecmp(hdr->field_name, "Require"))
+			continue;
+		cp = hdr->field_value;
+		for (;;) {
+			while (isspace(*cp) || *cp == ',')
+				cp++;
+			if (!*cp)
+				break;
+			np = cp;
+			while (*cp && !isspace(*cp) && *cp != ',')
+				cp++;
+			if (*cp)
+				*cp++ = '\0';
+			for (n = 0; n < nlsupp; n++) {
+				if (!strcasecmp(np, lsupp[n].name)) {
+					*lsupp[n].req_flag = 1;
+					break;
+				} else {
+					if (unsupp)
+						*unsupp = np;
+					return(-1);
+				}
+			}
+		}
+	}
+	/* now check Supported */
+	for (hdr = msg->hdr_fields; hdr < endhdr; hdr++) {
+		if (strcasecmp(hdr->field_name, "Supported") &&
+		    strcasecmp(hdr->field_name, "k"))
+			continue;
+		cp = hdr->field_value;
+		for (;;) {
+			while (isspace(*cp) || *cp == ',')
+				cp++;
+			if (!*cp)
+				break;
+			np = cp;
+			while (*cp && !isspace(*cp) && *cp != ',')
+				cp++;
+			if (*cp)
+				*cp++ = '\0';
+			for (n = 0; n < nlsupp; n++) {
+				if (!strcasecmp(np, lsupp[n].name)) {
+					*lsupp[n].sup_flag = 1;
+					break;
+				}
+			}
+		}
+	}
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/req_supp.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,9 @@
+/*
+ * Data structure for parsing Require and Supported header fields
+ */
+
+struct supported_ext {
+	char	*name;
+	int	*req_flag;
+	int	*sup_flag;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/resp_ident.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,52 @@
+/*
+ * In this module we implement an essential step in the process
+ * of handling incoming SIP response messages: extracting Call-ID
+ * and CSeq headers and preparsing the latter, providing key info
+ * for matching these incoming responses with call state and
+ * with outstanding UAC requests.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "parse.h"
+#include "resp_ident.h"
+
+extern char *get_single_header();
+
+sip_resp_extract_ident(msg, id)
+	struct sip_pkt_rx *msg;
+	struct sip_resp_ident *id;
+{
+	char *hval, *cp;
+	int dup_flag = 0;
+
+	hval = get_single_header(msg, "Call-ID", "i", &dup_flag);
+	if (!hval || dup_flag) {
+		id->error_field = "Call-ID";
+		return(-1);
+	}
+	id->call_id = hval;
+	hval = get_single_header(msg, "CSeq", (char *) 0, &dup_flag);
+	if (!hval || dup_flag) {
+bad_cseq:	id->error_field = "CSeq";
+		return(-1);
+	}
+	if (!isdigit(*hval))
+		goto bad_cseq;
+	id->cseq_num = strtoul(hval, &cp, 10);
+	if (!isspace(*cp))
+		goto bad_cseq;
+	while (isspace(*cp))
+		cp++;
+	if (!isupper(*cp))
+		goto bad_cseq;
+	id->cseq_method = cp;
+	while (isalnum(*cp))
+		cp++;
+	if (*cp)
+		goto bad_cseq;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/resp_ident.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,13 @@
+/*
+ * Here we define struct sip_resp_ident, a structure that captures
+ * Call-ID and CSeq headers from initial parsing of SIP responses,
+ * allowing these responses to be matched with call state and
+ * with UAC requests that elicited them.
+ */
+
+struct sip_resp_ident {
+	char		*call_id;
+	unsigned	cseq_num;
+	char		*cseq_method;
+	char		*error_field;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/sdp.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,24 @@
+/*
+ * This header file defines structures to be used for SDP
+ * parsing and generation.
+ */
+
+struct sdp_parse {
+	struct in_addr	ip_addr;
+	unsigned	audio_port;
+	unsigned	codec_mask;
+};
+
+struct sdp_gen {
+	unsigned	session_id;
+	unsigned	version;
+	struct in_addr	owner_ip;
+	struct in_addr	conn_ip;
+	unsigned	conn_port;
+	unsigned	codec_mask;
+};
+
+#define	SDP_CODEC_MASK_PCMU		1
+#define	SDP_CODEC_MASK_PCMA		2
+#define	SDP_CODEC_MASK_BOTH		3
+#define	SDP_CODEC_MASK_PCMA_PREF	4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/sdp_gen.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,81 @@
+/*
+ * Here we implement generation of outgoing SDP descriptions.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "sdp.h"
+#include "out_msg.h"
+
+format_sdp_in_buffer(gen, buf, lenptr)
+	struct sdp_gen *gen;
+	char *buf;
+	unsigned *lenptr;
+{
+	char *dp, *codec_list;
+
+	dp = buf;
+	strcpy(dp, "v=0\r\n");
+	dp += 5;
+	sprintf(dp, "o=- %u %u IN IP4 %s\r\n", gen->session_id, gen->version,
+		inet_ntoa(gen->owner_ip));
+	dp = index(dp, '\0');
+	strcpy(dp, "s=-\r\n");
+	dp += 5;
+	sprintf(dp, "c=IN IP4 %s\r\n", inet_ntoa(gen->conn_ip));
+	dp = index(dp, '\0');
+	strcpy(dp, "t=0 0\r\n");
+	dp += 7;
+	switch (gen->codec_mask) {
+	case SDP_CODEC_MASK_PCMU:
+		codec_list = "0";
+		break;
+	case SDP_CODEC_MASK_PCMA:
+		codec_list = "8";
+		break;
+	case SDP_CODEC_MASK_BOTH:
+		codec_list = "0 8";
+		break;
+	case SDP_CODEC_MASK_BOTH | SDP_CODEC_MASK_PCMA_PREF:
+		codec_list = "8 0";
+		break;
+	default:
+		return(-2);
+	}
+	sprintf(dp, "m=audio %u RTP/AVP %s\r\n", gen->conn_port, codec_list);
+	dp = index(dp, '\0');
+	if (gen->codec_mask & SDP_CODEC_MASK_PCMU) {
+		strcpy(dp, "a=rtpmap:0 PCMU/8000\r\n");
+		dp += 22;
+	}
+	if (gen->codec_mask & SDP_CODEC_MASK_PCMA) {
+		strcpy(dp, "a=rtpmap:8 PCMA/8000\r\n");
+		dp += 22;
+	}
+	strcpy(dp, "a=sendrecv\r\n");
+	dp += 12;
+	strcpy(dp, "a=ptime:20\r\n");
+	dp += 12;
+	*lenptr = (dp - buf);
+	return(0);
+}
+
+out_msg_finish_sdp(msg, gen)
+	struct sip_msg_out *msg;
+	struct sdp_gen *gen;
+{
+	char sdp_buf[256];
+	unsigned sdp_len;
+	int rc;
+
+	rc = format_sdp_in_buffer(gen, sdp_buf, &sdp_len);
+	if (rc < 0)
+		return rc;
+	return out_msg_finish_body(msg, sdp_buf, sdp_len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/sdp_parse.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,104 @@
+/*
+ * Here we implement the function that parses received SDP descriptions.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "../include/tmgw_const.h"
+#include "sdp.h"
+
+parse_incoming_sdp(body, bodylen, dist)
+	char *body;
+	unsigned bodylen;
+	struct sdp_parse *dist;
+{
+	char *cp, *endp;
+	int got_c = 0, got_m = 0, prefer_pcma = 0;
+	unsigned codec;
+
+	cp = body;
+	endp = body + bodylen;
+	while (cp < endp) {
+		if (!islower(cp[0]) || cp[1] != '=')
+			return(-1);
+		switch (cp[0]) {
+		case 'c':
+			if (got_c)
+				return(-1);
+			got_c = 1;
+			if (strncmp(cp + 2, "IN IP4 ", 7))
+				return(-1);
+			cp += 9;
+			dist->ip_addr.s_addr = inet_addr(cp);
+			if (dist->ip_addr.s_addr == INADDR_NONE)
+				return(-1);
+			break;
+		case 'm':
+			if (got_m)
+				return(-1);
+			got_m = 1;
+			if (strncmp(cp + 2, "audio ", 6))
+				return(-1);
+			cp += 8;
+			if (!isdigit(*cp))
+				return(-1);
+			dist->audio_port = strtoul(cp, &cp, 10);
+			if (strncmp(cp, " RTP/AVP", 8))
+				return(-1);
+			cp += 8;
+			dist->codec_mask = 0;
+			for (;;) {
+				if (*cp == '\r' || *cp == '\n')
+					break;
+				if (*cp++ != ' ')
+					return(-1);
+				codec = strtoul(cp, &cp, 10);
+				switch (codec) {
+				case PSTN_CODEC_PCMU:
+					dist->codec_mask |= SDP_CODEC_MASK_PCMU;
+					break;
+				case PSTN_CODEC_PCMA:
+					if (!dist->codec_mask)
+						prefer_pcma = 1;
+					dist->codec_mask |= SDP_CODEC_MASK_PCMA;
+					break;
+				}
+			}
+			if (dist->codec_mask == SDP_CODEC_MASK_BOTH &&
+			    prefer_pcma)
+				dist->codec_mask |= SDP_CODEC_MASK_PCMA_PREF;
+			break;
+		default:
+			cp += 2;
+		}
+		for (;;) {
+			if (cp >= endp)
+				return(-1);
+			if (!*cp)
+				return(-1);
+			if (*cp == '\n') {
+				cp++;
+				break;
+			}
+			if (*cp == '\r') {
+				cp++;
+				if (cp >= endp)
+					return(-1);
+				if (*cp++ != '\n')
+					return(-1);
+				break;
+			}
+			cp++;
+		}
+	}
+	if (got_c && got_m)
+		return(0);
+	else
+		return(-1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/to_tag.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,76 @@
+/*
+ * The function implemented in this module extracts the tag
+ * from the To header of a SIP destination server's response.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "parse.h"
+
+extern char *get_single_header();
+
+char *
+extract_to_tag(msg, expect_uri)
+	struct sip_pkt_rx *msg;
+	char *expect_uri;
+{
+	char *cp, *tag;
+	int bracketed, c;
+	unsigned expect_uri_len;
+
+	cp = get_single_header(msg, "To", "t", (int *) 0);
+	if (!cp)
+		return 0;
+	if (*cp == '<') {
+		cp++;
+		bracketed = 1;
+	} else
+		bracketed = 0;
+	expect_uri_len = strlen(expect_uri);
+	if (strncasecmp(cp, expect_uri, expect_uri_len))
+		return 0;
+	cp += expect_uri_len;
+	if (bracketed) {
+		if (*cp++ != '>')
+			return 0;
+	}
+	while (isspace(*cp))
+		cp++;
+	if (*cp++ != ';')
+		return 0;
+	while (isspace(*cp))
+		cp++;
+	if (strncasecmp(cp, "tag", 3))
+		return 0;
+	cp += 3;
+	while (isspace(*cp))
+		cp++;
+	if (*cp++ != '=')
+		return 0;
+	while (isspace(*cp))
+		cp++;
+	tag = cp;
+	for (; c = *cp; cp++) {
+		switch (c) {
+		case '-':
+		case '.':
+		case '!':
+		case '%':
+		case '*':
+		case '_':
+		case '+':
+		case '`':
+		case '\'':
+		case '~':
+			continue;
+		default:
+			if (isalnum(c))
+				continue;
+			return 0;
+		}
+	}
+	return tag;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/uas_basic.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,88 @@
+/*
+ * Here we implement some essential UAS functions.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "parse.h"
+#include "uas_basic.h"
+#include "out_msg.h"
+
+extern char *get_single_header();
+
+uas_get_basic_headers(msg, ess)
+	struct sip_pkt_rx *msg;
+	struct uas_parse_hdrs *ess;
+{
+	char *hval, *cp;
+	int dup_flag = 0;
+
+	hval = get_single_header(msg, "Call-ID", "i", &dup_flag);
+	if (!hval || dup_flag) {
+		ess->error_field = "Call-ID";
+		return(-1);
+	}
+	ess->call_id = hval;
+	hval = get_single_header(msg, "CSeq", (char *) 0, &dup_flag);
+	if (!hval || dup_flag) {
+bad_cseq:	ess->error_field = "CSeq";
+		return(-1);
+	}
+	if (!isdigit(*hval))
+		goto bad_cseq;
+	ess->cseq_num = strtoul(hval, &cp, 10);
+	if (*cp) {
+		if (!isspace(*cp))
+			goto bad_cseq;
+		while (isspace(*cp))
+			cp++;
+		if (strcmp(cp, msg->req_method))
+			goto bad_cseq;
+	}
+	hval = get_single_header(msg, "From", "f", &dup_flag);
+	if (!hval || dup_flag) {
+		ess->error_field = "From";
+		return(-1);
+	}
+	ess->from = hval;
+	hval = get_single_header(msg, "To", "t", &dup_flag);
+	if (!hval || dup_flag) {
+		ess->error_field = "To";
+		return(-1);
+	}
+	ess->to = hval;
+	hval = get_single_header(msg, "Via", "v", &dup_flag);
+	if (!hval || dup_flag) {
+		ess->error_field = "Via";
+		return(-1);
+	}
+	ess->via = hval;
+	return(0);
+}
+
+add_resp_basic_headers(msg, ess, req_method)
+	struct sip_msg_out *msg;
+	struct uas_parse_hdrs *ess;
+	char *req_method;
+{
+	char cseq_str[80];
+	int rc;
+
+	rc = out_msg_add_header(msg, "From", ess->from);
+	if (rc < 0)
+		return rc;
+	rc = out_msg_add_header(msg, "To", ess->to);
+	if (rc < 0)
+		return rc;
+	rc = out_msg_add_header(msg, "Call-ID", ess->call_id);
+	if (rc < 0)
+		return rc;
+	sprintf(cseq_str, "%u %.64s", ess->cseq_num, req_method);
+	rc = out_msg_add_header(msg, "CSeq", cseq_str);
+	if (rc < 0)
+		return rc;
+	return out_msg_add_header(msg, "Via", ess->via);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/uas_basic.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,14 @@
+/*
+ * Here we define struct uas_parse_hdrs, a structure that captures
+ * the 5 essential header fields that must be present in every
+ * received request in order for us to be able to respond to it.
+ */
+
+struct uas_parse_hdrs {
+	char		*from;
+	char		*to;
+	char		*call_id;
+	unsigned	cseq_num;
+	char		*via;
+	char		*error_field;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libsip/uri_utils.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,32 @@
+/*
+ * Some utility functions for working with SIP URIs.
+ */
+
+#include <string.h>
+#include <strings.h>
+
+user_from_sip_uri(uri, outbuf, maxlen)
+	char *uri, *outbuf;
+	unsigned maxlen;
+{
+	char *cp, *dp;
+	unsigned n;
+
+	if (strncasecmp(uri, "sip:", 4))
+		return(-1);
+	cp = uri + 4;
+	dp = outbuf;
+	n = 0;
+	for (;;) {
+		if (!*cp)
+			return(-1);
+		if (*cp == '@')
+			break;
+		if (n >= maxlen)
+			return(-2);
+		*dp++ = *cp++;
+		n++;
+	}
+	*dp = '\0';
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/Makefile	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,13 @@
+CC=	gcc
+CFLAGS=	-O2
+OBJS=	bitfunc.o crc8gen.o sockinit.o tfo_msg_enc.o
+LIB=	libutil.a
+
+all:	${LIB}
+
+${LIB}:	${OBJS}
+	ar rcu $@ ${OBJS}
+	ranlib $@
+
+clean:
+	rm -f *.[oa] errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/bitfunc.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,14 @@
+/*
+ * Some bit manipulation functions.
+ */
+
+#include <stdint.h>
+#include "osmo_bits.h"
+
+void uint16_to_bits(uint16_t word, ubit_t *out, unsigned nbits)
+{
+	unsigned n;
+
+	for (n = 0; n < nbits; n++)
+		out[n] = (word >> (nbits-n-1)) & 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/crc8gen.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,115 @@
+/*! \file crc8gen.c
+ * Osmocom generic CRC routines (for max 8 bits poly). */
+/*
+ * Copyright (C) 2011  Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*! \addtogroup crc
+ *  @{
+ *  Osmocom generic CRC routines (for max 8 bits poly).
+ *
+ *  \file crc8gen.c.tpl */
+
+#include <stdint.h>
+#include "osmo_bits.h"
+
+/*! Compute the CRC value of a given array of hard-bits
+ *  \param[in] code The CRC code description to apply
+ *  \param[in] in Array of hard bits
+ *  \param[in] len Length of the array of hard bits
+ *  \returns The CRC value
+ */
+uint8_t
+osmo_crc8gen_compute_bits(const struct osmo_crc8gen_code *code,
+                           const ubit_t *in, int len)
+{
+	const uint8_t poly = code->poly;
+	uint8_t crc = code->init;
+	int i, n = code->bits-1;
+
+	for (i=0; i<len; i++) {
+		uint8_t bit = in[i] & 1;
+		crc ^= (bit << n);
+		if (crc & ((uint8_t)1 << n)) {
+			crc <<= 1;
+			crc ^= poly;
+		} else {
+			crc <<= 1;
+		}
+		crc &= ((uint8_t)1 << code->bits) - 1;
+	}
+
+	crc ^= code->remainder;
+
+	return crc;
+}
+
+
+/*! Checks the CRC value of a given array of hard-bits
+ *  \param[in] code The CRC code description to apply
+ *  \param[in] in Array of hard bits
+ *  \param[in] len Length of the array of hard bits
+ *  \param[in] crc_bits Array of hard bits with the alleged CRC
+ *  \returns 0 if CRC matches. 1 in case of error.
+ *
+ * The crc_bits array must have a length of code->len
+ */
+int
+osmo_crc8gen_check_bits(const struct osmo_crc8gen_code *code,
+                         const ubit_t *in, int len, const ubit_t *crc_bits)
+{
+	uint8_t crc;
+	int i;
+
+	crc = osmo_crc8gen_compute_bits(code, in, len);
+
+	for (i=0; i<code->bits; i++)
+		if (crc_bits[i] ^ ((crc >> (code->bits-i-1)) & 1))
+			return 1;
+
+	return 0;
+}
+
+
+/*! Computes and writes the CRC value of a given array of bits
+ *  \param[in] code The CRC code description to apply
+ *  \param[in] in Array of hard bits
+ *  \param[in] len Length of the array of hard bits
+ *  \param[in] crc_bits Array of hard bits to write the computed CRC to
+ *
+ * The crc_bits array must have a length of code->len
+ */
+void
+osmo_crc8gen_set_bits(const struct osmo_crc8gen_code *code,
+                       const ubit_t *in, int len, ubit_t *crc_bits)
+{
+	uint8_t crc;
+	int i;
+
+	crc = osmo_crc8gen_compute_bits(code, in, len);
+
+	for (i=0; i<code->bits; i++)
+		crc_bits[i] = ((crc >> (code->bits-i-1)) & 1);
+}
+
+/*! @} */
+
+/* vim: set syntax=c: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/osmo_bits.h	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,35 @@
+/*
+ * This include file has been put together from Osmocom (specifically
+ * libosmocore) header files, containing definitions for bit vector
+ * manipulation and CRC functions.
+ */
+
+/* from bits.h */
+
+/*! unpacked bit (0 or 1): 1 bit per byte */
+typedef uint8_t ubit_t;
+
+/* from crc8gen.h */
+
+/*! structure describing a given CRC code of max 8 bits */
+struct osmo_crc8gen_code {
+	int bits;          /*!< Actual number of bits of the CRC */
+	uint8_t poly;      /*!< Polynom (normal representation, MSB omitted */
+	uint8_t init;      /*!< Initialization value of the CRC state */
+	uint8_t remainder; /*!< Remainder of the CRC (final XOR) */
+};
+
+uint8_t osmo_crc8gen_compute_bits(const struct osmo_crc8gen_code *code,
+                                  const ubit_t *in, int len);
+int osmo_crc8gen_check_bits(const struct osmo_crc8gen_code *code,
+                            const ubit_t *in, int len, const ubit_t *crc_bits);
+void osmo_crc8gen_set_bits(const struct osmo_crc8gen_code *code,
+                           const ubit_t *in, int len, ubit_t *crc_bits);
+
+/*
+ * Themyscira-added (not Osmocom-originating) functions
+ * that use the ubit_t defined type that (to our knowledge)
+ * was invented by Osmocom.
+ */
+
+void uint16_to_bits(uint16_t word, ubit_t *out, unsigned nbits);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/sockinit.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,40 @@
+/*
+ * This library module implements a function that helps initialize
+ * sockaddr for bind or connect operations on UNIX domain sockets.
+ *
+ * Back when I programmed under 4.3BSD UNIX, this operation was simple
+ * and straightforward - but under "modern" Unixes, it appears to be
+ * a complex affair, given the messy code (originally copied from
+ * Osmocom) that appears in FreeCalypso host tools for the rvinterf
+ * local socket interface.  Hence I am factoring that mess out into
+ * its own library function this time around.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <strings.h>
+
+void
+fill_sockaddr_un(pathname, sunp, lenp)
+	char *pathname;
+	struct sockaddr_un *sunp;
+	unsigned *lenp;
+{
+	/* local socket binding voodoo copied from osmocon */
+	sunp->sun_family = AF_UNIX;
+	strncpy(sunp->sun_path, pathname, sizeof(sunp->sun_path));
+	sunp->sun_path[sizeof(sunp->sun_path) - 1] = '\0';
+	/* we use the same magic that X11 uses in Xtranssock.c for
+	 * calculating the proper length of the sockaddr */
+#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
+	sunp->sun_len = strlen(sunp->sun_path);
+#endif
+#if defined(BSD44SOCKETS) || defined(SUN_LEN)
+	*lenp = SUN_LEN(sunp);
+#else
+	*lenp = strlen(sunp->sun_path) +
+		offsetof(struct sockaddr_un, sun_path) + 1;
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/tfo_msg_enc.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,33 @@
+/*
+ * The function implemented in this module encodes a GSM 08.62 Extension_Block.
+ */
+
+#include <stdint.h>
+#include "osmo_bits.h"
+
+/*
+ * EFR TRAU parity
+ *
+ * g(x) = x^3 + x^1 + 1
+ */
+static const struct osmo_crc8gen_code gsm0860_efr_crc3 = {
+	.bits = 3,
+	.poly = 0x3,
+	.init = 0x0,
+	.remainder = 0x7,
+};
+
+void
+encode_tfo_ext_words(bits_2_10, bits_12_15, ex, out)
+	unsigned bits_2_10, bits_12_15, ex;
+	uint16_t *out;
+{
+	ubit_t crc_in[13];
+	uint8_t crc;
+
+	uint16_to_bits(bits_2_10, crc_in, 9);
+	uint16_to_bits(bits_12_15, crc_in + 9, 4);
+	crc = osmo_crc8gen_compute_bits(&gsm0860_efr_crc3, crc_in, 13);
+	out[0] = bits_2_10;
+	out[1] = (bits_12_15 << 5) | (crc << 2) | ex;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/Makefile	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,18 @@
+CC=	gcc
+CFLAGS=	-O2
+PROG=	sipout-test-voice
+OBJS=	bye_in.o disc_cmd.o main.o readconf.o reinvite.o rtp_rx.o rtp_tx.o \
+	sdp_in.o sip_log.o sip_udp.o uac.o uas.o user_cmd.o
+LIBS=	../libsip/libsip.a ../librtpalloc/librtpalloc.a ../libutil/libutil.a
+INSTBIN=/opt/themwi/bin
+
+all:	${PROG}
+
+${PROG}: ${OBJS} ${LIBS}
+	${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS}
+
+install:
+	install -c -m 755 ${PROG} ${INSTBIN}
+
+clean:
+	rm -f *.o ${PROG} errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/bye_in.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,70 @@
+/*
+ * Here we handle incoming BYE requests in the UAS role.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "../libsip/parse.h"
+#include "../libsip/uas_basic.h"
+#include "../libsip/out_msg.h"
+
+extern char call_id[];
+extern int rtp_out_enable;
+
+static void
+bye_correct_call(req, ess, sin)
+	struct sip_pkt_rx *req;
+	struct uas_parse_hdrs *ess;
+	struct sockaddr_in *sin;
+{
+	struct sip_msg_out resp;
+	int rc;
+
+	printf("Received BYE for our call, responding with 200\n");
+	rtp_out_enable = 0;
+	start_response_out_msg(&resp, "200 OK");
+	rc = add_resp_basic_headers(&resp, ess, req->req_method);
+	if (rc < 0) {
+		fprintf(stderr, "sending 200 response: msg length exceeded\n");
+		return;
+	}
+	out_msg_finish(&resp);
+	sip_tx_packet(&resp, sin);
+}
+
+static void
+bye_unknown_call(req, ess, sin)
+	struct sip_pkt_rx *req;
+	struct uas_parse_hdrs *ess;
+	struct sockaddr_in *sin;
+{
+	struct sip_msg_out resp;
+	int rc;
+
+	printf("Received BYE for unknown call, responding with 481\n");
+	start_response_out_msg(&resp, "481 Call-ID not found");
+	rc = add_resp_basic_headers(&resp, ess, req->req_method);
+	if (rc < 0) {
+		fprintf(stderr, "sending 481 response: msg length exceeded\n");
+		return;
+	}
+	out_msg_finish(&resp);
+	sip_tx_packet(&resp, sin);
+}
+
+void
+handle_bye_req(req, ess, sin)
+	struct sip_pkt_rx *req;
+	struct uas_parse_hdrs *ess;
+	struct sockaddr_in *sin;
+{
+	if (!strcmp(ess->call_id, call_id))
+		bye_correct_call(req, ess, sin);
+	else
+		bye_unknown_call(req, ess, sin);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/disc_cmd.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,55 @@
+/*
+ * In this module we implement user-driven sending of CANCEL and BYE
+ * disconnection requests.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "../libsip/out_msg.h"
+
+extern struct sockaddr_in sip_dest_sin;
+extern char to_uri[];
+extern int rtp_out_enable;
+
+send_cancel_req()
+{
+	struct sip_msg_out msg;
+	int rc;
+
+	rtp_out_enable = 0;
+	rc = start_request_out_msg(&msg, "CANCEL", to_uri);
+	if (rc < 0) {
+msg_size_err:	fprintf(stderr, "composing CANCEL message: size error\n");
+		return(-1);
+	}
+	rc = add_req_boilerplate(&msg, "1 CANCEL", 0);
+	if (rc < 0)
+		goto msg_size_err;
+	out_msg_finish(&msg);
+	sip_tx_packet(&msg, &sip_dest_sin);
+	return(0);
+}
+
+send_bye_req()
+{
+	struct sip_msg_out msg;
+	int rc;
+
+	rtp_out_enable = 0;
+	rc = start_request_out_msg(&msg, "BYE", to_uri);
+	if (rc < 0) {
+msg_size_err:	fprintf(stderr, "composing BYE message: size error\n");
+		return(-1);
+	}
+	rc = add_req_boilerplate(&msg, "2 BYE", 1);
+	if (rc < 0)
+		goto msg_size_err;
+	out_msg_finish(&msg);
+	sip_tx_packet(&msg, &sip_dest_sin);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/main.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,200 @@
+/*
+ * This is the main module for sip-manual-out test program.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.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>
+#include "../libsip/out_msg.h"
+#include "../libsip/sdp.h"
+
+extern int sip_socket;
+extern struct in_addr sip_bind_ip, sip_dest_ip;
+extern unsigned sip_bind_port, sip_dest_port;
+extern char sip_dest_domain[];
+extern struct sockaddr_in rtp_local_addr;
+extern int rtp_udp_fd, rtcp_udp_fd;
+extern int rtp_out_enable;
+
+struct sockaddr_in sip_dest_sin;
+char from_uri[128], to_uri[128], call_id[128];
+struct timeval cur_event_time;
+unsigned max_forwards = 70;
+int declare_100rel_supp;
+int pcma_codec_pref, pcma_codec_force;
+
+send_invite_req()
+{
+	struct sip_msg_out msg;
+	struct sdp_gen sdp;
+	int rc;
+
+	rc = start_request_out_msg(&msg, "INVITE", to_uri);
+	if (rc < 0) {
+msg_size_err:	fprintf(stderr, "composing INVITE req: msg size error\n");
+		exit(1);
+	}
+	rc = add_req_boilerplate(&msg, "1 INVITE", 0);
+	if (rc < 0)
+		goto msg_size_err;
+	rc = add_contact_header(&msg);
+	if (rc < 0)
+		goto msg_size_err;
+	if (declare_100rel_supp) {
+		rc = out_msg_add_header(&msg, "Supported", "100rel");
+		if (rc < 0)
+			goto msg_size_err;
+	}
+	rc = out_msg_add_header(&msg, "Content-Type", "application/sdp");
+	if (rc < 0)
+		goto msg_size_err;
+	bzero(&sdp, sizeof sdp);
+	sdp.conn_ip = rtp_local_addr.sin_addr;
+	sdp.conn_port = ntohs(rtp_local_addr.sin_port);
+	if (pcma_codec_force)
+		sdp.codec_mask = SDP_CODEC_MASK_PCMA;
+	else {
+		sdp.codec_mask = SDP_CODEC_MASK_BOTH;
+		if (pcma_codec_pref)
+			sdp.codec_mask |= SDP_CODEC_MASK_PCMA_PREF;
+	}
+	sdp.session_id = sdp.conn_port << 16;
+	sdp.owner_ip = sip_bind_ip;
+	rc = out_msg_finish_sdp(&msg, &sdp);
+	if (rc < 0)
+		goto msg_size_err;
+	sip_tx_packet(&msg, &sip_dest_sin);
+}
+
+static void
+preliminary_proc(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	extern char *optarg;
+	char *logfile;
+	int opt, rc;
+
+	logfile = 0;
+	while ((opt = getopt(argc, argv, "aAl:m:r")) != EOF) {
+		switch (opt) {
+		case 'a':
+			pcma_codec_pref = 1;
+			continue;
+		case 'A':
+			pcma_codec_force = 1;
+			continue;
+		case 'l':
+			logfile = optarg;
+			continue;
+		case 'm':
+			max_forwards = atoi(optarg);
+			continue;
+		case 'r':
+			declare_100rel_supp = 1;
+			continue;
+		default:
+		usage:
+			fprintf(stderr,
+			"usage: %s [options] dest-conf from-num to-num\n",
+				argv[0]);
+			exit(1);
+		}
+	}
+	if (argc != optind + 3)
+		goto usage;
+	read_config_file(argv[optind]);
+	open_sip_udp_socket();
+	obtain_rtp_endp();
+	sip_dest_sin.sin_family = AF_INET;
+	sip_dest_sin.sin_addr = sip_dest_ip;
+	sip_dest_sin.sin_port = htons(sip_dest_port);
+	sprintf(from_uri, "<sip:%s@%s>;tag=out%u", argv[optind+1],
+		inet_ntoa(sip_bind_ip), ntohs(rtp_local_addr.sin_port));
+	sprintf(to_uri, "sip:%s@%s", argv[optind+2], sip_dest_domain);
+	if (logfile) {
+		rc = open_sip_log_file(logfile);
+		if (rc < 0)
+			exit(1);	/* error msg already printed */
+	}
+}
+
+main(argc, argv)
+	char **argv;
+{
+	fd_set fds;
+	struct timeval next_rtp_out, timeout;
+	int rc, max_fd, rtp_out_running;
+
+	preliminary_proc(argc, argv);
+	gettimeofday(&cur_event_time, 0);
+	sprintf(call_id, "%08u_%u@%s",
+		(unsigned)(cur_event_time.tv_sec % 100000000),
+		ntohs(rtp_local_addr.sin_port), inet_ntoa(sip_bind_ip));
+	send_invite_req();
+	/* main select loop */
+	max_fd = sip_socket;
+	if (rtp_udp_fd > max_fd)
+		max_fd = rtp_udp_fd;
+	if (rtcp_udp_fd > max_fd)
+		max_fd = rtcp_udp_fd;
+	rtp_out_running = 0;
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(0, &fds);
+		FD_SET(sip_socket, &fds);
+		FD_SET(rtp_udp_fd, &fds);
+		FD_SET(rtcp_udp_fd, &fds);
+		if (rtp_out_enable) {
+			if (!rtp_out_running) {
+				printf("Starting RTP output\n");
+				bcopy(&cur_event_time, &next_rtp_out,
+					sizeof(struct timeval));
+				rtp_out_running = 1;
+			}
+			if (timercmp(&cur_event_time, &next_rtp_out, <))
+				timersub(&next_rtp_out, &cur_event_time,
+					 &timeout);
+			else
+				timerclear(&timeout);
+			rc = select(max_fd+1, &fds, 0, 0, &timeout);
+		} else {
+			if (rtp_out_running) {
+				printf("Stopping RTP output\n");
+				rtp_out_running = 0;
+			}
+			rc = select(max_fd+1, &fds, 0, 0, 0);
+		}
+		if (rc < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			exit(1);
+		}
+		gettimeofday(&cur_event_time, 0);
+		if (FD_ISSET(0, &fds))
+			select_stdin();
+		if (FD_ISSET(sip_socket, &fds))
+			sip_socket_select();
+		if (FD_ISSET(rtp_udp_fd, &fds))
+			rtp_rx_select();
+		if (FD_ISSET(rtcp_udp_fd, &fds))
+			rtcp_rx_select();
+		if (rtp_out_running && (rc == 0)) {
+			generate_rtp_packet();
+			next_rtp_out.tv_usec += 20000;
+			if (next_rtp_out.tv_usec >= 1000000) {
+				next_rtp_out.tv_sec++;
+				next_rtp_out.tv_usec -= 1000000;
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/readconf.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,159 @@
+/*
+ * In this module we implement the reading of destination configuration
+ * for sip-manual-out.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+struct in_addr sip_bind_ip, sip_dest_ip;
+unsigned sip_bind_port, sip_dest_port = 5060;
+char sip_dest_domain[64];
+
+struct parse_state {
+	char *filename;
+	int lineno;
+	int set_mask;
+};
+
+static void
+handle_ip(st, kw, var, arg)
+	struct parse_state *st;
+	char *kw, *arg;
+	struct in_addr *var;
+{
+	var->s_addr = inet_addr(arg);
+	if (var->s_addr == INADDR_NONE) {
+		fprintf(stderr,
+			"%s line %d: invalid IP address argument \"%s\"\n",
+			st->filename, st->lineno, arg);
+		exit(1);
+	}
+}
+
+static void
+handle_num(st, kw, var, arg)
+	struct parse_state *st;
+	char *kw, *arg;
+	unsigned *var;
+{
+	char *endp;
+
+	*var = strtoul(arg, &endp, 10);
+	if (*endp) {
+		fprintf(stderr, "%s line %d: invalid numeric argument \"%s\"\n",
+			st->filename, st->lineno, arg);
+		exit(1);
+	}
+}
+
+static void
+handle_str(st, kw, var, arg)
+	struct parse_state *st;
+	char *kw, *arg, *var;
+{
+	strcpy(var, arg);
+}
+
+static void
+process_line(st, line)
+	struct parse_state *st;
+	char *line;
+{
+	char *cp, *np, *arg;
+	void (*handler)(), *var;
+	int set_id;
+
+	if (!index(line, '\n')) {
+		fprintf(stderr, "%s line %d: too long or missing newline\n",
+			st->filename, st->lineno);
+		exit(1);
+	}
+	for (cp = line; isspace(*cp); cp++)
+		;
+	if (*cp == '\0' || *cp == '#')
+		return;
+	for (np = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	if (!strcmp(np, "bind-ip")) {
+		handler = handle_ip;
+		var = &sip_bind_ip;
+		set_id = 1;
+	} else if (!strcmp(np, "bind-port")) {
+		handler = handle_num;
+		var = &sip_bind_port;
+		set_id = 2;
+	} else if (!strcmp(np, "dest-ip")) {
+		handler = handle_ip;
+		var = &sip_dest_ip;
+		set_id = 4;
+	} else if (!strcmp(np, "dest-port")) {
+		handler = handle_num;
+		var = &sip_dest_port;
+		set_id = 0;
+	} else if (!strcmp(np, "dest-domain")) {
+		handler = handle_str;
+		var = sip_dest_domain;
+		set_id = 8;
+	} else {
+		fprintf(stderr, "%s line %d: non-understood keyword \"%s\"\n",
+			st->filename, st->lineno, np);
+		exit(1);
+	}
+	if (st->set_mask & set_id) {
+		fprintf(stderr, "%s line %d: duplicate %s setting\n",
+			st->filename, st->lineno, np);
+		exit(1);
+	}
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#') {
+inv_syntax:	fprintf(stderr,
+			"%s line %d: %s setting requires one argument\n",
+			st->filename, st->lineno, np);
+		exit(1);
+	}
+	for (arg = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp != '\0' && *cp != '#')
+		goto inv_syntax;
+	handler(st, np, var, arg);
+	st->set_mask |= set_id;
+}
+
+read_config_file(filename)
+	char *filename;
+{
+	FILE *inf;
+	struct parse_state pst;
+	char linebuf[256];
+
+	inf = fopen(filename, "r");
+	if (!inf) {
+		perror(filename);
+		exit(1);
+	}
+	pst.set_mask = 0;
+	pst.filename = filename;
+	for (pst.lineno = 1; fgets(linebuf, sizeof linebuf, inf); pst.lineno++)
+		process_line(&pst, linebuf);
+	fclose(inf);
+	if (pst.set_mask != 15) {
+		fprintf(stderr, "error: %s did not set all required settings\n",
+			filename);
+		exit(1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/reinvite.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,70 @@
+/*
+ * Here we handle incoming INVITE requests in the UAS role: even though
+ * we are strictly outbound, BulkVS servers will send us periodic
+ * re-INVITEs as keepalives, and we have to play along.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "../libsip/parse.h"
+#include "../libsip/uas_basic.h"
+#include "../libsip/out_msg.h"
+
+extern char call_id[];
+
+static void
+invite_correct_call(req, ess, sin)
+	struct sip_pkt_rx *req;
+	struct uas_parse_hdrs *ess;
+	struct sockaddr_in *sin;
+{
+	struct sip_msg_out resp;
+	int rc;
+
+	printf("Received re-INVITE for our call, responding with 200\n");
+	start_response_out_msg(&resp, "200 OK");
+	rc = add_resp_basic_headers(&resp, ess, req->req_method);
+	if (rc < 0) {
+		fprintf(stderr, "sending 200 response: msg length exceeded\n");
+		return;
+	}
+	out_msg_finish(&resp);
+	sip_tx_packet(&resp, sin);
+}
+
+static void
+invite_unknown_call(req, ess, sin)
+	struct sip_pkt_rx *req;
+	struct uas_parse_hdrs *ess;
+	struct sockaddr_in *sin;
+{
+	struct sip_msg_out resp;
+	int rc;
+
+	printf("Received INVITE for unknown call, responding with 405\n");
+	start_response_out_msg(&resp, "405 This gateway is outbound only");
+	rc = add_resp_basic_headers(&resp, ess, req->req_method);
+	if (rc < 0) {
+		fprintf(stderr, "sending 405 response: msg length exceeded\n");
+		return;
+	}
+	out_msg_finish(&resp);
+	sip_tx_packet(&resp, sin);
+}
+
+void
+handle_invite_req(req, ess, sin)
+	struct sip_pkt_rx *req;
+	struct uas_parse_hdrs *ess;
+	struct sockaddr_in *sin;
+{
+	if (!strcmp(ess->call_id, call_id))
+		invite_correct_call(req, ess, sin);
+	else
+		invite_unknown_call(req, ess, sin);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/rtp_rx.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,270 @@
+/*
+ * In this module we implement our RTP handling: obtaining a PSTN-side
+ * RTP endpoint from themwi-rtp-mgr, then handling read select on RTP
+ * and RTCP UDP sockets.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "../include/tmgw_const.h"
+#include "../include/rtp_defs.h"
+#include "../librtpalloc/rtp_alloc_simple.h"
+
+struct sockaddr_in rtp_local_addr;
+int rtp_udp_fd, rtcp_udp_fd;
+
+static int rtp_start_flag, rtp_bad_flag, rtp_ssrc_chg_flag;
+static int rtp_seq_brk_flag, rtp_seq_zero_flag, rtp_seq_neg_flag;
+static int rtp_ts_brk_flag;
+static uint32_t rtp_ssrc;
+static uint32_t rtp_last_ts;
+static uint16_t rtp_last_seq;
+static int got_some_rtcp;
+
+static const uint8_t hdr_pattern[20] =	{0, 1, 0, 1, 0, 1, 1, 0, 1, 0,
+					 0, 1, 1, 0, 1, 0, 1, 0, 0, 1};
+
+static uint8_t is_hunt_buf[320];
+static int is_state;
+static unsigned is_offset, is_bit_count, is_hunt_fill;
+static uint32_t is_rx_word;
+
+static void
+reset_is_hunt()
+{
+	is_state = 0;
+	is_hunt_fill = 0;
+}
+
+static void
+is_rx_hunt(input_pos)
+	unsigned input_pos;
+{
+	unsigned offset, n;
+
+	for (offset = 0; offset < 16; offset++) {
+		for (n = 0; n < 20; n++)
+			if ((is_hunt_buf[offset + n*16] & 1) != hdr_pattern[n])
+				break;
+		if (n == 20)
+			break;
+	}
+	if (n != 20)
+		return;
+	printf("Found IS_Header, last bit offset %u\n",
+		input_pos * 16 + offset);
+	is_offset = offset;
+	is_state = 1;
+	is_bit_count = 0;
+	is_rx_word = 0;
+	is_hunt_fill = 0;
+}
+
+static void
+is_process_cmd()
+{
+	int cont;
+
+	printf("IS_Command: 0x%03X", is_rx_word);
+	switch (is_rx_word) {
+	case 0x05D:
+		printf(" (REQ)\n");
+		cont = 1;
+		break;
+	case 0x0BA:
+		printf(" (ACK)\n");
+		cont = 1;
+		break;
+	case 0x0E7:
+		printf(" (IPE)\n");
+		cont = 1;
+		break;
+	case 0x129:
+		printf(" (FILL)\n");
+		cont = 0;
+		break;
+	case 0x174:
+		printf(" (DUP)\n");
+		cont = 0;
+		break;
+	case 0x193:
+		printf(" (SYL)\n");
+		cont = 0;
+		break;
+	default:
+		printf(" (bad)\n");
+		cont = 0;
+	}
+	if (cont) {
+		is_state = 2;
+		is_bit_count = 0;
+		is_rx_word = 0;
+	} else
+		is_state = 0;
+}
+
+static void
+is_process_ext()
+{
+	printf("IS_Extension: 0x%05X", is_rx_word);
+	if (is_rx_word & 0x80200) {
+		printf(" (bad sync)\n");
+		is_state = 0;
+		return;
+	}
+	switch (is_rx_word & 3) {
+	case 0:
+		printf(" (final)\n");
+		is_state = 0;
+		return;
+	case 3:
+		printf(" (continue)\n");
+		is_state = 2;
+		is_bit_count = 0;
+		is_rx_word = 0;
+		return;
+	default:
+		printf(" (bad EX)\n");
+		is_state = 0;
+	}
+}
+
+static void
+is_rx_process(input, input_pos)
+	uint8_t *input;
+	unsigned input_pos;
+{
+	unsigned new_bit;
+
+	memmove(is_hunt_buf, is_hunt_buf + 16, 304);
+	memcpy(is_hunt_buf + 304, input, 16);
+	if (!is_state) {
+		if (is_hunt_fill < 20)
+			is_hunt_fill++;
+		if (is_hunt_fill == 20)
+			is_rx_hunt(input_pos);
+		return;
+	}
+	new_bit = input[is_offset] & 1;
+	is_rx_word <<= 1;
+	is_rx_word |= new_bit;
+	is_bit_count++;
+	if (is_state == 1 && is_bit_count == 10)
+		is_process_cmd();
+	else if (is_state == 2 && is_bit_count == 20)
+		is_process_ext();
+}
+
+void
+obtain_rtp_endp()
+{
+	int rc;
+	struct rtp_alloc_simple res;
+
+	rc = rtp_alloc_simple(TMGW_EP_TYPE_PSTN_ONLY, &res);
+	if (rc < 0)
+		exit(1);	/* error msg already printed */
+	bcopy(&res.pstn_addr, &rtp_local_addr, sizeof(struct sockaddr_in));
+	rtp_udp_fd = res.pstn_rtp_fd;
+	rtcp_udp_fd = res.pstn_rtcp_fd;
+	reset_is_hunt();
+}
+
+void
+rtp_rx_select()
+{
+	struct rtp_packet pkt;
+	struct sockaddr_in sin_from;
+	socklen_t addrlen;
+	int16_t seq_delta;
+	int32_t ts_delta;
+	int rc;
+	unsigned is_chunk;
+
+	addrlen = sizeof(struct sockaddr_in);
+	rc = recvfrom(rtp_udp_fd, &pkt, sizeof pkt, 0,
+			(struct sockaddr *) &sin_from, &addrlen);
+	if (rc < 0)
+		return;
+	if (rc != RTP_PACKET_SIZE_PSTN) {
+bad_rtp_pkt:	if (!rtp_bad_flag) {
+			printf("Got a bad RTP packet\n");
+			rtp_bad_flag = 1;
+		}
+		return;
+	}
+	if (pkt.v_p_x_cc != 0x80)
+		goto bad_rtp_pkt;
+	switch (pkt.m_pt & 0x7F) {
+	case PSTN_CODEC_PCMU:
+	case PSTN_CODEC_PCMA:
+		break;
+	default:
+		goto bad_rtp_pkt;
+	}
+	if (rtp_start_flag && pkt.ssrc != rtp_ssrc) {
+		if (!rtp_ssrc_chg_flag) {
+			printf("Rx RTP stream changed SSRC\n");
+			rtp_ssrc_chg_flag = 1;
+		}
+		reset_is_hunt();
+	} else if (rtp_start_flag) {
+		seq_delta = ntohs(pkt.seq) - rtp_last_seq;
+		ts_delta = ntohl(pkt.tstamp) - rtp_last_ts;
+		if (seq_delta == 0) {
+			if (!rtp_seq_zero_flag) {
+				printf("Rx RTP seq zero increment\n");
+				rtp_seq_zero_flag = 1;
+			}
+			return;
+		}
+		if (seq_delta < 0) {
+			if (!rtp_seq_neg_flag) {
+				printf("Rx RTP seq negative increment\n");
+				rtp_seq_neg_flag = 1;
+			}
+			return;
+		}
+		if (seq_delta != 1) {
+			if (!rtp_seq_brk_flag) {
+				printf("Rx RTP stream seq break\n");
+				rtp_seq_brk_flag = 1;
+			}
+			reset_is_hunt();
+		} else if (ts_delta != 160) {
+			if (!rtp_ts_brk_flag) {
+				printf("Rx RTP stream tstamp break\n");
+				rtp_ts_brk_flag = 1;
+			}
+			reset_is_hunt();
+		}
+	}
+	rtp_ssrc = pkt.ssrc;
+	rtp_last_ts = ntohl(pkt.tstamp);
+	rtp_last_seq = ntohs(pkt.seq);
+	if (!rtp_start_flag) {
+		printf("Rx RTP stream begins with seq=%u ts=%u\n",
+			rtp_last_seq, rtp_last_ts);
+		rtp_start_flag = 1;
+	}
+	for (is_chunk = 0; is_chunk < 10; is_chunk++)
+		is_rx_process(pkt.payload + is_chunk * 16, is_chunk);
+}
+
+void
+rtcp_rx_select()
+{
+	u_char buf[512];
+
+	recv(rtcp_udp_fd, buf, sizeof buf, 0);
+	if (!got_some_rtcp) {
+		printf("Got some RTCP\n");
+		got_some_rtcp = 1;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/rtp_tx.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,132 @@
+/*
+ * In this module we implement outgoing RTP stream generation.
+ */
+
+#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 <unistd.h>
+#include "../include/tmgw_const.h"
+#include "../include/rtp_defs.h"
+#include "../libutil/osmo_bits.h"
+
+extern struct sockaddr_in rtp_local_addr, rtp_remote_addr;
+extern int rtp_udp_fd, rtcp_udp_fd, pcma_selected;
+extern struct timeval cur_event_time;
+
+static uint32_t rtp_ssrc;
+static uint32_t rtp_out_ts;
+static uint16_t rtp_out_seq;
+
+static uint8_t pcm_fill_octet;
+
+static uint16_t tfo_fill_buf[9], tfo_req_buf[7];
+static uint16_t *is_out_ptr;
+static unsigned is_out_count;
+static int tfo_stop_req;
+
+void
+assign_rtpout_ssrc()
+{
+	rtp_ssrc = cur_event_time.tv_sec ^ cur_event_time.tv_usec ^ getpid();
+}
+
+void
+init_pcm_fill_octet()
+{
+	if (pcma_selected)
+		pcm_fill_octet = 0xD5;
+	else
+		pcm_fill_octet = 0xFF;
+}
+
+void
+prepare_tfo_fill()
+{
+	tfo_fill_buf[0] = 0x15A;
+	tfo_fill_buf[1] = 0x1A9;
+	tfo_fill_buf[2] = 0x129;
+	tfo_fill_buf[3] = 0x15A;
+	tfo_fill_buf[4] = 0x1A9;
+	tfo_fill_buf[5] = 0x129;
+	tfo_fill_buf[6] = 0x15A;
+	tfo_fill_buf[7] = 0x1A9;
+	tfo_fill_buf[8] = 0x129;
+}
+
+static void
+insert_is_msg(payload, word)
+	uint8_t *payload;
+	uint16_t word;
+{
+	ubit_t is_bits[10];
+	uint8_t *bytep;
+	unsigned n;
+
+	uint16_to_bits(word, is_bits, 10);
+	for (n = 0; n < 10; n++) {
+		bytep = payload + n * 16;
+		*bytep &= 0xFE;
+		*bytep |= is_bits[n];
+	}
+}
+
+void
+generate_rtp_packet()
+{
+	struct rtp_packet pkt;
+	socklen_t addrlen;
+
+	pkt.v_p_x_cc = 0x80;
+	pkt.m_pt = pcma_selected ? PSTN_CODEC_PCMA : PSTN_CODEC_PCMU;
+	pkt.seq = htons(rtp_out_seq++);
+	pkt.tstamp = htonl(rtp_out_ts);
+	rtp_out_ts += 160;
+	pkt.ssrc = rtp_ssrc;
+	memset(pkt.payload, pcm_fill_octet, RTP_MAX_PAYLOAD);
+	if (is_out_count) {
+		insert_is_msg(pkt.payload, *is_out_ptr++);
+		is_out_count--;
+		if (!is_out_count && !tfo_stop_req) {
+			is_out_ptr = tfo_req_buf;
+			is_out_count = 7;
+		}
+	}
+	addrlen = sizeof(struct sockaddr_in);
+	sendto(rtp_udp_fd, &pkt, RTP_PACKET_SIZE_PSTN, 0,
+		(struct sockaddr *) &rtp_remote_addr, addrlen);
+}
+
+void
+set_pcm_fill_octet(oct)
+	unsigned oct;
+{
+	pcm_fill_octet = oct;
+}
+
+void
+send_tfo_req(sig, codec)
+	unsigned sig, codec;
+{
+	tfo_req_buf[0] = 0x15A;
+	tfo_req_buf[1] = 0x1A9;
+	tfo_req_buf[2] = 0x05D;
+	tfo_req_buf[3] = 0x14E;
+	tfo_req_buf[4] = 0x14B;
+	encode_tfo_ext_words(sig, codec, 0, tfo_req_buf + 5);
+	is_out_ptr = tfo_fill_buf;
+	is_out_count = 9;
+	tfo_stop_req = 0;
+}
+
+void
+stop_tfo_out()
+{
+	tfo_stop_req = 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/sdp_in.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,86 @@
+/*
+ * In this module we handle SDP responses to our INVITE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "../libsip/parse.h"
+#include "../libsip/sdp.h"
+
+extern char *get_single_header();
+extern char *extract_to_tag();
+
+struct sockaddr_in rtp_remote_addr;
+int got_sdp_answer, pcma_selected, rtp_out_enable;
+
+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;
+}
+
+void
+extract_resp_sdp(msg)
+	struct sip_pkt_rx *msg;
+{
+	struct sdp_parse sdp_parse;
+	int rc;
+
+	if (!check_sdp_present(msg))
+		return;
+	rc = parse_incoming_sdp(msg->msg_body, msg->msg_body_len, &sdp_parse);
+	if (rc < 0) {
+		printf("SDP parse error: %d\n", rc);
+		return;
+	}
+	switch (sdp_parse.codec_mask) {
+	case SDP_CODEC_MASK_PCMU:
+	case SDP_CODEC_MASK_BOTH:
+		pcma_selected = 0;
+		break;
+	case SDP_CODEC_MASK_PCMA:
+	case SDP_CODEC_MASK_BOTH | SDP_CODEC_MASK_PCMA_PREF:
+		pcma_selected = 1;
+		break;
+	default:
+		printf("SDP error: no supported codec\n");
+		return;
+	}
+	printf("SDP response: IP %s port %u codec %s\n",
+		inet_ntoa(sdp_parse.ip_addr), sdp_parse.audio_port,
+		pcma_selected ? "PCMA" : "PCMU");
+	rtp_remote_addr.sin_family = AF_INET;
+	rtp_remote_addr.sin_addr = sdp_parse.ip_addr;
+	rtp_remote_addr.sin_port = htons(sdp_parse.audio_port);
+	got_sdp_answer = 1;
+}
+
+void
+invite_200_rtpout()
+{
+	if (!got_sdp_answer) {
+		printf("INVITE response has no SDP!\n");
+		return;
+	}
+	rtp_out_enable = 1;
+	assign_rtpout_ssrc();
+	init_pcm_fill_octet();
+	prepare_tfo_fill();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/sip_log.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,68 @@
+/*
+ * In this module we implement debug logging of SIP Rx & Tx messages.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+extern struct timeval cur_event_time;
+
+static FILE *logfile;
+
+open_sip_log_file(filename)
+	char *filename;
+{
+	logfile = fopen(filename, "a");
+	if (!logfile) {
+		perror(filename);
+		return(-1);
+	}
+	return(0);
+}
+
+static void
+log_common(msg, msglen, sin, dir)
+	char *msg, *dir;
+	unsigned msglen;
+	struct sockaddr_in *sin;
+{
+	unsigned sec, ms;
+
+	sec = cur_event_time.tv_sec % 86400;
+	ms = cur_event_time.tv_usec / 1000;
+	fprintf(logfile, "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, logfile);
+	putc('\n', logfile);
+	fflush(logfile);
+}
+
+void
+log_sip_msg_rx(msg, msglen, sin)
+	char *msg;
+	unsigned msglen;
+	struct sockaddr_in *sin;
+{
+	if (!logfile)
+		return;
+	log_common(msg, msglen, sin, "from");
+}
+
+void
+log_sip_msg_tx(msg, msglen, sin)
+	char *msg;
+	unsigned msglen;
+	struct sockaddr_in *sin;
+{
+	if (!logfile)
+		return;
+	log_common(msg, msglen, sin, "to");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/sip_udp.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,83 @@
+/*
+ * In this module we implement our UDP socket for SIP,
+ * and the associated lowest-level protocol handling.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include "../libsip/parse.h"
+#include "../libsip/out_msg.h"
+
+extern struct in_addr sip_bind_ip;
+extern unsigned sip_bind_port;
+
+int sip_socket;
+
+open_sip_udp_socket()
+{
+	struct sockaddr_in sin;
+	int rc;
+
+	sip_socket = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sip_socket < 0) {
+		perror("socket(AF_INET, SOCK_DGRAM, 0)");
+		exit(1);
+	}
+	sin.sin_family = AF_INET;
+	sin.sin_addr = sip_bind_ip;
+	sin.sin_port = htons(sip_bind_port);
+	rc = bind(sip_socket, (struct sockaddr *) &sin, sizeof sin);
+	if (rc < 0) {
+		perror("bind of SIP UDP socket");
+		exit(1);
+	}
+	return(0);
+}
+
+void
+sip_socket_select()
+{
+	struct sip_pkt_rx pkt;
+	struct sockaddr_in sin;
+	socklen_t addrlen;
+	int rc;
+
+	addrlen = sizeof sin;
+	rc = recvfrom(sip_socket, pkt.pkt_buffer, MAX_SIP_RX_PACKET, 0,
+			(struct sockaddr *) &sin, &addrlen);
+	if (rc <= 0) {
+		perror("recvfrom");
+		return;
+	}
+	pkt.pkt_length = rc;
+	log_sip_msg_rx(pkt.pkt_buffer, pkt.pkt_length, &sin);
+	rc = parse_incoming_sip_msg(&pkt);
+	if (rc < 0) {
+		printf("Incoming SIP UDP message parse error %d\n", rc);
+		return;
+	}
+	/* dispatch good-so-far SIP message */
+	if (pkt.parse_msgtype == SIP_MSG_TYPE_REQ)
+		process_sip_request(&pkt, &sin);
+	else if (pkt.parse_msgtype == SIP_MSG_TYPE_RESP)
+		process_sip_response(&pkt, &sin);
+}
+
+void
+sip_tx_packet(msg, sin)
+	struct sip_msg_out *msg;
+	struct sockaddr_in *sin;
+{
+	socklen_t addrlen;
+
+	addrlen = sizeof(struct sockaddr_in);
+	sendto(sip_socket, msg->buf, msg->msg_len, 0, (struct sockaddr *) sin,
+		addrlen);
+	log_sip_msg_tx(msg->buf, msg->msg_len, sin);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/uac.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,142 @@
+/*
+ * Here we implement processing of SIP responses to the requests we sent out.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "../libsip/parse.h"
+#include "../libsip/resp_ident.h"
+#include "../libsip/out_msg.h"
+
+#define	MAX_TO_TAG	63
+
+extern char *get_single_header();
+extern char *extract_to_tag();
+
+extern struct in_addr sip_bind_ip;
+extern unsigned sip_bind_port;
+extern char call_id[], from_uri[], to_uri[];
+extern unsigned max_forwards;
+
+char to_tag[MAX_TO_TAG+1];
+
+add_req_boilerplate(msg, cseq, add_to_tag)
+	struct sip_msg_out *msg;
+	char *cseq;
+{
+	char strbuf[256];
+	int rc;
+
+	sprintf(strbuf, "SIP/2.0/UDP %s:%u",
+		inet_ntoa(sip_bind_ip), sip_bind_port);
+	rc = out_msg_add_header(msg, "Via", strbuf);
+	if (rc < 0)
+		return rc;
+	rc = out_msg_add_header(msg, "From", from_uri);
+	if (rc < 0)
+		return rc;
+	if (add_to_tag && to_tag[0]) {
+		sprintf(strbuf, "<%s>;tag=%s", to_uri, to_tag);
+		rc = out_msg_add_header(msg, "To", strbuf);
+	} else
+		rc = out_msg_add_header(msg, "To", to_uri);
+	if (rc < 0)
+		return rc;
+	rc = out_msg_add_header(msg, "Call-ID", call_id);
+	if (rc < 0)
+		return rc;
+	rc = out_msg_add_header(msg, "CSeq", cseq);
+	if (rc < 0)
+		return rc;
+	sprintf(strbuf, "%u", max_forwards);
+	return out_msg_add_header(msg, "Max-Forwards", strbuf);
+}
+
+add_contact_header(msg)
+	struct sip_msg_out *msg;
+{
+	char strbuf[80];
+
+	sprintf(strbuf, "<sip:%s:%u;transport=udp>",
+		inet_ntoa(sip_bind_ip), sip_bind_port);
+	return out_msg_add_header(msg, "Contact", strbuf);
+}
+
+static void
+send_ack(sin)
+	struct sockaddr_in *sin;
+{
+	struct sip_msg_out msg;
+	int rc;
+
+	rc = start_request_out_msg(&msg, "ACK", to_uri);
+	if (rc < 0) {
+msg_size_err:	fprintf(stderr, "composing ACK message: size error\n");
+		return;
+	}
+	rc = add_req_boilerplate(&msg, "1 ACK", 1);
+	if (rc < 0)
+		goto msg_size_err;
+	out_msg_finish(&msg);
+	sip_tx_packet(&msg, sin);
+}
+
+static void
+handle_invite_response(msg, sin)
+	struct sip_pkt_rx *msg;
+	struct sockaddr_in *sin;
+{
+	char *tag;
+
+	printf("Response to INVITE: %s\n", msg->status_str);
+	tag = extract_to_tag(msg, to_uri);
+	if (tag) {
+		printf("To tag: %s\n", tag);
+		if (strlen(tag) <= MAX_TO_TAG)
+			strcpy(to_tag, tag);
+		else
+			printf("To tag exceeds length limit!\n");
+	}
+	extract_resp_sdp(msg);
+	if (msg->status_code >= 200) {
+		printf("Sending ACK\n");
+		send_ack(sin);
+		if (msg->status_code <= 299)
+			invite_200_rtpout();
+	}
+}
+
+void
+process_sip_response(msg, sin)
+	struct sip_pkt_rx *msg;
+	struct sockaddr_in *sin;
+{
+	struct sip_resp_ident rid;
+	int rc;
+
+	rc = sip_resp_extract_ident(msg, &rid);
+	if (rc < 0) {
+		printf("SIP %03u response: bad or missing %s header\n",
+			msg->status_code, rid.error_field);
+		return;
+	}
+	if (strcmp(rid.call_id, call_id)) {
+		printf("Got SIP response with wrong Call-ID\n");
+		return;
+	}
+	if (rid.cseq_num == 1 && !strcmp(rid.cseq_method, "INVITE"))
+		handle_invite_response(msg, sin);
+	else if (rid.cseq_num == 1 && !strcmp(rid.cseq_method, "CANCEL"))
+		printf("Response to CANCEL: %s\n", msg->status_str);
+	else if (rid.cseq_num == 2 && !strcmp(rid.cseq_method, "BYE"))
+		printf("Response to BYE: %s\n", msg->status_str);
+	else
+		printf("Got SIP resp for our Call-ID with unknown CSeq %u %s\n",
+			rid.cseq_num, rid.cseq_method);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/uas.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,63 @@
+/*
+ * UAS for sip-manual-out.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "../libsip/parse.h"
+#include "../libsip/uas_basic.h"
+#include "../libsip/out_msg.h"
+
+static void
+unsupported_method(req, ess, sin)
+	struct sip_pkt_rx *req;
+	struct uas_parse_hdrs *ess;
+	struct sockaddr_in *sin;
+{
+	struct sip_msg_out resp;
+	int rc;
+
+	printf("SIP %.16s request: unsupported method\n", req->req_method);
+	start_response_out_msg(&resp, "501 Method not supported");
+	rc = add_resp_basic_headers(&resp, ess, req->req_method);
+	if (rc < 0) {
+too_long:	fprintf(stderr,
+			"sending 501 error: response length exceeded\n");
+		return;
+	}
+	rc = out_msg_add_header(&resp, "Allow", "INVITE,ACK,BYE");
+	if (rc < 0)
+		goto too_long;
+	out_msg_finish(&resp);
+	sip_tx_packet(&resp, sin);
+}
+
+void
+process_sip_request(msg, sin)
+	struct sip_pkt_rx *msg;
+	struct sockaddr_in *sin;
+{
+	struct uas_parse_hdrs ess;
+	int rc;
+
+	rc = uas_get_basic_headers(msg, &ess);
+	if (rc < 0) {
+		printf("SIP %.16s request: bad or missing %s header\n",
+			msg->req_method, ess.error_field);
+		return;
+	}
+	/* dispatch by method */
+	if (!strcmp(msg->req_method, "INVITE"))
+		handle_invite_req(msg, &ess, sin);
+	else if (!strcmp(msg->req_method, "ACK"))
+		printf("Received ACK request, swallowing it\n");
+	else if (!strcmp(msg->req_method, "BYE"))
+		handle_bye_req(msg, &ess, sin);
+	else
+		unsupported_method(msg, &ess, sin);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-voice/user_cmd.c	Sun Mar 03 23:20:19 2024 -0800
@@ -0,0 +1,102 @@
+/*
+ * In this module we implement stdin command handling, supporting
+ * user-initiated CANCEL and BYE as well as TFO testing commands.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+static void
+pcm_fill_cmd(arg)
+	char *arg;
+{
+	char *cp;
+	unsigned octet;
+
+	for (cp = arg; isspace(*cp); cp++)
+		;
+	if (!isxdigit(*cp)) {
+inv_syntax:	fprintf(stderr, "error: pcm-fill command invalid syntax\n");
+		return;
+	}
+	octet = strtoul(cp, &cp, 16);
+	if (*cp)
+		goto inv_syntax;
+	if (octet > 0xFF) {
+		fprintf(stderr,
+			"error: pcm-fill octet argument out of range\n");
+		return;
+	}
+	set_pcm_fill_octet(octet);
+}
+
+static void
+tfo_req_cmd(args)
+	char *args;
+{
+	char *cp;
+	unsigned sig, codec;
+
+	for (cp = args; isspace(*cp); cp++)
+		;
+	if (!isdigit(*cp)) {
+inv_syntax:	fprintf(stderr, "error: tfo-req command invalid syntax\n");
+		return;
+	}
+	sig = strtoul(cp, &cp, 0);
+	if (!isspace(*cp))
+		goto inv_syntax;
+	if (sig > 0xFF) {
+		fprintf(stderr, "error: Sig argument out of range\n");
+		return;
+	}
+	while (isspace(*cp))
+		cp++;
+	if (!isdigit(*cp))
+		goto inv_syntax;
+	codec = strtoul(cp, &cp, 0);
+	if (*cp && !isspace(*cp))
+		goto inv_syntax;
+	if (codec > 14) {
+		fprintf(stderr, "error: Codec_Type argument out of range\n");
+		return;
+	}
+	while (isspace(*cp))
+		cp++;
+	if (*cp)
+		goto inv_syntax;
+	send_tfo_req(sig, codec);
+}
+
+void
+select_stdin()
+{
+	char buf[256], *cp;
+
+	fgets(buf, sizeof buf, stdin);
+	cp = index(buf, '\n');
+	if (cp) {
+		while (cp > buf && isspace(cp[-1]))
+			cp--;
+		*cp = '\0';
+	}
+	for (cp = buf; isspace(*cp); cp++)
+		;
+	if (!*cp)
+		return;
+	if (!strcmp(cp, "b") || !strcasecmp(cp, "bye"))
+		send_bye_req();
+	else if (!strcmp(cp, "c") || !strcasecmp(cp, "cancel"))
+		send_cancel_req();
+	else if (!strncmp(cp, "pcm-fill", 8) && isspace(cp[8]))
+		pcm_fill_cmd(cp + 9);
+	else if (!strncmp(cp, "tfo-req", 7) && isspace(cp[7]))
+		tfo_req_cmd(cp + 8);
+	else if (!strcmp(cp, "tfo-stop"))
+		stop_tfo_out();
+	else
+		fprintf(stderr, "error: non-understood stdin command\n");
+}