changeset 71:d74b545a3c2a

sip-manual-out: new test program
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 20 Sep 2022 10:14:18 -0800
parents 47976db01894
children 9ca6f0708237
files .hgignore Makefile sip-manual-out/Makefile sip-manual-out/dummy_rtp.c sip-manual-out/main.c sip-manual-out/readconf.c sip-manual-out/sip_log.c sip-manual-out/sip_udp.c sip-manual-out/uac.c sip-manual-out/uas.c
diffstat 10 files changed, 740 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Mon Sep 19 21:30:33 2022 -0800
+++ b/.hgignore	Tue Sep 20 10:14:18 2022 -0800
@@ -10,6 +10,8 @@
 
 ^sip-in/themwi-sip-in$
 
+^sip-manual-out/sip-manual-out$
+
 ^utils/sip-out-test$
 ^utils/sip-rx-test$
 ^utils/sip-udp-dump$
--- a/Makefile	Mon Sep 19 21:30:33 2022 -0800
+++ b/Makefile	Tue Sep 20 10:14:18 2022 -0800
@@ -1,7 +1,7 @@
 CC=	gcc
 CFLAGS=	-O2
 
-PROGDIR=mgw mncc mtctest sip-in utils
+PROGDIR=mgw mncc mtctest sip-in sip-manual-out utils
 LIBDIR=	libnumdb libsip libutil
 SUBDIR=	${PROGDIR} ${LIBDIR}
 
@@ -11,6 +11,7 @@
 mncc:		libnumdb libutil
 mtctest:	libnumdb libutil
 sip-in:		libnumdb libsip libutil
