changeset 47:62f39c7cee15

themwi-sip-in skeleton started
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 06 Sep 2022 20:33:56 -0800
parents 5427b26525cd
children 8117d8ee44a5
files .hgignore Makefile sip-in/Makefile sip-in/main.c sip-in/mgw_sock.c sip-in/readconf.c sip-in/sip_log.c sip-in/sip_uas.c sip-in/sip_udp.c
diffstat 9 files changed, 542 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Tue Sep 06 20:29:44 2022 -0800
+++ b/.hgignore	Tue Sep 06 20:33:56 2022 -0800
@@ -8,6 +8,8 @@
 
 ^mtctest/themwi-test-mtc$
 
+^sip-in/themwi-sip-in$
+
 ^utils/sip-rx-test$
 ^utils/sip-udp-dump$
 ^utils/themwi-check-own$
--- a/Makefile	Tue Sep 06 20:29:44 2022 -0800
+++ b/Makefile	Tue Sep 06 20:33:56 2022 -0800
@@ -1,7 +1,7 @@
 CC=	gcc
 CFLAGS=	-O2
 
-PROGDIR=mgw mncc mtctest utils
+PROGDIR=mgw mncc mtctest sip-in utils
 LIBDIR=	libnumdb libsip libutil
 SUBDIR=	${PROGDIR} ${LIBDIR}
 
@@ -10,6 +10,7 @@
 mgw:		libutil
 mncc:		libnumdb libutil
 mtctest:	libnumdb libutil
