view sip-in/readconf.c @ 124:7e04d28fae8b

sip-in: default use-100rel to no BulkVS servers act badly when we send a reliable 180 Ringing response to an incoming call, even though they advertise 100rel support in the Supported header in the INVITE packet, and we probably won't be implementing 100rel for outbound because doing per-the-spec PRACK as a UAC is just too burdensome. Therefore, we need to consider 100rel extension as not-really-supported in themwi-system-sw.
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 01 Oct 2022 15:54:50 -0800
parents 9b87894704eb
children
line wrap: on
line source

/*
 * 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;
int cfg_force_pcma;
unsigned cfg_retrans_timeout = 500;
unsigned cfg_retrans_count = 10;
unsigned max_forwards = 70;
unsigned sip_linger_acked = 5;
unsigned sip_linger_gotbye = 30;
unsigned sip_linger_error = 180;

static char config_file_pathname[] = "/var/gsm/themwi-sip-in.cfg";

struct parse_state {
	int lineno;
	int set_mask;
};

static void
require_one_arg(st, kw, arg)
	struct parse_state *st;
	char *kw, *arg;
{
	char *cp;

	if (*arg == '\0' || *arg == '#') {
inv_syntax:	fprintf(stderr,
			"%s line %d: %s setting requires one argument\n",
			config_file_pathname, st->lineno, kw);
		exit(1);
	}
	for (cp = arg; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#')
		goto inv_syntax;
}

static void
handle_ip(st, kw, arg, var)
	struct parse_state *st;
	char *kw, *arg;
	struct in_addr *var;
{
	require_one_arg(st, kw, arg);
	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, arg, var)
	struct parse_state *st;
	char *kw, *arg;
	unsigned *var;
{
	char *endp;

	require_one_arg(st, kw, arg);
	*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, arg, var)
	struct parse_state *st;
	char *kw, *arg;
	int *var;
{
	require_one_arg(st, kw, arg);
	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
handle_retrans_conf(st, kw, arg)
	struct parse_state *st;
	char *kw, *arg;
{
	char *cp = arg;

	if (!isdigit(*cp)) {
inv:		fprintf(stderr,
		"%s line %d: %s setting requires two numeric arguments\n",
			config_file_pathname, st->lineno, kw);
		exit(1);
	}
	cfg_retrans_timeout = strtoul(cp, &cp, 10);
	while (isspace(*cp))
		cp++;
	if (!isdigit(*cp))
		goto inv;
	cfg_retrans_count = strtoul(cp, &cp, 10);
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#')
		goto inv;
}

static void
process_line(st, line)
	struct parse_state *st;
	char *line;
{
	char *cp, *kw;
	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 (kw = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	if (!strcmp(kw, "bind-ip")) {
		handler = handle_ip;
		var = &sip_bind_ip;
		set_id = 1;
	} else if (!strcmp(kw, "bind-port")) {
		handler = handle_num;
		var = &sip_bind_port;
		set_id = 2;
	} else if (!strcmp(kw, "use-100rel")) {
		handler = handle_bool;
		var = &cfg_use_100rel;
		set_id = 0;
	} else if (!strcmp(kw, "force-pcma-codec")) {
		handler = handle_bool;
		var = &cfg_force_pcma;
		set_id = 0;
	} else if (!strcmp(kw, "sip-udp-retrans")) {
		handler = handle_retrans_conf;
		var = (void *) 0;
		set_id = 0;
	} else if (!strcmp(kw, "sip-linger-acked")) {
		handler = handle_num;
		var = &sip_linger_acked;
		set_id = 0;
	} else if (!strcmp(kw, "sip-linger-got-bye")) {
		handler = handle_num;
		var = &sip_linger_gotbye;
		set_id = 0;
	} else if (!strcmp(kw, "sip-linger-error")) {
		handler = handle_num;
		var = &sip_linger_error;
		set_id = 0;
	} else if (!strcmp(kw, "max-forwards")) {
		handler = &handle_num;
		var = &max_forwards;
		set_id = 0;
	} else {
		fprintf(stderr, "%s line %d: non-understood keyword \"%s\"\n",
			config_file_pathname, st->lineno, kw);
		exit(1);
	}
	if (st->set_mask & set_id) {
		fprintf(stderr, "%s line %d: duplicate %s setting\n",
			config_file_pathname, st->lineno, kw);
		exit(1);
	}
	while (isspace(*cp))
		cp++;
	handler(st, kw, cp, var);
	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);
	}
}