From 17287576555a5c46fa23549e2e5f073660dccb70 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 21 Apr 1999 09:51:03 +0200 Subject: Import ax25-tools 0.0.1 from tarball --- ax25/ax25d.c | 1195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1195 insertions(+) create mode 100644 ax25/ax25d.c (limited to 'ax25/ax25d.c') diff --git a/ax25/ax25d.c b/ax25/ax25d.c new file mode 100644 index 0000000..8d45713 --- /dev/null +++ b/ax25/ax25d.c @@ -0,0 +1,1195 @@ +/* + * This is my version of axl.c, written for the LBBS code to make it + * compatable with the kernel AX25 driver. It appears to work, with + * my setup, so it'll probably not work else where :-). + * + * This was inspired by the example code written by Alan Cox (GW4PTS) + * axl.c in AX25USER.TGZ from sunacm.swan.ac.uk. + * + * + * Copyright (C) 1995, 1996 by Darryl L. Miles, G7LED. + * Copyright (C) 1996 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * + * Just a quickie Feb 1995. + * It *was* just a quickie (at the time) but you'd know how these things + * just... Apr 1995. + * + * If your AX25/NETROM system is relying on this code for + * securetty/firewalling then please be aware this has been coded + * with the intent on striving on through system/(mis)configuration + * errors in the hope that at worst it will run with a degraded + * service. Rather than leave your system providing no service at + * all, if opinions require the old behavior back when let me know + * and I'll #ifdef it in. + * + * + * History: + * + * 1.0 Feb 1995 Basic AX25 listening daemon, Multi-port, Call + * matching, etc... + * + * 1.1 Feb 1995 Moved entry scanning before fork(). + * Added setgroups() to plug security hole. + * Minor fixes + Improved handling. + * + * 1.2 Apr 1995 NETROM support added from developing AX25 + * 028b/029. + * Added 'defaults' port setting. + * Added FLAG_NODIGIS. + * + * 1.3 Jul 1995 Make it a little more intelligent about what to + * do with errors. + * Added exec and argv[0] as two different fields, + * much like inetd uses. + * + * 1.4 Aug 1995 Confirmed support for AX25 030 (1.3.20 + hacks), + * it appears to work. + * It will now bootup even if initial config errors + * occur when setting up and binding (e.g. port(s) + * down), it will skip the port(s) with a problem + * and listen out on those which are left standing. + * + * 1.5 Aug 1995 Updated old (buggy) libax25.a function copies in axl. + * Causing all sorts of problems. + * + * 1.6 Aug 1995 Reset the 'defaults' entry's when we start parsing + * a new interface. + * + * 1.7 Dec 1995 Added BROKEN_NETROM_KERNEL define for setsockopt. + * + * 1.8 Jan 1996 Added support for AX25_BIND_ANY_DEVICE, specify just + * [CALL-X VIA *]. + * Better param parsing, T1 and T2 now using the real + * time in seconds as params, and not kernel units. + * Connection loggin added, either via it's own logfile + * or syslog. + * Modified 'defaults' to 'parameters'. + * + * 1.9 Jun 1996 Reworked config file parsing to use port names instead + * of callsigns. Reformated source code. + * + * Under alpha: + * BPQ like clever mode called for.... also a mode that + * requires a packet to kick application open. + * A flag/mode which will cause a call to initgroups() + * based on uid. + * Callsign validation check. + * Logging of errors. + * + * + * TODO: + * The timing of the 'accept()' might be changed, defered to the + * child, then that child fork() itself, to stop race conditions + * around 'accept()'. + * Add a config file to allow/disallow connections/services at + * different times of the day to restrict access say. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "../pathnames.h" + +/* Maximum number of command line arguments for the application we run */ +#define MAX_ARGS 32 + +#define FLAG_VALIDCALL 0x01 /* NOTUSED */ +#define FLAG_NOLOGGING 0x02 /* Don't log this connection */ +#define FLAG_CHKNRN 0x04 /* Check NetRom Neighbour - NOTUSED */ +#define FLAG_NODIGIS 0x08 /* Disallow digipeated uplinks */ +#define FLAG_LOCKOUT 0x10 /* Disallow connection */ + + +struct axlist { /* Have used same struct for quickness */ + struct axlist *next; /* Port list */ + + char *port; /* Port call, only set across the port list */ + int fd; /* The listening socket fd */ + + int af_type; /* AF_AX25, AF_NETROM or AF_ROSE port */ + + struct axlist *ents; /* Exec line entries */ + char *call; /* Call in listing entries */ + char *node; /* Node call in listing entries */ + uid_t uid; /* UID to run program as */ + gid_t gid; /* GID to run program as */ + char *exec; /* Real exec */ + char *shell; /* Command line. With possible escapes. */ + + unsigned int window; /* Set window to... */ + unsigned long t1; /* Set T1 to... (Retrans timer) */ + unsigned long t2; /* Set T2 to... (Ack delay) */ + unsigned long t3; /* Set T3 to... (Idle Poll timer) */ + unsigned long idle; /* Set T4 to... (Link Drop timer) */ + unsigned long n2; /* Set N2 to... (Retries) */ + unsigned long flags; /* FLAG_ values ORed... */ +}; + +static struct axlist *AXL = NULL; +static char *ConfigFile = CONF_AX25D_FILE; +static char User[10]; /* Room for 'GB9ZZZ-15\0' */ +static char Node[11]; /* Room for 'GB9ZZZ-15\0' */ +static char *Port; +static sig_atomic_t Update = TRUE; /* Cause update on bootup */ +static int Logging = FALSE; + +static void SignalHUP(int); +static void SignalTERM(int); +static void WorkoutArgs(int, char *, int *, char **); +static void SetupOptions(int, struct axlist *); +static int ReadConfig(void); +static unsigned long ParseFlags(const char *, int); +static struct axlist *ClearList(struct axlist *); +static char *stripssid(const char *); + + +int main(int argc, char *argv[]) +{ + struct axlist *axltmp, *paxl, *raxl; + union { + struct full_sockaddr_ax25 ax25; + struct sockaddr_rose rose; + } sockaddr; + struct sigaction act, oact; + int maxfd = -1; + fd_set fdread; + int addrlen; + int cnt; + + while ((cnt = getopt(argc, argv, "c:lv")) != EOF) { + switch (cnt) { + case 'c': + ConfigFile = optarg; + break; + + case 'l': + Logging = TRUE; + break; + + case 'v': + printf("ax25d: %s\n", VERSION); + return 1; + + default: + fprintf(stderr, "Usage: ax25d [-v] [-c altfile] [-l]\n"); + return 1; + } + } + + if (ax25_config_load_ports() == 0) { + fprintf(stderr, "ax25d: no AX.25 port data configured\n"); + return 1; + } + + nr_config_load_ports(); + + rs_config_load_ports(); + + if (!daemon_start(TRUE)) { + fprintf(stderr, "ax25d: cannot become a daemon\n"); + return 1; + } + + if (Logging) { + openlog("ax25d", LOG_PID, LOG_DAEMON); + syslog(LOG_INFO, "starting"); + } + + act.sa_handler = SignalHUP; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGHUP, &act, &oact); + + act.sa_handler = SignalTERM; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGTERM, &act, &oact); + + for (;;) { + if (Update) { + if (ReadConfig() < 0) { + if (AXL == NULL) { + if (Logging) + syslog(LOG_ERR, "config file reload error, exiting"); + return 1; + } else { + if (Logging) + syslog(LOG_INFO, "config file reload error, continuing with original"); + } + } else { + if (Logging) + syslog(LOG_INFO, "new config file loaded successfuly"); + } + + Update = FALSE; + } + + FD_ZERO(&fdread); + + for (paxl = AXL; paxl != NULL && paxl->fd >= 0; paxl = paxl->next) { + FD_SET(paxl->fd, &fdread); + + if (paxl->fd > maxfd) + maxfd = paxl->fd; + } + + if (select(maxfd + 1, &fdread, NULL, NULL, NULL) <= 0) + continue; + + for (paxl = AXL; paxl != NULL; paxl = paxl->next) { + if (FD_ISSET(paxl->fd, &fdread)) { + pid_t pid; + gid_t grps[2]; + char *argv[MAX_ARGS]; + int argc; + int new; + int i; + + /* + * Setting up a non-blocking accept() so is does not hang up + * - I am not sure at this time why I didn't/don't assign + * the socket non-blocking to start with. + */ + /* + * We really need to setup the netrom window option here so + * that it's negotiated correctly on accepting the connection. + */ + /* + * It would be very useful if recvmsg/sendmsg were supported + * then we can move the call checking up here. + */ + i = TRUE; + ioctl(paxl->fd, FIONBIO, &i); + + addrlen = sizeof(struct full_sockaddr_ax25); + new = accept(paxl->fd, (struct sockaddr *)&sockaddr, &addrlen); + + i = FALSE; + ioctl(paxl->fd, FIONBIO, &i); + + if (new < 0) { + if (errno == EWOULDBLOCK) + continue; /* It's gone ??? */ + + if (Logging) + syslog(LOG_ERR, "accept error %m, closing socket on port %s", paxl->port); + close(paxl->fd); + paxl->fd = -1; + continue; + } + + switch (paxl->af_type) { + case AF_AX25: + strcpy(User, ax25_ntoa(&sockaddr.ax25.fsa_ax25.sax25_call)); + strcpy(Node, ""); + break; + case AF_NETROM: + strcpy(User, ax25_ntoa(&sockaddr.ax25.fsa_ax25.sax25_call)); + strcpy(Node, ax25_ntoa(&sockaddr.ax25.fsa_digipeater[0])); + break; + case AF_ROSE: + strcpy(User, ax25_ntoa(&sockaddr.rose.srose_call)); + strcpy(Node, rose_ntoa(&sockaddr.rose.srose_addr)); + break; + } + + for (raxl = paxl->ents; raxl != NULL; raxl = raxl->ents) { + if (paxl->af_type == AF_NETROM && raxl->node != NULL && Node[0] != '\0') { + if (strchr(raxl->node, '-') == NULL) { + if (strcasecmp(raxl->node, stripssid(Node)) != 0) + continue; /* Found no match (for any SSID) */ + } else { + if (strcasecmp(raxl->node, Node) != 0) + continue; /* Found no match */ + } + } + + if (raxl->call == NULL) /* default */ + break; + + if (strchr(raxl->call, '-') == NULL) { + if (strcasecmp(raxl->call, stripssid(User)) == 0) + break; /* Found a match (for any SSID) */ + } else { + if (strcasecmp(raxl->call, User) == 0) + break; /* Found a match */ + } + } + + addrlen = sizeof(struct full_sockaddr_ax25); + getsockname(new, (struct sockaddr *)&sockaddr, &addrlen); + + switch (paxl->af_type) { + case AF_AX25: + Port = ax25_config_get_port(&sockaddr.ax25.fsa_digipeater[0]); + break; + case AF_NETROM: + Port = nr_config_get_port(&sockaddr.ax25.fsa_ax25.sax25_call); + break; + case AF_ROSE: + Port = rs_config_get_port(&sockaddr.rose.srose_addr); + break; + default: + Port = "???"; + break; + } + + if (raxl == NULL) { + /* No default */ + if (Logging && !(paxl->flags & FLAG_NOLOGGING)) { + switch (paxl->af_type) { + case AF_AX25: + syslog(LOG_INFO, "AX.25 %s (%s) rejected - no default", User, Port); + break; + case AF_NETROM: + syslog(LOG_INFO, "NET/ROM %s@%s (%s) rejected - no default", User, Node, Port); + break; + case AF_ROSE: + syslog(LOG_INFO, "Rose %s@%s (%s) rejected - no default", User, Node, Port); + break; + } + } + close(new); + continue; + } + + if (raxl->flags & FLAG_LOCKOUT) { + /* Not allowed to connect */ + if (Logging && !(paxl->flags & FLAG_NOLOGGING)) { + switch (raxl->af_type) { + case AF_AX25: + syslog(LOG_INFO, "AX.25 %s (%s) rejected - port locked", User, Port); + break; + case AF_NETROM: + syslog(LOG_INFO, "NET/ROM %s@%s (%s) rejected - port locked", User, Node, Port); + break; + case AF_ROSE: + syslog(LOG_INFO, "Rose %s@%s (%s) rejected - port locked", User, Node, Port); + break; + } + } + close(new); + continue; + } + + if (raxl->af_type == AF_AX25 && (raxl->flags & FLAG_NODIGIS) && sockaddr.ax25.fsa_ax25.sax25_ndigis != 0) { + /* Not allowed to uplink via digi's */ + if (Logging && !(paxl->flags & FLAG_NOLOGGING)) + syslog(LOG_INFO, "AX.25 %s (%s) rejected - digipeaters", User, Port); + close(new); + continue; + } + + pid = fork(); + + switch (pid) { + case -1: + if (Logging) + syslog(LOG_ERR, "fork error %m"); + /* + * I don't think AX25 at the moment will hold the + * connection open, if the above does not make it + * through first time. + */ + close(new); + break; /* Oh well... */ + + case 0: + SetupOptions(new, raxl); + WorkoutArgs(raxl->af_type, raxl->shell, &argc, argv); + + if (Logging && !(paxl->flags & FLAG_NOLOGGING)) { + switch (paxl->af_type) { + case AF_AX25: + syslog(LOG_INFO, "AX.25 %s (%s) %s", User, Port, argv[0]); + break; + case AF_NETROM: + syslog(LOG_INFO, "NET/ROM %s@%s (%s) %s", User, Node, Port, argv[0]); + break; + case AF_ROSE: + syslog(LOG_INFO, "Rose %s@%s (%s) %s", User, Node, Port, argv[0]); + break; + } + } + + dup2(new, STDIN_FILENO); + dup2(new, STDOUT_FILENO); + close(new); + + /* + * Might be more efficient if we just went down AXL, + * we cleaned up our parents left overs on bootup. + */ + for (axltmp = AXL; axltmp != NULL; axltmp = axltmp->next) + close(axltmp->fd); + + if (Logging) + closelog(); + + /* Make root secure, before we exec() */ + setgroups(0, grps); /* Strip any supplementary gid's */ + setgid(raxl->gid); + setuid(raxl->uid); + execve(raxl->exec, argv, NULL); + return 1; + + default: + close(new); + break; + } + } + } + } + + /* NOT REACHED */ + return 0; +} + +static void SignalHUP(int code) +{ + Update = TRUE; /* Schedule an update */ +} + +static void SignalTERM(int code) +{ + if (Logging) { + syslog(LOG_INFO, "terminating on SIGTERM\n"); + closelog(); + } + + exit(0); +} + +static void WorkoutArgs(int af_type, char *shell, int *argc, char **argv) +{ + char buffer[1024]; /* Maximum arg size */ + char *sp, *cp; + int cnt = 0; + int args = 0; + + for (cp = shell; *cp != '\0'; cp++) { + if (isspace(*cp) && cnt != 0) { + buffer[cnt] = '\0'; + argv[args++] = strdup(buffer); + cnt = 0; + + if (args == MAX_ARGS - 1) { + argv[args] = NULL; + *argc = args; + return; + } + + continue; + } else if (isspace(*cp)) /* && !cnt */ + continue; + + if (*cp == '%') { + cp++; + + switch(*cp) { + case 'd': /* portname */ + for (sp = Port; *sp != '\0' && *sp != '-'; sp++) + buffer[cnt++] = *sp; + break; + + case 'U': /* username in UPPER */ + for (sp = User; *sp != '\0' && *sp != '-'; sp++) + buffer[cnt++] = toupper(*sp); + break; + + case 'u': /* USERNAME IN lower */ + for (sp = User; *sp != '\0' && *sp != '-'; sp++) + buffer[cnt++] = tolower(*sp); + break; + + case 'S': /* username in UPPER (with SSID) */ + for (sp = User; *sp != '\0'; sp++) + buffer[cnt++] = toupper(*sp); + break; + + case 's': /* USERNAME IN lower (with SSID) */ + for (sp = User; *sp != '\0'; sp++) + buffer[cnt++] = tolower(*sp); + break; + + case 'P': /* nodename in UPPER */ + if (af_type == AF_NETROM) { + for (sp = Node; *sp != '\0' && *sp != '-'; sp++) + buffer[cnt++] = toupper(*sp); + } else { + buffer[cnt++] = '%'; + } + break; + + case 'p': /* NODENAME IN lower */ + if (af_type == AF_NETROM) { + for(sp = Node; *sp != '\0' && *sp != '-'; sp++) + buffer[cnt++] = tolower(*sp); + } else { + buffer[cnt++] = '%'; + } + break; + + case 'R': /* nodename in UPPER (with SSID) */ + if (af_type == AF_NETROM) { + for (sp = Node; *sp != '\0'; sp++) + buffer[cnt++] = toupper(*sp); + } else { + buffer[cnt++] = '%'; + } + break; + + case 'r': /* NODENAME IN lower (with SSID) */ + if (af_type == AF_NETROM) { + for (sp = Node; *sp != '\0'; sp++) + buffer[cnt++] = tolower(*sp); + } else { + buffer[cnt++] = '%'; + } + break; + + case '\0': + case '%': + default: + buffer[cnt++] = '%'; + break; + } + } else { + buffer[cnt++] = *cp; + } + } + + if (cnt != 0) { + buffer[cnt] = '\0'; + argv[args++] = strdup(buffer); + } + + argv[args] = NULL; + *argc = args; +} + +static void SetupOptions(int new, struct axlist *axl) +{ + switch (axl->af_type) { + case AF_AX25: + if (axl->window != 0) + setsockopt(new, SOL_AX25, AX25_WINDOW, &axl->window, sizeof(axl->window)); + if (axl->t1 != 0) + setsockopt(new, SOL_AX25, AX25_T1, &axl->t1, sizeof(axl->t1)); + if (axl->n2 != 0) + setsockopt(new, SOL_AX25, AX25_N2, &axl->n2, sizeof(axl->n2)); + if (axl->t2 != 0) + setsockopt(new, SOL_AX25, AX25_T2, &axl->t2, sizeof(axl->t2)); + if (axl->t3 != 0) + setsockopt(new, SOL_AX25, AX25_T3, &axl->t3, sizeof(axl->t3)); + if (axl->idle != 0) + setsockopt(new, SOL_AX25, AX25_IDLE, &axl->idle, sizeof(axl->idle)); + break; + case AF_NETROM: + if (axl->t1 != 0) + setsockopt(new, SOL_NETROM, NETROM_T1, &axl->t1, sizeof(axl->t1)); + if (axl->n2 != 0) + setsockopt(new, SOL_NETROM, NETROM_N2, &axl->n2, sizeof(axl->n2)); + if (axl->t2 != 0) + setsockopt(new, SOL_NETROM, NETROM_T2, &axl->t2, sizeof(axl->t2)); + break; + case AF_ROSE: + if (axl->idle != 0) + setsockopt(new, SOL_ROSE, ROSE_IDLE, &axl->idle, sizeof(axl->idle)); + break; + } +} + +/**************************** CONFIGURATION STUFF ***************************/ + +static int ReadConfig(void) +{ + struct axlist axl_defaults; + struct axlist *axl_build = NULL; + struct axlist *axl_port = NULL; + struct axlist *axl_ent, *axltmp; + union { + struct full_sockaddr_ax25 ax25; + struct sockaddr_rose rose; + } sockaddr; + struct passwd *pwd; + FILE *fp; + char buffer[2048]; + char *s, *port, *call, *node, *addr = NULL; + unsigned long val; + int addrlen; + int af_type = 0; /* Keep GCC happy */ + int line = 0; + int hunt = TRUE, error = FALSE; + int iamdigi = FALSE, yes; + int parameters = 0; + + memset(&axl_defaults, 0, sizeof(axl_defaults)); + + if ((fp = fopen(ConfigFile, "r")) == NULL) + return -1; + + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + line++; + + if ((s = strchr(buffer, '\n')) != NULL) + *s = '\0'; + if ((s = strchr(buffer, '\r')) != NULL) + *s = '\0'; + + if (buffer[0] == '#') + continue; + + switch (buffer[0]) { + case '[': /* AX25 port call */ + af_type = AF_AX25; + hunt = TRUE; + error = FALSE; + iamdigi = FALSE; + break; + + case '<': /* NETROM iface call */ + af_type = AF_NETROM; + hunt = TRUE; + error = FALSE; + iamdigi = FALSE; + break; + + case '{': /* ROSE iface call */ + af_type = AF_ROSE; + hunt = TRUE; + error = FALSE; + iamdigi = FALSE; + break; + + default: + if (hunt && !error) + goto BadLine; + break; + } + + if (hunt) { /* We've found a Iface entry */ + /* Reset 'defaults' entry on the interface */ + memset(&axl_defaults, 0, sizeof(axl_defaults)); + + switch (af_type) { + case AF_AX25: + if ((s = strchr(buffer, ']')) == NULL) + goto BadLine; + *s = '\0'; + if ((s = strtok(buffer + 1, " \t")) == NULL) + goto BadLine; + port = s; + call = NULL; + if ((s = strtok(NULL, " \t")) != NULL) { + if (strcasecmp(s, "VIA") == 0 || strcasecmp(s, "V") == 0) { + if ((s = strtok(NULL, " \t")) == NULL) + goto BadLine; + } + + call = port; + port = s; + + if ((s = strchr(call, '*')) != NULL) { + iamdigi = TRUE; + *s = '\0'; + } + } + if (strcmp(port, "*") == 0 && call == NULL) { + fprintf(stderr, "ax25d: invalid port name\n"); + continue; + } + if (strcmp(port, "*") != 0) { + if ((addr = ax25_config_get_addr(port)) == NULL) { + fprintf(stderr, "ax25d: invalid AX.25 port '%s'\n", port); + continue; + } + } + if (call == NULL) { + sockaddr.ax25.fsa_ax25.sax25_family = AF_AX25; + sockaddr.ax25.fsa_ax25.sax25_ndigis = 0; + ax25_aton_entry(addr, sockaddr.ax25.fsa_ax25.sax25_call.ax25_call); + } else { + sockaddr.ax25.fsa_ax25.sax25_family = AF_AX25; + sockaddr.ax25.fsa_ax25.sax25_ndigis = 1; + ax25_aton_entry(call, sockaddr.ax25.fsa_ax25.sax25_call.ax25_call); + if (strcmp(port, "*") != 0) + ax25_aton_entry(addr, sockaddr.ax25.fsa_digipeater[0].ax25_call); + else + sockaddr.ax25.fsa_digipeater[0] = null_ax25_address; + } + addrlen = sizeof(struct full_sockaddr_ax25); + break; + + case AF_NETROM: + if ((s = strchr(buffer, '>')) == NULL) + goto BadLine; + *s = '\0'; + port = buffer + 1; + if ((addr = nr_config_get_addr(port)) == NULL) { + fprintf(stderr, "ax25d: invalid NET/ROM port '%s'\n", port); + continue; + } + sockaddr.ax25.fsa_ax25.sax25_family = AF_NETROM; + sockaddr.ax25.fsa_ax25.sax25_ndigis = 0; + ax25_aton_entry(addr, sockaddr.ax25.fsa_ax25.sax25_call.ax25_call); + addrlen = sizeof(struct full_sockaddr_ax25); + break; + + case AF_ROSE: + if ((s = strchr(buffer, '}')) == NULL) + goto BadLine; + *s = '\0'; + if ((s = strtok(buffer + 1, " \t")) == NULL) + goto BadLine; + call = s; + if ((s = strtok(NULL, " \t")) == NULL) + goto BadLine; + if (strcasecmp(s, "VIA") == 0 || strcasecmp(s, "V") == 0) { + if ((s = strtok(NULL, " \t")) == NULL) + goto BadLine; + } + port = s; + if ((addr = rs_config_get_addr(port)) == NULL) { + fprintf(stderr, "ax25d: invalid Rose port '%s'\n", port); + continue; + } + if (strcmp(call, "*") == 0) { + sockaddr.rose.srose_family = AF_ROSE; + sockaddr.rose.srose_ndigis = 0; + rose_aton(addr, sockaddr.rose.srose_addr.rose_addr); + sockaddr.rose.srose_call = null_ax25_address; + } else { + sockaddr.rose.srose_family = AF_ROSE; + sockaddr.rose.srose_ndigis = 0; + rose_aton(addr, sockaddr.rose.srose_addr.rose_addr); + ax25_aton_entry(call, sockaddr.rose.srose_call.ax25_call); + } + addrlen = sizeof(struct sockaddr_rose); + break; + + default: + fprintf(stderr, "ax25d: unknown af_type=%d\n", af_type); + exit(1); + } + + if ((axl_port = calloc(1, sizeof(*axl_port))) == NULL) { + fprintf(stderr, "ax25d: out of memory\n"); + goto Error; + } + + axl_port->port = strdup(port); + axl_port->af_type = af_type; + + if ((axl_port->fd = socket(axl_port->af_type, SOCK_SEQPACKET, 0)) < 0) { + fprintf(stderr, "ax25d: socket: %s\n", strerror(errno)); + free(axl_port->port); + free(axl_port); + error = TRUE; + continue; + } + /* xlz - have to nuke this as this option is gone + * what should be here? + if (iamdigi) { + yes = 1; + setsockopt(axl_port->fd, SOL_AX25, AX25_IAMDIGI, &yes, sizeof(yes)); + } + */ + + if (bind(axl_port->fd, (struct sockaddr *)&sockaddr, addrlen) < 0) { + fprintf(stderr, "ax25d: bind: %s on port %s\n", strerror(errno), axl_port->port); + close(axl_port->fd); + free(axl_port->port); + free(axl_port); + error = TRUE; + continue; + } + + if (listen(axl_port->fd, SOMAXCONN) < 0) { + fprintf(stderr, "ax25d: listen: %s\n", strerror(errno)); + close(axl_port->fd); + free(axl_port->port); + free(axl_port); + error = TRUE; + continue; + } + + /* Add it to the head of the list we are building */ + if (axl_build == NULL) { + axl_build = axl_port; + } else { + for (axltmp = axl_build; axltmp->next != NULL; axltmp = axltmp->next); + axltmp->next = axl_port; + } + + hunt = FALSE; /* Next lines will be entries */ + } else { /* This is an entry */ + if ((axl_ent = calloc(1, sizeof(*axl_ent))) == NULL) { + fprintf(stderr, "ax25d: out of memory\n"); + goto Error; + } + + axl_ent->af_type = axl_port->af_type; /* Inherit this */ + + if ((call = strtok(buffer, " \t")) == NULL) { + free(axl_ent); + continue; + } + + strupr(call); + + if (axl_ent->af_type == AF_NETROM) { + if ((s = strchr(call, '@')) != NULL) { + node = s + 1; + *s = '\0'; + + if (*node == '\0') { + free(axl_ent); + continue; + } + + axl_ent->node = strdup(node); + + if (*call == '\0') + call = "default"; /* @NODE means default@NODE */ + } + } + + parameters = FALSE; + + if (strcasecmp("parameters", call) == 0) + parameters = TRUE; + else if (strcasecmp("default", call) != 0) + axl_ent->call = strdup(call); + + /* Window */ + if ((s = strtok(NULL, " \t")) == NULL) + goto BadArgsFree; + + if (!parameters) { + if (strcmp(s, "*") != 0) + axl_ent->window = atoi(s); + else + axl_ent->window = axl_defaults.window; + } else { + if (strcmp(s, "*") != 0) + axl_defaults.window = atoi(s); + } + + /* T1 */ + if ((s = strtok(NULL, " \t")) == NULL) + goto BadArgsFree; + + if (!parameters) { + if (strcmp(s, "*") != 0) { + val = (unsigned long)(atof(s) / 0.1); + + if (val == 0 || val > 65535) + axl_ent->t1 = axl_defaults.t1; + else + axl_ent->t1 = val; + } else { + axl_ent->t1 = axl_defaults.t1; + } + } else { + if (strcmp(s, "*") != 0) { + val = (unsigned long)(atof(s) / 0.1); + + if (val > 0 && val < 65535) + axl_defaults.t1 = val; + } + } + + /* T2 */ + if ((s = strtok(NULL, " \t")) == NULL) + goto BadArgsFree; + + if (!parameters) { + if (strcmp(s, "*") != 0) { + val = (unsigned long)(atof(s) / 0.1); + + if (val == 0 || val > 65535) + axl_ent->t2 = axl_defaults.t2; + else + axl_ent->t2 = val; + } else { + axl_ent->t2 = axl_defaults.t2; + } + } else { + if (strcmp(s, "*") != 0) { + val = (unsigned long)(atof(s) / 0.1); + + if (val > 0 && val < 65535) + axl_defaults.t2 = val; + } + } + + /* T3 */ + if ((s = strtok(NULL, " \t")) == NULL) + goto BadArgsFree; + + if (!parameters) { + if (strcmp(s, "*") != 0) + axl_ent->t3 = atoi(s); + else + axl_ent->t3 = axl_defaults.t3; + } else { + if (strcmp(s, "*") != 0) + axl_defaults.t3 = atoi(s); + } + + /* Idle */ + if ((s = strtok(NULL, " \t")) == NULL) + goto BadArgsFree; + + if (!parameters) { + if (strcmp(s, "*") != 0) + axl_ent->idle = atoi(s); + else + axl_ent->idle = axl_defaults.idle; + } else { + if (strcmp(s, "*") != 0) + axl_defaults.idle = atoi(s); + } + + /* N2 */ + if ((s = strtok(NULL, " \t")) == NULL) + goto BadArgsFree; + + if (!parameters) { + if (strcmp(s, "*") != 0) + axl_ent->n2 = atoi(s); + else + axl_ent->n2 = axl_defaults.n2; + } else { + if (strcmp(s, "*") != 0) + axl_defaults.n2 = atoi(s); + } + + if (!parameters) { + /* Flags */ + if ((s = strtok(NULL, " \t")) == NULL) + goto BadArgsFree; + + axl_ent->flags = ParseFlags(s, line); + + if (!(axl_ent->flags & FLAG_LOCKOUT)) { + /* Get username */ + if ((s = strtok(NULL, " \t")) == NULL) + goto BadArgsFree; + + if ((pwd = getpwnam(s)) == NULL) { + fprintf(stderr, "ax25d: UID for user '%s' is unknown, ignoring entry\n", s); + goto BadUID; + } + + axl_ent->uid = pwd->pw_uid; + axl_ent->gid = pwd->pw_gid; + + /* Get exec file */ + if ((s = strtok(NULL, " \t")) == NULL) + goto BadArgsFree; + + axl_ent->exec = strdup(s); + + /* Get command line */ + if ((s = strtok(NULL, "")) == NULL) + goto BadArgsFree2; + + axl_ent->shell = strdup(s); + } + + axl_ent->next = NULL; + + if (axl_port->ents == NULL) { + axl_port->ents = axl_ent; + } else { + for (axltmp = axl_port->ents; axltmp->ents != NULL; axltmp = axltmp->ents) + ; + axltmp->ents = axl_ent; + } + } + } + + continue; + +BadLine: + fprintf(stderr, "ax25d: bad config entry on line %d\n", line); + continue; + +BadUID: + if (axl_ent->call != NULL) + free(axl_ent->call); + free(axl_ent); + continue; + +BadArgsFree2: + if (axl_ent->exec != NULL) + free(axl_ent->exec); +BadArgsFree: + if (axl_ent->call != NULL) + free(axl_ent->call); + free(axl_ent); + + /* BadArgs: */ + fprintf(stderr, "ax25d: bad config entry on line %d, not enough fields.\n", line); + continue; + } + + fclose(fp); + + AXL = ClearList(AXL); + AXL = axl_build; /* Assign our built list to AXL */ + + return 0; + +Error: + axl_build = ClearList(axl_build); + return -1; +} + +static unsigned long ParseFlags(const char *kp, int line) +{ + unsigned long flags = 0UL; + + for (; *kp != '\0'; kp++) { + switch (*kp) { + case 'v': + case 'V': + flags |= FLAG_VALIDCALL; + break; + + case 'q': + case 'Q': + flags |= FLAG_NOLOGGING; + break; + + case 'n': + case 'N': + flags |= FLAG_CHKNRN; + break; + + case 'd': + case 'D': + flags |= FLAG_NODIGIS; + break; + + case 'l': + case 'L': + flags |= FLAG_LOCKOUT; + break; + + case '0': + case '*': + case '-': /* Be compatible and allow markers */ + break; + + default: + fprintf(stderr, "ax25d: config file line %d bad flag option '%c'.\n", line, *kp); + break; + } + } + + return flags; +} + +static struct axlist *ClearList(struct axlist *list) +{ + struct axlist *tp1, *tp2, *tmp; + + for (tp1 = list; tp1 != NULL; ) { + for (tp2 = tp1->ents; tp2 != NULL; ) { + if (tp2->port != NULL) + free(tp2->port); + if (tp2->call != NULL) + free(tp2->call); + if (tp2->node != NULL) + free(tp2->node); + if (tp2->exec != NULL) + free(tp2->exec); + if (tp2->shell != NULL) + free(tp2->shell); + + tmp = tp2->ents; + free(tp2); + tp2 = tmp; + } + + if (tp1->port != NULL) + free(tp1->port); + if (tp1->call != NULL) + free(tp1->call); + if (tp1->node != NULL) + free(tp1->node); + if (tp1->exec != NULL) + free(tp1->exec); + if (tp1->shell != NULL) + free(tp1->shell); + + close(tp1->fd); + + tmp = tp1->next; + free(tp1); + tp1 = tmp; + } + + return NULL; +} + +static char *stripssid(const char *call) +{ + static char newcall[10]; + char *s; + + strcpy(newcall, call); + + if ((s = strchr(newcall, '-')) != NULL) + *s = '\0'; + + return newcall; +} -- cgit v1.2.3