changeset 32:b3f74df7b808

beginning of themwi-mgw
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 09 Jul 2022 22:51:44 -0800
parents 08d7794cdd0a
children 2b072ad13de6
files .hgignore mgw/Makefile mgw/crcx.c mgw/ctrl_prot.c mgw/ctrl_sock.c mgw/dlcx.c mgw/main.c mgw/mdcx.c mgw/readconf.c mgw/select.h mgw/struct.h mgw/udpsink.c
diffstat 12 files changed, 800 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Wed Jul 06 22:38:44 2022 -0800
+++ b/.hgignore	Sat Jul 09 22:51:44 2022 -0800
@@ -2,6 +2,8 @@
 
 \.[oa]$
 
+^mgw/themwi-mgw$
+
 ^mncc/themwi-mncc$
 
 ^mtctest/themwi-test-mtc$
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/Makefile	Sat Jul 09 22:51:44 2022 -0800
@@ -0,0 +1,17 @@
+CC=	gcc
+CFLAGS=	-O2
+PROG=	themwi-mgw
+OBJS=	crcx.o ctrl_prot.o ctrl_sock.o dlcx.o main.o mdcx.o readconf.o udpsink.o
+LIBS=	../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/mgw/crcx.c	Sat Jul 09 22:51:44 2022 -0800
@@ -0,0 +1,164 @@
+/*
+ * In this module we implement our CRCX operation.
+ */
+
+#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 <syslog.h>
+#include <unistd.h>
+#include "../include/tmgw_ctrl.h"
+#include "../include/tmgw_const.h"
+#include "struct.h"
+#include "select.h"
+
+extern struct endpoint *find_ep_by_id();
+extern void udp_sink_rcvr();
+
+extern struct bind_range_cfg bind_range_gsm, bind_range_pstn;
+
+static unsigned
+get_new_ep_id(conn)
+	struct ctrl_conn *conn;
+{
+	unsigned id;
+
+	for (;;) {
+		id = conn->next_ep_id++;
+		if (!find_ep_by_id(conn, id))
+			return id;
+	}
+}
+
+static int
+get_local_port_pair(ep, roe, brc)
+	struct endpoint *ep;
+	struct rtp_one_end *roe;
+	struct bind_range_cfg *brc;
+{
+	struct sockaddr_in sin;
+	unsigned tries, rtp_port;
+	int rc;
+
+	sin.sin_family = AF_INET;
+	sin.sin_addr = brc->bind_ip;
+	for (tries = brc->port_tries; tries; tries--) {
+		rtp_port = brc->port_next;
+		brc->port_next += 2;
+		if (brc->port_next >= brc->port_range_end)
+			brc->port_next = brc->port_range_start;
+		sin.sin_port = htons(rtp_port);
+		roe->rtp_fd = socket(AF_INET, SOCK_DGRAM, 0);
+		if (roe->rtp_fd < 0) {
+			syslog(LOG_CRIT, "socket(AF_INET, SOCK_DGRAM, 0): %m");
+			return(-1);
+		}
+		rc = bind(roe->rtp_fd, (struct sockaddr *) &sin, sizeof sin);
+		if (rc < 0) {
+			close(roe->rtp_fd);
+			continue;
+		}
+		bcopy(&sin, &roe->bound_addr, sizeof(struct sockaddr_in));
+		sin.sin_port = htons(rtp_port+1);
+		roe->rtcp_fd = socket(AF_INET, SOCK_DGRAM, 0);
+		if (roe->rtcp_fd < 0) {
+			syslog(LOG_CRIT, "socket(AF_INET, SOCK_DGRAM, 0): %m");
+			close(roe->rtp_fd);
+			return(-1);
+		}
+		rc = bind(roe->rtcp_fd, (struct sockaddr *) &sin, sizeof sin);
+		if (rc < 0) {
+			close(roe->rtp_fd);
+			close(roe->rtcp_fd);
+			continue;
+		}
+		/* all good - make the file descriptors live for select */
+		update_max_fd(roe->rtp_fd);
+		FD_SET(roe->rtp_fd, &select_for_read);
+		select_handlers[roe->rtp_fd] = udp_sink_rcvr;
+		select_data[roe->rtp_fd] = (void *) ep;
+		update_max_fd(roe->rtcp_fd);
+		FD_SET(roe->rtcp_fd, &select_for_read);
+		select_handlers[roe->rtcp_fd] = udp_sink_rcvr;
+		select_data[roe->rtcp_fd] = (void *) ep;
+		return(0);
+	}
+	/* couldn't find a free port pair */
+	return(-1);
+}
+
+void
+process_crcx(conn, req, resp)
+	struct ctrl_conn *conn;
+	struct tmgw_ctrl_req *req;
+	struct tmgw_ctrl_resp *resp;
+{
+	struct endpoint *ep;
+	int rc;
+
+	/* ep_id in request encodes ep_type */
+	switch (req->ep_id) {
+	case TMGW_EP_TYPE_DUMMY_GSM:
+	case TMGW_EP_TYPE_DUMMY_PSTN:
+	case TMGW_EP_TYPE_GATEWAY:
+		break;
+	default:
+		resp->res = TMGW_RESP_ERR_PROT;
+		return;
+	}
+	ep = malloc(sizeof(struct endpoint));
+	if (!ep) {
+		syslog(LOG_CRIT, "malloc for endpoint: %m");
+		resp->res = TMGW_RESP_ERR_RSRC;
+		return;
+	}
+	bzero(ep, sizeof(struct endpoint));
+	ep->ep_type = req->ep_id;
+	ep->ep_id = get_new_ep_id(conn);
+	if (ep->ep_type & TMGW_EP_HAS_GSM_SOCK) {
+		rc = get_local_port_pair(ep, &ep->rtp_gsm, &bind_range_gsm);
+		if (rc < 0) {
+			syslog(LOG_ERR,
+				"unable to get local port pair on GSM side");
+			free(ep);
+			resp->res = TMGW_RESP_ERR_RSRC;
+			return;
+		}
+	}
+	if (ep->ep_type & TMGW_EP_HAS_PSTN_SOCK) {
+		rc = get_local_port_pair(ep, &ep->rtp_pstn, &bind_range_pstn);
+		if (rc < 0) {
+			syslog(LOG_ERR,
+				"unable to get local port pair on PSTN side");
+			if (ep->ep_type & TMGW_EP_HAS_GSM_SOCK)
+				free_rtp_end(&ep->rtp_gsm);
+			free(ep);
+			resp->res = TMGW_RESP_ERR_RSRC;
+			return;
+		}
+	}
+	rc = mdcx_operation(ep, req, resp);
+	if (rc < 0) {
+		if (ep->ep_type & TMGW_EP_HAS_GSM_SOCK)
+			free_rtp_end(&ep->rtp_gsm);
+		if (ep->ep_type & TMGW_EP_HAS_PSTN_SOCK)
+			free_rtp_end(&ep->rtp_pstn);
+		free(ep);
+		return;
+	}
+	/* all good - accept the new endpoint and return OK */
+	ep->next = conn->endp_list;
+	conn->endp_list = ep;
+	resp->res = TMGW_RESP_OK;
+	resp->ep_id = ep->ep_id;
+	bcopy(&ep->rtp_gsm.bound_addr, &resp->gsm_addr,
+		sizeof(struct sockaddr_in));
+	bcopy(&ep->rtp_pstn.bound_addr, &resp->pstn_addr,
+		sizeof(struct sockaddr_in));
+	syslog(LOG_INFO, "CRCX endpoint type %u id %u", ep->ep_type, ep->ep_id);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/ctrl_prot.c	Sat Jul 09 22:51:44 2022 -0800
@@ -0,0 +1,65 @@
+/*
+ * In this module we implement our control socket protocol.
+ */
+
+#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 <syslog.h>
+#include <unistd.h>
+#include "../include/tmgw_ctrl.h"
+#include "struct.h"
+#include "select.h"
+
+struct endpoint *
+find_ep_by_id(conn, id)
+	struct ctrl_conn *conn;
+	unsigned id;
+{
+	struct endpoint *ep;
+
+	for (ep = conn->endp_list; ep; ep = ep->next)
+		if (ep->ep_id == id)
+			return ep;
+	return 0;
+}
+
+void
+ctrl_message_handler(fd, conn)
+	struct ctrl_conn *conn;
+{
+	struct tmgw_ctrl_req req;
+	struct tmgw_ctrl_resp resp;
+	int rc;
+
+	rc = recv(fd, &req, sizeof req, 0);
+	if (rc < sizeof req) {
+		syslog(LOG_INFO, "ctrl connection closing %s active endpoints",
+			conn->endp_list ? "with" : "without");
+		close(fd);
+		FD_CLR(fd, &select_for_read);
+		dlcx_all_on_conn(conn);
+		free(conn);
+		return;
+	}
+	bzero(&resp, sizeof resp);
+	resp.transact_ref = req.transact_ref;
+	switch (req.opcode) {
+	case TMGW_CTRL_OP_CRCX:
+		process_crcx(conn, &req, &resp);
+		break;
+	case TMGW_CTRL_OP_MDCX:
+		process_mdcx(conn, &req, &resp);
+		break;
+	case TMGW_CTRL_OP_DLCX:
+		process_dlcx(conn, &req, &resp);
+		break;
+	default:
+		resp.res = TMGW_RESP_ERR_PROT;
+	}
+	send(fd, &resp, sizeof resp, 0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/ctrl_sock.c	Sat Jul 09 22:51:44 2022 -0800
@@ -0,0 +1,78 @@
+/*
+ * In this module we implement the logic of listening on the
+ * TMGW control socket and accepting control connections.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "struct.h"
+#include "select.h"
+
+static char ctrl_socket_pathname[] = "/var/gsm/tmgw_socket";
+
+extern void ctrl_message_handler();
+
+void
+ctrlsock_accept_handler(listener_fd)
+{
+	struct sockaddr_un sa;
+	socklen_t sa_len;
+	int conn_fd;
+	struct ctrl_conn *conn;
+
+	sa_len = sizeof sa;
+	conn_fd = accept(listener_fd, (struct sockaddr *) &sa, &sa_len);
+	if (conn_fd < 0) {
+		syslog(LOG_CRIT, "accept on UNIX socket: %m");
+		exit(1);
+	}
+	conn = malloc(sizeof(struct ctrl_conn));
+	if (!conn) {
+		syslog(LOG_CRIT, "malloc for ctrl socket conn: %m");
+		close(conn_fd);
+		return;
+	}
+	bzero(conn, sizeof(struct ctrl_conn));
+	update_max_fd(conn_fd);
+	FD_SET(conn_fd, &select_for_read);
+	select_handlers[conn_fd] = ctrl_message_handler;
+	select_data[conn_fd] = (void *) conn;
+	syslog(LOG_INFO, "accepted ctrl connection");
+}
+
+create_ctrl_socket()
+{
+	struct sockaddr_un sa;
+	unsigned sa_len;
+	int fd, rc;
+
+	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (fd < 0) {
+		syslog(LOG_CRIT, "socket(AF_UNIX, SOCK_SEQPACKET, 0): %m");
+		return(-1);
+	}
+	unlink(ctrl_socket_pathname);
+	fill_sockaddr_un(ctrl_socket_pathname, &sa, &sa_len);
+	rc = bind(fd, (struct sockaddr *) &sa, sa_len);
+	if (rc < 0) {
+		syslog(LOG_ERR, "bind to %s: %m", ctrl_socket_pathname);
+		return(-1);
+	}
+	rc = listen(fd, 3);
+	if (rc < 0) {
+		syslog(LOG_CRIT, "listen on UNIX socket: %m");
+		return(-1);
+	}
+	update_max_fd(fd);
+	FD_SET(fd, &select_for_read);
+	select_handlers[fd] = ctrlsock_accept_handler;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/dlcx.c	Sat Jul 09 22:51:44 2022 -0800
@@ -0,0 +1,88 @@
+/*
+ * In this module we implement our DLCX operation.
+ */
+
+#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 <syslog.h>
+#include <unistd.h>
+#include "../include/tmgw_ctrl.h"
+#include "../include/tmgw_const.h"
+#include "struct.h"
+#include "select.h"
+
+extern struct endpoint *find_ep_by_id();
+
+static struct endpoint *delq;
+
+static void
+put_on_delq(ep)
+	struct endpoint *ep;
+{
+	ep->next = delq;
+	delq = ep;
+}
+
+void
+process_dlcx(conn, req, resp)
+	struct ctrl_conn *conn;
+	struct tmgw_ctrl_req *req;
+	struct tmgw_ctrl_resp *resp;
+{
+	struct endpoint *ep, **epp;
+
+	for (epp = &conn->endp_list; ep = *epp; epp = &ep->next)
+		if (ep->ep_id == req->ep_id)
+			break;
+	if (!ep) {
+		resp->res = TMGW_RESP_ERR_PROT;
+		return;
+	}
+	syslog(LOG_INFO, "DLCX endpoint id %u", ep->ep_id);
+	*epp = ep->next;
+	put_on_delq(ep);
+	resp->res = TMGW_RESP_OK;
+}
+
+void
+dlcx_all_on_conn(conn)
+	struct ctrl_conn *conn;
+{
+	struct endpoint *ep, *np;
+
+	for (ep = conn->endp_list; ep; ep = np) {
+		np = ep->next;
+		put_on_delq(ep);
+	}
+}
+
+void
+free_rtp_end(roe)
+	struct rtp_one_end *roe;
+{
+	close(roe->rtp_fd);
+	close(roe->rtcp_fd);
+	FD_CLR(roe->rtp_fd, &select_for_read);
+	FD_CLR(roe->rtcp_fd, &select_for_read);
+}
+
+void
+free_deleted_endpoints()
+{
+	struct endpoint *ep, *np;
+
+	for (ep = delq; ep; ep = np) {
+		np = ep->next;
+		if (ep->ep_type & TMGW_EP_HAS_GSM_SOCK)
+			free_rtp_end(&ep->rtp_gsm);
+		if (ep->ep_type & TMGW_EP_HAS_PSTN_SOCK)
+			free_rtp_end(&ep->rtp_pstn);
+		free(ep);
+	}
+	delq = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/main.c	Sat Jul 09 22:51:44 2022 -0800
@@ -0,0 +1,63 @@
+/*
+ * Main module for themwi-mgw.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <signal.h>
+#include <syslog.h>
+#include <unistd.h>
+
+fd_set select_for_read;
+void (*select_handlers[FD_SETSIZE])();
+void *select_data[FD_SETSIZE];
+
+static int max_fd;
+
+update_max_fd(newfd)
+{
+	if (newfd >= FD_SETSIZE) {
+		syslog(LOG_CRIT, "FATAL: file descriptor %d >= FD_SETSIZE",
+			newfd);
+		exit(1);
+	}
+	if (newfd > max_fd)
+		max_fd = newfd;
+}
+
+main(argc, argv)
+	char **argv;
+{
+	fd_set fds;
+	int cc, i;
+
+	openlog("themwi-mgw", 0, LOG_LOCAL5);
+	read_config_file();
+	if (create_ctrl_socket() < 0) {
+		fprintf(stderr, "error creating TMGW control socket\n");
+		exit(1);
+	}
+	signal(SIGPIPE, SIG_IGN);
+	/* main select loop */
+	for (;;) {
+		bcopy(&select_for_read, &fds, sizeof(fd_set));
+		cc = select(max_fd+1, &fds, 0, 0, 0);
+		if (cc < 0) {
+			if (errno == EINTR)
+				continue;
+			syslog(LOG_CRIT, "select: %m");
+			exit(1);
+		}
+		for (i = 0; cc && i <= max_fd; i++) {
+			if (FD_ISSET(i, &fds)) {
+				select_handlers[i](i, select_data[i]);
+				cc--;
+			}
+		}
+		free_deleted_endpoints();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/mdcx.c	Sat Jul 09 22:51:44 2022 -0800
@@ -0,0 +1,72 @@
+/*
+ * In this module we implement our MDCX operation.
+ */
+
+#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 <syslog.h>
+#include "../include/tmgw_ctrl.h"
+#include "../include/tmgw_const.h"
+#include "struct.h"
+
+extern struct endpoint *find_ep_by_id();
+
+mdcx_operation(ep, req, resp)
+	struct endpoint *ep;
+	struct tmgw_ctrl_req *req;
+	struct tmgw_ctrl_resp *resp;
+{
+	if (req->setup_mask & TMGW_CTRL_MASK_GSM_CONN) {
+		if (ep->ep_type != TMGW_EP_TYPE_GATEWAY) {
+			resp->res = TMGW_RESP_ERR_PROT;
+			return(-1);
+		}
+		bcopy(&req->gsm_addr, &ep->rtp_gsm.remote_addr,
+			sizeof(struct sockaddr_in));
+		ep->gsm_payload_type = req->gsm_payload_type;
+		ep->gsm_payload_msg_type = req->gsm_payload_msg_type;
+	}
+	if (req->setup_mask & TMGW_CTRL_MASK_PSTN_CONN) {
+		if (ep->ep_type != TMGW_EP_TYPE_GATEWAY) {
+			resp->res = TMGW_RESP_ERR_PROT;
+			return(-1);
+		}
+		bcopy(&req->pstn_addr, &ep->rtp_pstn.remote_addr,
+			sizeof(struct sockaddr_in));
+		ep->pstn_payload_type = req->pstn_payload_type;
+	}
+	if (req->setup_mask & TMGW_CTRL_MASK_FWD_MODE) {
+		if (ep->ep_type != TMGW_EP_TYPE_GATEWAY ||
+		    ep->rtp_gsm.remote_addr.sin_family != AF_INET ||
+		    ep->rtp_pstn.remote_addr.sin_family != AF_INET) {
+			resp->res = TMGW_RESP_ERR_PROT;
+			return(-1);
+		}
+		ep->fwd_mode = req->fwd_mode;
+	}
+	return(0);
+}
+
+void
+process_mdcx(conn, req, resp)
+	struct ctrl_conn *conn;
+	struct tmgw_ctrl_req *req;
+	struct tmgw_ctrl_resp *resp;
+{
+	struct endpoint *ep;
+	int rc;
+
+	ep = find_ep_by_id(conn, req->ep_id);
+	if (!ep) {
+		resp->res = TMGW_RESP_ERR_PROT;
+		return;
+	}
+	rc = mdcx_operation(ep, req, resp);
+	if (rc == 0)
+		resp->res = TMGW_RESP_OK;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/readconf.c	Sat Jul 09 22:51:44 2022 -0800
@@ -0,0 +1,187 @@
+/*
+ * In this module we implement the reading of /var/gsm/themwi-mgw.cfg:
+ * we parse and save the configured IP address and port range for each
+ * of our two sides, GSM and PSTN.
+ */
+
+#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>
+#include "struct.h"
+
+struct bind_range_cfg bind_range_gsm, bind_range_pstn;
+
+static char config_file_pathname[] = "/var/gsm/themwi-mgw.cfg";
+
+struct parse_state {
+	int lineno;
+	int set_mask;
+};
+
+static void
+handle_bind_ip(st, kw, brc, line)
+	struct parse_state *st;
+	char *kw, *line;
+	struct bind_range_cfg *brc;
+{
+	char *cp, *np;
+
+	for (cp = line; isspace(*cp); cp++)
+		;
+	if (*cp == '\0' || *cp == '#') {
+inv_syntax:	fprintf(stderr,
+			"%s line %d: %s setting requires one argument\n",
+			config_file_pathname, st->lineno, kw);
+		exit(1);
+	}
+	for (np = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp != '\0' && *cp != '#')
+		goto inv_syntax;
+	brc->bind_ip.s_addr = inet_addr(np);
+	if (brc->bind_ip.s_addr == INADDR_NONE) {
+		fprintf(stderr,
+			"%s line %d: invalid IP address argument \"%s\"\n",
+			config_file_pathname, st->lineno, np);
+		exit(1);
+	}
+}
+
+static void
+handle_port_range(st, kw, brc, line)
+	struct parse_state *st;
+	char *kw, *line;
+	struct bind_range_cfg *brc;
+{
+	char *cp, *np1, *np2;
+
+	for (cp = line; isspace(*cp); cp++)
+		;
+	if (!isdigit(*cp)) {
+inv_syntax:	fprintf(stderr,
+		"%s line %d: %s setting requires two numeric arguments\n",
+			config_file_pathname, st->lineno, kw);
+		exit(1);
+	}
+	for (np1 = cp; isdigit(*cp); cp++)
+		;
+	if (!isspace(*cp))
+		goto inv_syntax;
+	while (isspace(*cp))
+		cp++;
+	if (!isdigit(*cp))
+		goto inv_syntax;
+	for (np2 = cp; isdigit(*cp); cp++)
+		;
+	if (*cp && !isspace(*cp))
+		goto inv_syntax;
+	while (isspace(*cp))
+		cp++;
+	if (*cp != '\0' && *cp != '#')
+		goto inv_syntax;
+	brc->port_range_start = atoi(np1);
+	brc->port_range_end = atoi(np2);
+	if (brc->port_range_start & 1) {
+		fprintf(stderr, "%s line %d: start port must be even\n",
+			config_file_pathname, st->lineno);
+		exit(1);
+	}
+	if (!(brc->port_range_end & 1)) {
+		fprintf(stderr, "%s line %d: end port must be odd\n",
+			config_file_pathname, st->lineno);
+		exit(1);
+	}
+	if (brc->port_range_end <= brc->port_range_start) {
+		fprintf(stderr,
+		"%s line %d: end port must be greater than start port\n",
+			config_file_pathname, st->lineno);
+		exit(1);
+	}
+	brc->port_next = brc->port_range_start;
+	brc->port_tries = (brc->port_range_end - brc->port_range_start + 1) / 2;
+}
+
+static void
+process_line(st, line)
+	struct parse_state *st;
+	char *line;
+{
+	char *cp, *np;
+	void (*handler)();
+	struct bind_range_cfg *ipside;
+	int set_id;
+
+	if (!index(line, '\n')) {
+		fprintf(stderr, "%s line %d: too long or missing newline\n",
+			config_file_pathname, 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, "gsm-ip-addr")) {
+		handler = handle_bind_ip;
+		ipside = &bind_range_gsm;
+		set_id = 1;
+	} else if (!strcmp(np, "gsm-port-range")) {
+		handler = handle_port_range;
+		ipside = &bind_range_gsm;
+		set_id = 2;
+	} else if (!strcmp(np, "pstn-ip-addr")) {
+		handler = handle_bind_ip;
+		ipside = &bind_range_pstn;
+		set_id = 4;
+	} else if (!strcmp(np, "pstn-port-range")) {
+		handler = handle_port_range;
+		ipside = &bind_range_pstn;
+		set_id = 8;
+	} else {
+		fprintf(stderr, "%s line %d: non-understood keyword \"%s\"\n",
+			config_file_pathname, st->lineno, np);
+		exit(1);
+	}
+	if (st->set_mask & set_id) {
+		fprintf(stderr, "%s line %d: duplicate %s setting\n",
+			config_file_pathname, st->lineno, np);
+		exit(1);
+	}
+	handler(st, np, ipside, cp);
+	st->set_mask |= set_id;
+}
+
+read_config_file()
+{
+	FILE *inf;
+	struct parse_state pst;
+	char linebuf[256];
+
+	inf = fopen(config_file_pathname, "r");
+	if (!inf) {
+		perror(config_file_pathname);
+		exit(1);
+	}
+	pst.set_mask = 0;
+	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",
+			config_file_pathname);
+		exit(1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/select.h	Sat Jul 09 22:51:44 2022 -0800
@@ -0,0 +1,5 @@
+/* extern declarations for select loop global variables */
+
+extern fd_set select_for_read;
+extern void (*select_handlers[])();
+extern void *select_data[];
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/struct.h	Sat Jul 09 22:51:44 2022 -0800
@@ -0,0 +1,36 @@
+/*
+ * This header file defines internal data structures for themwi-mgw.
+ */
+
+struct bind_range_cfg {
+	struct in_addr	bind_ip;
+	unsigned	port_range_start;
+	unsigned	port_range_end;
+	unsigned	port_next;
+	unsigned	port_tries;
+};
+
+struct rtp_one_end {
+	int	rtp_fd;
+	int	rtcp_fd;
+	struct sockaddr_in bound_addr;
+	struct sockaddr_in remote_addr;
+};
+
+struct endpoint {
+	unsigned	ep_type;
+	struct rtp_one_end rtp_gsm;
+	struct rtp_one_end rtp_pstn;
+	unsigned	gsm_payload_type;
+	unsigned	gsm_payload_msg_type;
+	unsigned	pstn_payload_type;
+	unsigned	fwd_mode;
+	/* linked list management */
+	unsigned	ep_id;
+	struct endpoint	*next;
+};
+
+struct ctrl_conn {
+	struct endpoint	*endp_list;
+	unsigned	next_ep_id;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/udpsink.c	Sat Jul 09 22:51:44 2022 -0800
@@ -0,0 +1,23 @@
+/*
+ * There are times when we hold open some UDP sockets, but can't do
+ * anything with incoming packets other than discard them.  This
+ * situation holds for dummy one-sided endpoints, for gateway
+ * endpoints that haven't been fully connected yet, and for all RTCP
+ * packets at the moment.
+ *
+ * In this module we implement the generic UDP "black hole" sink.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void
+udp_sink_rcvr(fd)
+{
+	u_char buf[512];
+
+	recv(fd, buf, sizeof buf, 0);
+}