+sip-manual-out:	libsip libutil
 utils:		libnumdb libsip libutil
 
 ${SUBDIR}: FRC
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-manual-out/Makefile	Tue Sep 20 10:14:18 2022 -0800
@@ -0,0 +1,17 @@
+CC=	gcc
+CFLAGS=	-O2
+PROG=	sip-manual-out
+OBJS=	dummy_rtp.o main.o readconf.o sip_log.o sip_udp.o uac.o uas.o
+LIBS=	../libsip/libsip.a ../libutil/libutil.a
+INSTBIN=/usr/local/bin
+
+all:	${PROG}
+
+${PROG}: ${OBJS} ${LIBS}
+	${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS}
+
+install:
+	install -c -o bin -g bin -m 755 ${PROG} ${INSTBIN}
+
+clean:
+	rm -f *.o ${PROG} errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-manual-out/dummy_rtp.c	Tue Sep 20 10:14:18 2022 -0800
@@ -0,0 +1,66 @@
+/*
+ * In this module we implement the code that connects to themwi-mgw
+ * and obtains a dummy PSTN-side RTP endpoint.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "../include/tmgw_ctrl.h"
+#include "../include/tmgw_const.h"
+
+struct sockaddr_in dummy_rtp_endp;
+
+static char tmgw_socket_pathname[] = "/var/gsm/tmgw_socket";
+
+obtain_dummy_rtp()
+{
+	struct sockaddr_un sa;
+	unsigned sa_len;
+	int fd, rc;
+	struct tmgw_ctrl_req req;
+	struct tmgw_ctrl_resp resp;
+
+	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (fd < 0) {
+		perror("socket(AF_UNIX, SOCK_SEQPACKET, 0)");
+		exit(1);
+	}
+	fill_sockaddr_un(tmgw_socket_pathname, &sa, &sa_len);
+	rc = connect(fd, (struct sockaddr *) &sa, sa_len);
+	if (rc < 0) {
+		perror(tmgw_socket_pathname);
+		exit(1);
+	}
+	bzero(&req, sizeof req);
+	req.opcode = TMGW_CTRL_OP_CRCX;
+	req.ep_id = TMGW_EP_TYPE_DUMMY_PSTN;
+	rc = send(fd, &req, sizeof req, 0);
+	if (rc < 0) {
+		perror("send to TMGW socket");
+		exit(1);
+	}
+	rc = recv(fd, &resp, sizeof resp, 0);
+	if (rc < 0) {
+		perror("recv from TMGW socket");
+		exit(1);
+	}
+	if (rc != sizeof resp) {
+		fprintf(stderr,
+	"error: response packet from TMGW has wrong length (%d bytes)\n",
+			rc);
+		exit(1);
+	}
+	if (resp.res != TMGW_RESP_OK) {
+		fprintf(stderr, "TMGW CRCX returned error %u\n", resp.res);
+		exit(1);
+	}
+	bcopy(&resp.pstn_addr, &dummy_rtp_endp, sizeof(struct sockaddr_in));
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-manual-out/main.c	Tue Sep 20 10:14:18 2022 -0800
@@ -0,0 +1,106 @@
+/*
+ * 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 dummy_rtp_endp;
+
+struct sockaddr_in sip_dest_sin;
+char from_uri[128], to_uri[128], call_id[128];
+struct timeval cur_event_time;
+
+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");
+	if (rc < 0)
+		goto msg_size_err;
+	rc = add_contact_header(&msg);
+	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 = dummy_rtp_endp.sin_addr;
+	sdp.conn_port = ntohs(dummy_rtp_endp.sin_port);
+	sdp.codec_mask = SDP_CODEC_MASK_BOTH;
+	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);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	fd_set fds;
+	int rc;
+
+	if (argc < 4 || argc > 5) {
+		fprintf(stderr,
+			"usage: %s dest-conf from-num to-num [logfile]\n",
+			argv[0]);
+		exit(1);
+	}
+	read_config_file(argv[1]);
+	open_sip_udp_socket();
+	obtain_dummy_rtp();
+	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", argv[2], inet_ntoa(sip_bind_ip));
+	sprintf(to_uri, "sip:%s@%s", argv[3], sip_dest_domain);
+	if (argv[4]) {
+		rc = open_sip_log_file(argv[4]);
+		if (rc < 0)
+			exit(1);	/* error msg already printed */
+	}
+	gettimeofday(&cur_event_time, 0);
+	sprintf(call_id, "%08u_%u@%s",
+		(unsigned)(cur_event_time.tv_sec % 100000000),
+		ntohs(dummy_rtp_endp.sin_port), inet_ntoa(sip_bind_ip));
+	send_invite_req();
+	/* main select loop */
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(sip_socket, &fds);
+		rc = select(sip_socket+1, &fds, 0, 0, 0);
+		if (rc < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			exit(1);
+		}
+		gettimeofday(&cur_event_time, 0);
+		if (FD_ISSET(sip_socket, &fds))
+			sip_socket_select();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-manual-out/readconf.c	Tue Sep 20 10:14:18 2022 -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/sip-manual-out/sip_log.c	Tue Sep 20 10:14:18 2022 -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/sip-manual-out/sip_udp.c	Tue Sep 20 10:14:18 2022 -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/sip-manual-out/uac.c	Tue Sep 20 10:14:18 2022 -0800
@@ -0,0 +1,124 @@
+/*
+ * 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/out_msg.h"
+
+#define	MAX_TO_TAG	63
+
+extern char *get_single_header();
+
+extern struct in_addr sip_bind_ip;
+extern unsigned sip_bind_port;
+extern char call_id[], from_uri[], to_uri[];
+
+char to_tag[MAX_TO_TAG+1];
+
+add_req_boilerplate(msg, cseq)
+	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 (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;
+	return out_msg_add_header(msg, "Max-Forwards", "70");
+}
+
+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");
+	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;
+{
+	printf("Response to INVITE: %s\n", msg->status_str);
+	if (msg->status_code >= 200) {
+		printf("Sending ACK\n");
+		send_ack(sin);
+	}
+}
+
+void
+process_sip_response(msg, sin)
+	struct sip_pkt_rx *msg;
+	struct sockaddr_in *sin;
+{
+	char *call_id_hdr, *cseq_hdr;
+
+	call_id_hdr = get_single_header(msg, "Call-ID", "i", (int *) 0);
+	if (!call_id_hdr) {
+		printf("Got SIP response w/o Call-ID header\n");
+		return;
+	}
+	if (strcmp(call_id_hdr, call_id)) {
+		printf("Got SIP response with wrong Call-ID\n");
+		return;
+	}
+	cseq_hdr = get_single_header(msg, "CSeq", (char *) 0, (int *) 0);
+	if (!cseq_hdr) {
+		printf("Got SIP response w/o CSeq header\n");
+		return;
+	}
+	if (!strcmp(cseq_hdr, "1 INVITE"))
+		handle_invite_response(msg, sin);
+	else
+		printf("Got SIP resp for our Call-ID with unknown CSeq %s\n",
+			cseq_hdr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-manual-out/uas.c	Tue Sep 20 10:14:18 2022 -0800
@@ -0,0 +1,113 @@
+/*
+ * 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"
+
+extern char call_id[];
+
+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");
+	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);
+}
+
+static void
+handle_bye(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);
+}
+
+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", "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, "BYE"))
+		handle_bye(msg, &ess, sin);
+	else
+		unsupported_method(msg, &ess, sin);
+}