diff utils/themwi-update-out-routes.c @ 130:44dc809ffec0

themwi-update-out-routes utility written, compiles
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 06 Oct 2022 20:56:14 -0800
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/themwi-update-out-routes.c	Thu Oct 06 20:56:14 2022 -0800
@@ -0,0 +1,473 @@
+/*
+ * This program reads (parses) ThemWi config file /var/gsm/out-routes,
+ * generates the compiled binary form of this database, and then makes
+ * it live via atomic rename.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include "../include/out_routes.h"
+
+#define	MAX_DEST_ENTRIES	16
+#define	MAX_INN_ENTRIES		64
+#define	MAX_SPC_NUM_ENTRIES	64
+
+static struct sip_out_dest dest_records[MAX_DEST_ENTRIES];
+static char *dest_names[MAX_DEST_ENTRIES];
+static struct inn_route inn_records[MAX_INN_ENTRIES];
+static struct special_num_route special_num_records[MAX_SPC_NUM_ENTRIES];
+static unsigned dest_rec_count, inn_rec_count, special_num_count;
+
+static char *system_dir;
+static FILE *inf;
+static int lineno;
+static char linebuf[256];
+
+static int
+find_dest_by_name(sought_name)
+	char *sought_name;
+{
+	unsigned n;
+
+	for (n = 0; n < dest_rec_count; n++)
+		if (!strcmp(dest_names[n], sought_name))
+			return n;
+	return -1;
+}
+
+static int
+find_dest_by_number(target_num)
+	char *target_num;
+{
+	unsigned inn_index;
+	struct inn_route *rec;
+	char *pp, *tp;
+
+	for (inn_index = 0; inn_index < inn_rec_count; inn_index++) {
+		rec = inn_records + inn_index;
+		pp = rec->prefix;
+		tp = target_num;
+		while (*pp && *pp == *tp) {
+			pp++;
+			tp++;
+		}
+		if (*pp)
+			continue;
+		return rec->sip_dest_id;
+	}
+	return -1;
+}
+
+static void
+handle_dest_line(cp)
+	char *cp;
+{
+	char *name, *name_copy, *domain, *ip_str, *port_str;
+	struct sip_out_dest *rec;
+	unsigned portnum;
+	int rc;
+
+	for (name = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#') {
+inv_syntax:	fprintf(stderr, "out-routes line %d: invalid syntax for dest\n",
+			lineno);
+		exit(1);
+	}
+	for (domain = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#')
+		goto inv_syntax;
+	for (ip_str = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#')
+		port_str = 0;
+	else {
+		for (port_str = cp; *cp && !isspace(*cp); cp++)
+			;
+		if (*cp)
+			*cp++ = '\0';
+		while (isspace(*cp))
+			cp++;
+		if (*cp != '\0' && *cp != '#')
+			goto inv_syntax;
+	}
+	rc = find_dest_by_name(name);
+	if (rc >= 0) {
+		fprintf(stderr,
+		"out-routes line %d: duplicate destination name \"%s\"\n",
+			lineno, name);
+		exit(1);
+	}
+	if (dest_rec_count >= MAX_DEST_ENTRIES) {
+		fprintf(stderr,
+			"out-routes line %d: MAX_DEST_ENTRIES exceeded\n",
+			lineno);
+		exit(1);
+	}
+	name_copy = strdup(name);
+	if (!name_copy) {
+		perror("strdup");
+		exit(1);
+	}
+	dest_names[dest_rec_count] = name_copy;
+	rec = dest_records + dest_rec_count;
+	if (strlen(domain) > MAX_SIP_DEST_DOMAIN) {
+		fprintf(stderr,
+			"out-routes line %d: dest domain string is too long\n",
+			lineno);
+		exit(1);
+	}
+	strcpy(rec->domain, domain);
+	rec->sin.sin_family = AF_INET;
+	rec->sin.sin_addr.s_addr = inet_addr(ip_str);
+	if (rec->sin.sin_addr.s_addr == INADDR_NONE) {
+		fprintf(stderr,
+			"out-routes line %d: dest IP address is invalid\n",
+			lineno);
+		exit(1);
+	}
+	if (port_str) {
+		portnum = strtoul(port_str, &cp, 10);
+		if (*cp)
+			goto inv_syntax;
+	} else
+		portnum = 5060;
+	rec->sin.sin_port = htons(portnum);
+	dest_rec_count++;
+}
+
+static void
+handle_inn_route(cp)
+	char *cp;
+{
+	char *prefix, *dest_name;
+	struct inn_route *rec;
+	int rc, dest_id;
+
+	for (prefix = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#') {
+inv_syntax:	fprintf(stderr,
+			"out-routes line %d: invalid syntax for inn-route\n",
+			lineno);
+		exit(1);
+	}
+	for (dest_name = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp != '\0' && *cp != '#')
+		goto inv_syntax;
+	rc = grok_number_string(prefix, 1);
+	if (rc < 1)
+		goto inv_syntax;
+	if (rc > MAX_INN_PREFIX) {
+		fprintf(stderr,
+			"out-routes line %d: inn-route prefix is too long\n",
+			lineno);
+		exit(1);
+	}
+	dest_id = find_dest_by_name(dest_name);
+	if (dest_id < 0) {
+		fprintf(stderr,
+		"out-routes line %d: SIP destination \"%s\" not defined\n",
+			lineno, dest_name);
+		exit(1);
+	}
+	if (inn_rec_count >= MAX_INN_ENTRIES) {
+		fprintf(stderr,
+			"out-routes line %d: MAX_INN_ENTRIES exceeded\n",
+			lineno);
+		exit(1);
+	}
+	rec = inn_records + inn_rec_count;
+	dehyphen_number_string(prefix, rec->prefix);
+	rec->sip_dest_id = dest_id;
+	inn_rec_count++;
+}
+
+static void
+preen_special_num_code(num_code)
+	char *num_code;
+{
+	char *cp;
+	int c;
+	unsigned n;
+
+	n = 0;
+	for (cp = num_code; *cp; ) {
+		c = *cp++;
+		if (is_valid_ext_digit(c))
+			n++;
+		else {
+			fprintf(stderr,
+		"out-routes line %d: special-num string \"%s\" is invalid\n",
+				lineno, num_code);
+			exit(1);
+		}
+	}
+	if (n > MAX_SPECIAL_NUM) {
+		fprintf(stderr,
+		"out-routes line %d: special-num string \"%s\" is too long\n",
+			lineno, num_code);
+		exit(1);
+	}
+}
+
+static void
+handle_special_num_map_to(num_code, cp)
+	char *num_code, *cp;
+{
+	struct special_num_route *rec;
+	char *tgt_num_src;
+	int rc, dest_id;
+
+	while (isspace(*cp))
+		cp++;
+	if (*cp++ != '+') {
+inv_syntax:	fprintf(stderr,
+		"out-routes line %d: invalid syntax for special-num map-to\n",
+			lineno);
+		exit(1);
+	}
+	if (!isdigit(*cp))
+		goto inv_syntax;
+	for (tgt_num_src = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp != '\0' && *cp != '#')
+		goto inv_syntax;
+	rc = grok_number_string(tgt_num_src, 1);
+	if (rc < 1)
+		goto inv_syntax;
+	if (rc > MAX_E164_NUMBER) {
+		fprintf(stderr,
+		"out-routes line %d: map-to number is too long for E.164\n",
+			lineno);
+		exit(1);
+	}
+	rec = special_num_records + special_num_count;
+	strcpy(rec->special_num, num_code);
+	rec->sip_user[0] = '+';
+	dehyphen_number_string(tgt_num_src, rec->sip_user+1);
+	dest_id = find_dest_by_number(rec->sip_user+1);
+	if (dest_id < 0) {
+		fprintf(stderr,
+			"out-routes line %d: no inn-route for map-to number\n",
+			lineno);
+		exit(1);
+	}
+	rec->sip_dest_id = dest_id;
+	special_num_count++;
+}
+
+static void
+handle_special_num_route_to(num_code, cp)
+	char *num_code, *cp;
+{
+	struct special_num_route *rec;
+	char *dest_name;
+	int dest_id;
+
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#') {
+inv_syntax:	fprintf(stderr,
+		"out-routes line %d: invalid syntax for special-num route-to\n",
+			lineno);
+		exit(1);
+	}
+	for (dest_name = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp != '\0' && *cp != '#')
+		goto inv_syntax;
+	dest_id = find_dest_by_name(dest_name);
+	if (dest_id < 0) {
+		fprintf(stderr,
+		"out-routes line %d: SIP destination \"%s\" not defined\n",
+			lineno, dest_name);
+		exit(1);
+	}
+	rec = special_num_records + special_num_count;
+	strcpy(rec->special_num, num_code);
+	strcpy(rec->sip_user, num_code);
+	rec->sip_dest_id = dest_id;
+	special_num_count++;
+}
+
+static void
+handle_special_num(cp)
+	char *cp;
+{
+	char *num_code, *handling_kw;
+
+	for (num_code = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	preen_special_num_code(num_code);
+	if (special_num_count >= MAX_SPC_NUM_ENTRIES) {
+		fprintf(stderr,
+			"out-routes line %d: MAX_SPC_NUM_ENTRIES exceeded\n",
+			lineno);
+		exit(1);
+	}
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#') {
+inv_syntax:	fprintf(stderr,
+			"out-routes line %d: invalid syntax for special-num\n",
+			lineno);
+		exit(1);
+	}
+	for (handling_kw = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	if (!strcmp(handling_kw, "map-to"))
+		handle_special_num_map_to(num_code, cp);
+	else if (!strcmp(handling_kw, "route-to"))
+		handle_special_num_route_to(num_code, cp);
+	else
+		goto inv_syntax;
+}
+
+static void
+process_line()
+{
+	char *cp, *np;
+	void (*handler)();
+
+	if (!index(linebuf, '\n')) {
+		fprintf(stderr,
+			"out-routes line %d: too long or missing newline\n",
+			lineno);
+		exit(1);
+	}
+	for (cp = linebuf; isspace(*cp); cp++)
+		;
+	if (*cp == '\0' || *cp == '#')
+		return;
+	for (np = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	if (!strcmp(np, "dest"))
+		handler = handle_dest_line;
+	else if (!strcmp(np, "inn-route"))
+		handler = handle_inn_route;
+	else if (!strcmp(np, "special-num"))
+		handler = handle_special_num;
+	else {
+		fprintf(stderr,
+			"out-routes line %d: non-understood keyword \"%s\"\n",
+			lineno, np);
+		exit(1);
+	}
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#') {
+		fprintf(stderr,
+		"out-routes line %d: missing argument after \"%s\" keyword\n",
+			lineno, np);
+		exit(1);
+	}
+	handler(cp);
+}
+
+static void
+emit_output()
+{
+	FILE *outf;
+	struct out_routes_header hdr;
+
+	outf = fopen("out-routes.newbin", "w");
+	if (!outf) {
+		perror("creating out-routes.newbin");
+		exit(1);
+	}
+	hdr.num_dest = dest_rec_count;
+	hdr.num_inn = inn_rec_count;
+	hdr.num_special = special_num_count;
+	if (fwrite(&hdr, sizeof hdr, 1, outf) != 1) {
+write_err:	fprintf(stderr, "error writing to new binary file\n");
+		exit(1);
+	}
+	if (fwrite(dest_records, sizeof(dest_records[0]), dest_rec_count, outf)
+	    != dest_rec_count)
+		goto write_err;
+	if (fwrite(inn_records, sizeof(inn_records[0]), inn_rec_count, outf)
+	    != inn_rec_count)
+		goto write_err;
+	if (fwrite(special_num_records, sizeof(special_num_records[0]),
+		   special_num_count, outf) != special_num_count)
+		goto write_err;
+	fclose(outf);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	if (argc > 2) {
+		fprintf(stderr, "usage: %s [directory]\n", argv[0]);
+		exit(1);
+	}
+	if (argv[1])
+		system_dir = argv[1];
+	else
+		system_dir = "/var/gsm";
+	if (chdir(system_dir) < 0) {
+		perror(system_dir);
+		exit(1);
+	}
+	inf = fopen("out-routes", "r");
+	if (!inf) {
+		perror("opening out-routes");
+		exit(1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++)
+		process_line();
+	fclose(inf);
+	emit_output();
+	/* make it live */
+	if (rename("out-routes.newbin", "out-routes.bin") < 0) {
+		perror("rename");
+		exit(1);
+	}
+	exit(0);
+}