+sip-in:		libnumdb libsip libutil
 utils:		libnumdb libsip libutil
 
 ${SUBDIR}: FRC
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-in/Makefile	Tue Sep 06 20:33:56 2022 -0800
@@ -0,0 +1,17 @@
+CC=	gcc
+CFLAGS=	-O2
+PROG=	themwi-sip-in
+OBJS=	main.o mgw_sock.o readconf.o sip_log.o sip_uas.o sip_udp.o
+LIBS=	../libnumdb/libnumdb.a ../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-in/main.c	Tue Sep 06 20:33:56 2022 -0800
@@ -0,0 +1,72 @@
+/*
+ * Main module for themwi-sip-in.
+ */
+
+#include <sys/types.h>
+#include <sys/time.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>
+
+extern int mgw_socket, sip_socket;
+
+static int max_fd;
+
+struct timeval cur_event_time;
+
+update_max_fd(newfd)
+{
+	if (newfd > max_fd)
+		max_fd = newfd;
+}
+
+main(argc, argv)
+	char **argv;
+{
+	fd_set fds;
+	int rc;
+
+	openlog("themwi-sip-in", 0, LOG_LOCAL5);
+	read_config_file();
+	if (read_number_db() < 0) {
+		fprintf(stderr, "error reading number database\n");
+		exit(1);
+	}
+	if (open_tmgw_socket() < 0) {
+		fprintf(stderr, "error connecting to themwi-mgw socket\n");
+		exit(1);
+	}
+	if (open_sip_udp_socket() < 0) {
+		fprintf(stderr, "error opening SIP UDP socket\n");
+		exit(1);
+	}
+	if (argv[1]) {
+		rc = open_sip_log_file(argv[1]);
+		if (rc < 0)
+			exit(1);	/* error msg already printed */
+	}
+	signal(SIGPIPE, SIG_IGN);
+	/* main select loop */
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(mgw_socket, &fds);
+		FD_SET(sip_socket, &fds);
+		rc = select(max_fd+1, &fds, 0, 0, 0);
+		if (rc < 0) {
+			if (errno == EINTR)
+				continue;
+			syslog(LOG_CRIT, "select: %m");
+			exit(1);
+		}
+		gettimeofday(&cur_event_time, 0);
+		if (FD_ISSET(mgw_socket, &fds))
+			mgw_socket_select();
+		if (FD_ISSET(sip_socket, &fds))
+			sip_socket_select();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-in/mgw_sock.c	Tue Sep 06 20:33:56 2022 -0800
@@ -0,0 +1,57 @@
+/*
+ * In this module we implement our local socket interface to themwi-mgw.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "../include/tmgw_ctrl.h"
+
+static char tmgw_socket_pathname[] = "/var/gsm/tmgw_socket";
+
+int mgw_socket;
+
+open_tmgw_socket()
+{
+	struct sockaddr_un sa;
+	unsigned sa_len;
+	int rc;
+
+	mgw_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (mgw_socket < 0) {
+		syslog(LOG_CRIT, "socket(AF_UNIX, SOCK_SEQPACKET, 0): %m");
+		return(-1);
+	}
+	fill_sockaddr_un(tmgw_socket_pathname, &sa, &sa_len);
+	rc = connect(mgw_socket, (struct sockaddr *) &sa, sa_len);
+	if (rc < 0) {
+		syslog(LOG_ERR, "connect to %s: %m", tmgw_socket_pathname);
+		return(-1);
+	}
+	update_max_fd(mgw_socket);
+	return(0);
+}
+
+void
+mgw_socket_select()
+{
+	struct tmgw_ctrl_resp msg;
+	int rc;
+
+	rc = recv(mgw_socket, &msg, sizeof msg, 0);
+	if (rc < 0) {
+		syslog(LOG_CRIT, "error reading from TMGW socket: %m");
+		exit(1);
+	}
+	if (rc != sizeof(struct tmgw_ctrl_resp)) {
+		syslog(LOG_CRIT,
+			"response packet from TMGW has wrong length: %d bytes",
+			rc);
+		exit(1);
+	}
+	/* processing to be implemented */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-in/readconf.c	Tue Sep 06 20:33:56 2022 -0800
@@ -0,0 +1,164 @@
+/*
+ * In this module we implement the reading of /var/gsm/themwi-sip-in.cfg:
+ * the main settings are bind-ip and bind-port, but we also have some
+ * optional config settings.
+ */
+
+#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;
+unsigned sip_bind_port;
+int cfg_use_100rel;
+
+static char config_file_pathname[] = "/var/gsm/themwi-sip-in.cfg";
+
+struct parse_state {
+	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",
+			config_file_pathname, 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",
+			config_file_pathname, st->lineno, arg);
+		exit(1);
+	}
+}
+
+static void
+handle_bool(st, kw, var, arg)
+	struct parse_state *st;
+	char *kw, *arg;
+	int *var;
+{
+	if (!strcmp(arg, "true") || !strcmp(arg, "on") || !strcmp(arg, "yes")
+	    || !strcmp(arg, "1")) {
+		*var = 1;
+		return;
+	}
+	if (!strcmp(arg, "false") || !strcmp(arg, "off") || !strcmp(arg, "no")
+	    || !strcmp(arg, "0")) {
+		*var = 0;
+		return;
+	}
+	fprintf(stderr, "%s line %d: invalid boolean argument \"%s\"\n",
+		config_file_pathname, st->lineno, arg);
+	exit(1);
+}
+
+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",
+			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, "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, "use-100rel")) {
+		handler = handle_bool;
+		var = &cfg_use_100rel;
+		set_id = 0;
+	} 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);
+	}
+	while (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, 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()
+{
+	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 != 3) {
+		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/sip-in/sip_log.c	Tue Sep 06 20:33:56 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>
+
+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-in/sip_uas.c	Tue Sep 06 20:33:56 2022 -0800
@@ -0,0 +1,75 @@
+/*
+ * Basic UAS functions for themwi-sip-in.
+ */
+
+#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 "../libsip/parse.h"
+#include "../libsip/uas_basic.h"
+#include "../libsip/out_msg.h"
+
+static void
+method_tbi(req, ess, sin)
+	struct sip_pkt_rx *req;
+	struct uas_parse_hdrs *ess;
+	struct sockaddr_in *sin;
+{
+	syslog(LOG_ERR, "SIP method %s remains to be implemented",
+		req->req_method);
+}
+
+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;
+
+	start_response_out_msg(&resp, "405 Method not supported");
+	rc = add_resp_basic_headers(&resp, ess, req->req_method);
+	if (rc < 0) {
+too_long:	syslog(LOG_ERR, "sending 405 error: response length exceeded");
+		return;
+	}
+	rc = out_msg_add_header(&resp, "Allow", "INVITE,ACK,CANCEL,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) {
+		syslog(LOG_ERR, "SIP %.16s request: bad or missing %s header",
+			msg->req_method, ess.error_field);
+		return;
+	}
+	/* dispatch by method */
+	if (!strcmp(msg->req_method, "INVITE"))
+		method_tbi(msg, &ess, sin);
+	else if (!strcmp(msg->req_method, "ACK"))
+		method_tbi(msg, &ess, sin);
+	else if (!strcmp(msg->req_method, "CANCEL"))
+		method_tbi(msg, &ess, sin);
+	else if (!strcmp(msg->req_method, "BYE"))
+		method_tbi(msg, &ess, sin);
+	else
+		unsupported_method(msg, &ess, sin);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sip-in/sip_udp.c	Tue Sep 06 20:33:56 2022 -0800
@@ -0,0 +1,85 @@
+/*
+ * 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 <syslog.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) {
+		syslog(LOG_CRIT, "socket(AF_INET, SOCK_DGRAM, 0): %m");
+		return(-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) {
+		syslog(LOG_CRIT, "bind of SIP UDP socket: %m");
+		return(-1);
+	}
+	update_max_fd(sip_socket);
+	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)
+		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) {
+		/* parse errors */
+		if (rc == -2 && pkt.parse_msgtype == SIP_MSG_TYPE_REQ)
+			syslog(LOG_ERR,
+				"SIP %.16s msg exceeds MAX_HEADER_FIELDS",
+				pkt.req_method);
+		else if (rc == -2 && pkt.parse_msgtype == SIP_MSG_TYPE_RESP)
+			syslog(LOG_ERR,
+				"SIP response msg exceeds MAX_HEADER_FIELDS");
+		/* in any case, silently discard */
+		return;
+	}
+	/* dispatch good-so-far SIP message */
+	if (pkt.parse_msgtype == SIP_MSG_TYPE_REQ)
+		process_sip_request(&pkt, &sin);
+}
+
+void
+sip_tx_packet(msg, sin)
+	struct sip_msg_out *msg;
+	struct sockaddr_in *sin;
+{
+	log_sip_msg_tx(msg->buf, msg->msg_len, sin);
+	/* actual UDP send to BulkVS omitted at this development stage */
+}