/* * ax25mond.c - Wrapper for offering AX.25 traffic to non-root processes * * Copyright (C) 1998-1999 Johann Hanne, DH3MB. All rights reserved. * * 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., 51 Franklin Street - Fifth Floor, Boston, MA * 02110-1301, USA. */ /*--------------------------------------------------------------------------*/ #define CONFFILE "/etc/ax25/ax25mond.conf" #define MAX_SOCKETS 5 #define MAX_CONNECTS 50 /*--------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For older kernels */ #ifndef PF_PACKET #define PF_PACKET PF_INET #endif /*--------------------------------------------------------------------------*/ static union { struct sockaddr sa; struct sockaddr_in si; struct sockaddr_un su; } addr; /*--------------------------------------------------------------------------*/ static int sock_list[MAX_SOCKETS]; static char sock_monmode[MAX_SOCKETS]; static char sock_filename[MAX_SOCKETS][100]; static int sock_num = 0; static int conn_list[MAX_CONNECTS]; static struct sockaddr conn_addr[MAX_CONNECTS]; static socklen_t conn_addrlen[MAX_CONNECTS]; static char conn_monmode[MAX_CONNECTS]; static int conn_num = 0; static int highest_sock_fd; static int end = 0; /*--------------------------------------------------------------------------*/ /* from buildsaddr.c */ static struct sockaddr *build_sockaddr(const char *name, int *addrlen) { char *host_name; char *serv_name; char buf[1024]; memset(&addr, 0, sizeof(addr)); *addrlen = 0; host_name = strcpy(buf, name); serv_name = strchr(buf, ':'); if (!serv_name) return NULL; *serv_name++ = 0; if (!*host_name || !*serv_name) return NULL; if (!strcmp(host_name, "local") || !strcmp(host_name, "unix")) { addr.su.sun_family = AF_UNIX; *addr.su.sun_path = 0; strcat(addr.su.sun_path, serv_name); *addrlen = sizeof(struct sockaddr_un); return &addr.sa; } addr.si.sin_family = AF_INET; if (!strcmp(host_name, "*")) { addr.si.sin_addr.s_addr = INADDR_ANY; } else if (!strcmp(host_name, "loopback")) { addr.si.sin_addr.s_addr = inet_addr("127.0.0.1"); } else {addr.si.sin_addr.s_addr = inet_addr(host_name); if (addr.si.sin_addr.s_addr == -1) { struct hostent *hp = gethostbyname(host_name); endhostent(); if (!hp) return NULL; addr.si.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr))->s_addr; } } if (isdigit(*serv_name & 0xff)) { addr.si.sin_port = htons(atoi(serv_name)); } else { struct servent *sp = getservbyname(serv_name, NULL); endservent(); if (!sp) return NULL; addr.si.sin_port = sp->s_port; } *addrlen = sizeof(struct sockaddr_in); return &addr.sa; } /*--------------------------------------------------------------------------*/ static void add_socket(char *sockname, char monmode) { struct sockaddr *saddr; int saddrlen; if (sock_num == MAX_SOCKETS - 1) { fprintf(stderr, "WARNING: Too many sockets defined - only %d are " "allowed\n", MAX_SOCKETS); return; } if (!(saddr = build_sockaddr(sockname, &saddrlen))) { fprintf(stderr, "WARNING: Invalid socket name: \"%s\"\n", sockname); return; } if (saddr->sa_family == AF_UNIX) strcpy(sock_filename[sock_num], strchr(sockname, ':') + 1); else sock_filename[sock_num][0] = 0; sock_list[sock_num] = socket(saddr->sa_family, SOCK_STREAM, 0); if (sock_list[sock_num] < 0) { fprintf(stderr, "WARNING: Error opening socket \"%s\": %s\n", sockname, strerror(errno)); return; } if (bind(sock_list[sock_num], saddr, saddrlen) < 0) { fprintf(stderr, "WARNING: Error binding socket \"%s\": %s\n", sockname, strerror(errno)); return; } if (listen(sock_list[sock_num], 5) < 0) { fprintf(stderr, "WARNING: Error listening on socket \"%s\": %s\n", sockname, strerror(errno)); return; } fcntl(sock_list[sock_num], F_SETFL, O_NONBLOCK); if (sock_list[sock_num] > highest_sock_fd) highest_sock_fd = sock_list[sock_num]; sock_monmode[sock_num] = monmode; sock_num++; } /*--------------------------------------------------------------------------*/ static void close_sockets(void) { int i; for (i = 0; i < sock_num; i++) { close(sock_list[i]); if (sock_filename[i][0]) unlink(sock_filename[i]); } } /*--------------------------------------------------------------------------*/ static void quit_handler(int dummy) { end = 1; } /*--------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { fd_set monavail; int monrx_fd, monrxtx_fd; struct sockaddr monfrom; socklen_t monfromlen; fd_set conn_request; struct timeval tv; char buf[500]; int size; int i; struct ifreq ifr; FILE *conffile; char confline[100]; char *mode; char *sockname; if (argc > 1) { if (argc == 2 && (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0)) { printf("%s: Version " VERSION "\n\n", argv[0]); printf ("Copyright (C) 1998-1999 Johann Hanne, DH3MB. All rights reserved.\n" "This program is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation; either version 2 of the License, or\n" "(at your option) any later version.\n\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); exit(1); } else { printf("Usage: %s [-v|--version]\n", argv[0]); exit(0); } } /* At first, read the configuration file */ if (!(conffile = fopen(CONFFILE, "r"))) { fprintf(stderr, "Unable to open " CONFFILE ".\n"); exit(1); } while (fgets(confline, 100, conffile)) { if (confline[0] == '#') /* Comment */ continue; confline[strlen(confline) - 1] = 0; /* Cut the LF */ if (!(sockname = strchr(confline, ' '))) { fprintf(stderr, "WARNING: The following configuration line includes " "one or more errors:\n%s\n", confline); continue; } *(sockname++) = 0; mode = confline; if (strcasecmp(mode, "RX") == 0) add_socket(sockname, 0); else if (strcasecmp(mode, "RXTX") == 0) add_socket(sockname, 1); else fprintf(stderr, "WARNING: Mode \"%s\" not supported\n", mode); } fclose(conffile); if (sock_num == 0) { fprintf(stderr, "FATAL: No usable socket found\n"); exit(1); } /* Fork into background */ if (fork()) exit(0); /* Close stdout, stderr and stdin */ fclose(stdout); fclose(stderr); fclose(stdin); /* Set some signal handlers */ signal(SIGPIPE, SIG_IGN); signal(SIGTERM, quit_handler); openlog("ax25mond", LOG_PID, LOG_DAEMON); /* Open AX.25 socket for monitoring RX traffic only */ if ((monrx_fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_AX25))) < 0) { syslog(LOG_ERR, "Error opening monitor socket: %s\n", strerror(errno)); exit(1); } fcntl(monrx_fd, F_SETFL, O_NONBLOCK); /* Open AX.25 socket for monitoring RX and TX traffic */ if ((monrxtx_fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ALL))) < 0) { syslog(LOG_ERR, "Error opening monitor socket: %s\n", strerror(errno)); exit(1); } fcntl(monrxtx_fd, F_SETFL, O_NONBLOCK); while (!end) { /* Look for incoming connects on all open sockets */ FD_ZERO(&conn_request); for (i = 0; i < sock_num; i++) FD_SET(sock_list[i], &conn_request); tv.tv_sec = 0; tv.tv_usec = 0; select(highest_sock_fd + 1, &conn_request, NULL, NULL, &tv); for (i = 0; i < sock_num; i++) if (FD_ISSET(sock_list[i], &conn_request)) { conn_list[conn_num] = accept(sock_list[i], &conn_addr [conn_num], &conn_addrlen [conn_num]); conn_monmode[conn_num] = sock_monmode[i]; conn_num++; } /* Check if there is new data on the RX-only monitor socket */ FD_ZERO(&monavail); FD_SET(monrx_fd, &monavail); tv.tv_sec = 0; tv.tv_usec = 10; select(monrx_fd + 1, &monavail, NULL, NULL, &tv); if (FD_ISSET(monrx_fd, &monavail)) { monfromlen = sizeof(monfrom); size = recvfrom(monrx_fd, buf, sizeof(buf), 0, &monfrom, &monfromlen); /* Send the packet to all connected sockets */ for (i = 0; i < conn_num; i++) { if (conn_monmode[i] == 0) if (send (conn_list[i], buf, size, 0) < 0) if (errno != EAGAIN) { syslog(LOG_ERR, "Error sending monitor data: %s\n", strerror (errno)); close(conn_list [i]); conn_list[i] = conn_list [conn_num - 1]; conn_num--; } } } /* Check if there is new data on the RX+TX monitor socket */ FD_ZERO(&monavail); FD_SET(monrxtx_fd, &monavail); tv.tv_sec = 0; tv.tv_usec = 10; select(monrxtx_fd + 1, &monavail, NULL, NULL, &tv); if (FD_ISSET(monrxtx_fd, &monavail)) { monfromlen = sizeof(monfrom); size = recvfrom(monrxtx_fd, buf, sizeof(buf), 0, &monfrom, &monfromlen); /* Check, if we have received a AX.25-packet */ strcpy(ifr.ifr_name, monfrom.sa_data); ioctl(monrxtx_fd, SIOCGIFHWADDR, &ifr); if (ifr.ifr_hwaddr.sa_family == AF_AX25) /* Send the packet to all connected sockets */ for (i = 0; i < conn_num; i++) { if (conn_monmode[i] == 1) if (send (conn_list[i], buf, size, 0) < 0) if (errno != EAGAIN) { syslog (LOG_ERR, "Error sending monitor data: %s\n", strerror (errno)); close (conn_list [i]); conn_list [i] = conn_list [conn_num - 1]; conn_num--; } } } } for (i = 0; i < conn_num; i++) close(conn_list[i]); close_sockets(); close(monrx_fd); close(monrxtx_fd); closelog(); return 0; }