# HG changeset patch # User Mychaela Falconia # Date 1716838939 0 # Node ID 247f4bbde24ca86cb7ec559d02f69a0e6998c7a2 # Parent 560a3765ab30a0da2835f5ec36420b0bdd127b39 rtp-mgr: daemon ported over diff -r 560a3765ab30 -r 247f4bbde24c .hgignore --- a/.hgignore Mon May 27 18:35:39 2024 +0000 +++ b/.hgignore Mon May 27 19:42:19 2024 +0000 @@ -2,3 +2,5 @@ \.[oa]$ ^config\.defs$ + +^rtp-mgr/themwi-rtp-mgr$ diff -r 560a3765ab30 -r 247f4bbde24c rtp-mgr/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtp-mgr/Makefile Mon May 27 19:42:19 2024 +0000 @@ -0,0 +1,17 @@ +CPPFLAGS=-I../build-inc +PROG= themwi-rtp-mgr +OBJS= alloc.o ctrl_prot.o ctrl_sock.o main.o readconf.o sockinit.o + +include ../config.defs + +all: ${PROG} + +${PROG}: ${OBJS} + ${CC} -o $@ ${OBJS} + +install: + mkdir -p ${DESTDIR}${bindir} + install -c -m 755 ${PROG} ${DESTDIR}${bindir} + +clean: + rm -f *.o ${PROG} errs diff -r 560a3765ab30 -r 247f4bbde24c rtp-mgr/alloc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtp-mgr/alloc.c Mon May 27 19:42:19 2024 +0000 @@ -0,0 +1,64 @@ +/* + * In this module we implement our RTP port allocation operation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "struct.h" + +get_rtp_port_pair(roe, brc) + struct rtp_one_end *roe; + struct bind_range_cfg *brc; +{ + struct sockaddr_in sin; + unsigned tries, rtp_port; + int rc; + + sin.sin_family = AF_INET; + sin.sin_addr = brc->bind_ip; + for (tries = brc->port_tries; tries; tries--) { + rtp_port = brc->port_next; + brc->port_next += 2; + if (brc->port_next >= brc->port_range_end) + brc->port_next = brc->port_range_start; + sin.sin_port = htons(rtp_port); + roe->rtp_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (roe->rtp_fd < 0) { + syslog(LOG_CRIT, "socket(AF_INET, SOCK_DGRAM, 0): %m"); + return(-1); + } + rc = bind(roe->rtp_fd, (struct sockaddr *) &sin, sizeof sin); + if (rc < 0) { + close(roe->rtp_fd); + continue; + } + bcopy(&sin, &roe->bound_addr, sizeof(struct sockaddr_in)); + sin.sin_port = htons(rtp_port+1); + roe->rtcp_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (roe->rtcp_fd < 0) { + syslog(LOG_CRIT, "socket(AF_INET, SOCK_DGRAM, 0): %m"); + close(roe->rtp_fd); + return(-1); + } + rc = bind(roe->rtcp_fd, (struct sockaddr *) &sin, sizeof sin); + if (rc < 0) { + close(roe->rtp_fd); + close(roe->rtcp_fd); + continue; + } + /* all good! */ + return(0); + } + /* couldn't find a free port pair */ + return(-1); +} diff -r 560a3765ab30 -r 247f4bbde24c rtp-mgr/ctrl_prot.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtp-mgr/ctrl_prot.c Mon May 27 19:42:19 2024 +0000 @@ -0,0 +1,152 @@ +/* + * In this module we implement our control socket protocol. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "struct.h" +#include "select.h" + +extern struct bind_range_cfg bind_range_gsm, bind_range_pstn; + +static void +free_rtp_end(roe) + struct rtp_one_end *roe; +{ + close(roe->rtp_fd); + close(roe->rtcp_fd); +} + +static void +close_fds(fd_list, num_fd) + int *fd_list, num_fd; +{ + int i; + + for (i = 0; i < num_fd; i++) + close(fd_list[i]); +} + +void +ctrl_message_handler(fd) +{ + struct rtp_alloc_req req; + struct rtp_alloc_resp resp; + struct rtp_one_end rtp_gsm, rtp_pstn; + struct iovec iov; + struct msghdr msg; + int fd_out[4], num_fd, *fd_bufptr; + union { + char buf[CMSG_SPACE(sizeof fd_out)]; + struct cmsghdr align; + } cmsgu; + struct cmsghdr *cmsg; + int rc; + + /* receive request */ + rc = recv(fd, &req, sizeof req, 0); + if (rc < sizeof req) { + syslog(LOG_DEBUG, "ctrl connection closing"); + close(fd); + FD_CLR(fd, &select_for_read); + return; + } + /* start preparing response */ + bzero(&resp, sizeof resp); + resp.transact_ref = req.transact_ref; + switch (req.ep_type) { + case RTP_ALLOC_TYPE_GSM: + case RTP_ALLOC_TYPE_PSTN: + case RTP_ALLOC_TYPE_GSM2PSTN: + case RTP_ALLOC_TYPE_GSM2GSM: + break; + default: + resp.res = RTP_ALLOC_ERR_PARAM; +error_resp: send(fd, &resp, sizeof resp, 0); + return; + } + /* allocate resources */ + if (req.ep_type & RTP_ALLOC_DO_GSM1) { + rc = get_rtp_port_pair(&rtp_gsm, &bind_range_gsm); + if (rc < 0) { + syslog(LOG_ERR, + "unable to get local port pair on GSM side"); + resp.res = RTP_ALLOC_ERR_RSRC; + goto error_resp; + } + } + if (req.ep_type & RTP_ALLOC_DO_PSTN) { + rc = get_rtp_port_pair(&rtp_pstn, &bind_range_pstn); + if (rc < 0) { + syslog(LOG_ERR, + "unable to get local port pair on PSTN side"); + if (req.ep_type & RTP_ALLOC_DO_GSM1) + free_rtp_end(&rtp_gsm); + resp.res = RTP_ALLOC_ERR_RSRC; + goto error_resp; + } + } + if (req.ep_type & RTP_ALLOC_DO_GSM2) { + rc = get_rtp_port_pair(&rtp_pstn, &bind_range_gsm); + if (rc < 0) { + syslog(LOG_ERR, + "unable to get 2nd local port pair on GSM side"); + free_rtp_end(&rtp_gsm); + resp.res = RTP_ALLOC_ERR_RSRC; + goto error_resp; + } + } + /* finish ordinary body of response */ + resp.res = RTP_ALLOC_OK; + if (req.ep_type & RTP_ALLOC_DO_GSM1) + bcopy(&rtp_gsm.bound_addr, &resp.gsm_addr, + sizeof(struct sockaddr_in)); + if (req.ep_type & (RTP_ALLOC_DO_PSTN | RTP_ALLOC_DO_GSM2)) + bcopy(&rtp_pstn.bound_addr, &resp.pstn_addr, + sizeof(struct sockaddr_in)); + iov.iov_base = &resp; + iov.iov_len = sizeof resp; + /* file descriptor passing voodoo */ + switch (req.ep_type) { + case RTP_ALLOC_TYPE_GSM: + num_fd = 2; + fd_out[0] = rtp_gsm.rtp_fd; + fd_out[1] = rtp_gsm.rtcp_fd; + break; + case RTP_ALLOC_TYPE_PSTN: + num_fd = 2; + fd_out[0] = rtp_pstn.rtp_fd; + fd_out[1] = rtp_pstn.rtcp_fd; + break; + case RTP_ALLOC_TYPE_GSM2PSTN: + case RTP_ALLOC_TYPE_GSM2GSM: + num_fd = 4; + fd_out[0] = rtp_gsm.rtp_fd; + fd_out[1] = rtp_gsm.rtcp_fd; + fd_out[2] = rtp_pstn.rtp_fd; + fd_out[3] = rtp_pstn.rtcp_fd; + } + bzero(&msg, sizeof msg); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgu.buf; + msg.msg_controllen = CMSG_SPACE(sizeof(int) * num_fd); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fd); + fd_bufptr = (int *) CMSG_DATA(cmsg); + bcopy(fd_out, fd_bufptr, sizeof(int) * num_fd); + sendmsg(fd, &msg, 0); + close_fds(fd_out, num_fd); +} diff -r 560a3765ab30 -r 247f4bbde24c rtp-mgr/ctrl_sock.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtp-mgr/ctrl_sock.c Mon May 27 19:42:19 2024 +0000 @@ -0,0 +1,72 @@ +/* + * In this module we implement the logic of listening on the RTP + * allocator control socket and accepting control connections. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "struct.h" +#include "select.h" + +static const char ctrl_socket_pathname[] = "/var/gsm/rtp_alloc_socket"; + +extern void ctrl_message_handler(); + +void +ctrlsock_accept_handler(listener_fd) +{ + struct sockaddr_un sa; + socklen_t sa_len; + int conn_fd; + + sa_len = sizeof sa; + conn_fd = accept(listener_fd, (struct sockaddr *) &sa, &sa_len); + if (conn_fd < 0) { + syslog(LOG_CRIT, "accept on UNIX socket: %m"); + exit(1); + } + update_max_fd(conn_fd); + FD_SET(conn_fd, &select_for_read); + select_handlers[conn_fd] = ctrl_message_handler; + syslog(LOG_DEBUG, "accepted ctrl connection"); +} + +create_ctrl_socket() +{ + struct sockaddr_un sa; + unsigned sa_len; + int fd, rc; + + fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (fd < 0) { + syslog(LOG_CRIT, "socket(AF_UNIX, SOCK_SEQPACKET, 0): %m"); + return(-1); + } + unlink(ctrl_socket_pathname); + fill_sockaddr_un(ctrl_socket_pathname, &sa, &sa_len); + rc = bind(fd, (struct sockaddr *) &sa, sa_len); + if (rc < 0) { + syslog(LOG_ERR, "bind to %s: %m", ctrl_socket_pathname); + return(-1); + } + rc = listen(fd, 3); + if (rc < 0) { + syslog(LOG_CRIT, "listen on UNIX socket: %m"); + return(-1); + } + chmod(ctrl_socket_pathname, 0775); + update_max_fd(fd); + FD_SET(fd, &select_for_read); + select_handlers[fd] = ctrlsock_accept_handler; + return(0); +} diff -r 560a3765ab30 -r 247f4bbde24c rtp-mgr/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtp-mgr/main.c Mon May 27 19:42:19 2024 +0000 @@ -0,0 +1,67 @@ +/* + * Main module for themwi-rtp-mgr. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int syslog_facility; + +fd_set select_for_read; +void (*select_handlers[FD_SETSIZE])(); +void *select_data[FD_SETSIZE]; + +static int max_fd; + +update_max_fd(newfd) +{ + if (newfd >= FD_SETSIZE) { + syslog(LOG_CRIT, "FATAL: file descriptor %d >= FD_SETSIZE", + newfd); + exit(1); + } + if (newfd > max_fd) + max_fd = newfd; +} + +main(argc, argv) + char **argv; +{ + fd_set fds; + int cc, i; + + read_config_file(); + openlog("themwi-rtp-mgr", 0, syslog_facility); + if (create_ctrl_socket() < 0) { + fprintf(stderr, + "error creating RTP allocator control socket\n"); + exit(1); + } + signal(SIGPIPE, SIG_IGN); + /* main select loop */ + for (;;) { + bcopy(&select_for_read, &fds, sizeof(fd_set)); + cc = select(max_fd+1, &fds, 0, 0, 0); + if (cc < 0) { + if (errno == EINTR) + continue; + syslog(LOG_CRIT, "select: %m"); + exit(1); + } + for (i = 0; cc && i <= max_fd; i++) { + if (FD_ISSET(i, &fds)) { + select_handlers[i](i, select_data[i]); + cc--; + } + } + } +} diff -r 560a3765ab30 -r 247f4bbde24c rtp-mgr/readconf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtp-mgr/readconf.c Mon May 27 19:42:19 2024 +0000 @@ -0,0 +1,261 @@ +/* + * In this module we implement the reading of /var/gsm/themwi-rtp.cfg: + * we parse and save the configured IP address and port range for each + * of our two sides, GSM and PSTN. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "struct.h" + +struct bind_range_cfg bind_range_gsm, bind_range_pstn; +int syslog_facility = LOG_USER; + +static const char config_file_pathname[] = "/var/gsm/themwi-rtp.cfg"; + +struct parse_state { + int lineno; + int set_mask; +}; + +static void +handle_bind_ip(st, kw, brc, line) + struct parse_state *st; + char *kw, *line; + struct bind_range_cfg *brc; +{ + char *cp, *np; + + for (cp = line; 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, kw); + exit(1); + } + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + while (isspace(*cp)) + cp++; + if (*cp != '\0' && *cp != '#') + goto inv_syntax; + brc->bind_ip.s_addr = inet_addr(np); + if (brc->bind_ip.s_addr == INADDR_NONE) { + fprintf(stderr, + "%s line %d: invalid IP address argument \"%s\"\n", + config_file_pathname, st->lineno, np); + exit(1); + } +} + +static void +handle_port_range(st, kw, brc, line) + struct parse_state *st; + char *kw, *line; + struct bind_range_cfg *brc; +{ + char *cp, *np1, *np2; + + for (cp = line; isspace(*cp); cp++) + ; + if (!isdigit(*cp)) { +inv_syntax: fprintf(stderr, + "%s line %d: %s setting requires two numeric arguments\n", + config_file_pathname, st->lineno, kw); + exit(1); + } + for (np1 = cp; isdigit(*cp); cp++) + ; + if (!isspace(*cp)) + goto inv_syntax; + while (isspace(*cp)) + cp++; + if (!isdigit(*cp)) + goto inv_syntax; + for (np2 = cp; isdigit(*cp); cp++) + ; + if (*cp && !isspace(*cp)) + goto inv_syntax; + while (isspace(*cp)) + cp++; + if (*cp != '\0' && *cp != '#') + goto inv_syntax; + brc->port_range_start = atoi(np1); + brc->port_range_end = atoi(np2); + if (brc->port_range_start & 1) { + fprintf(stderr, "%s line %d: start port must be even\n", + config_file_pathname, st->lineno); + exit(1); + } + if (!(brc->port_range_end & 1)) { + fprintf(stderr, "%s line %d: end port must be odd\n", + config_file_pathname, st->lineno); + exit(1); + } + if (brc->port_range_end <= brc->port_range_start) { + fprintf(stderr, + "%s line %d: end port must be greater than start port\n", + config_file_pathname, st->lineno); + exit(1); + } + brc->port_next = brc->port_range_start; + brc->port_tries = (brc->port_range_end - brc->port_range_start + 1) / 2; +} + +static void +handle_syslog_facility(st, kw, dummy_ptr, line) + struct parse_state *st; + char *kw, *line; + void *dummy_ptr; +{ + char *cp, *np; + + for (cp = line; 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, kw); + exit(1); + } + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + while (isspace(*cp)) + cp++; + if (*cp != '\0' && *cp != '#') + goto inv_syntax; + if (!strcmp(np, "authpriv")) + syslog_facility = LOG_AUTHPRIV; + else if (!strcmp(np, "cron")) + syslog_facility = LOG_CRON; + else if (!strcmp(np, "daemon")) + syslog_facility = LOG_DAEMON; + else if (!strcmp(np, "ftp")) + syslog_facility = LOG_FTP; + else if (!strcmp(np, "lpr")) + syslog_facility = LOG_LPR; + else if (!strcmp(np, "mail")) + syslog_facility = LOG_MAIL; + else if (!strcmp(np, "news")) + syslog_facility = LOG_NEWS; + else if (!strcmp(np, "user")) + syslog_facility = LOG_USER; + else if (!strcmp(np, "uucp")) + syslog_facility = LOG_UUCP; + else if (!strcmp(np, "local0")) + syslog_facility = LOG_LOCAL0; + else if (!strcmp(np, "local1")) + syslog_facility = LOG_LOCAL1; + else if (!strcmp(np, "local2")) + syslog_facility = LOG_LOCAL2; + else if (!strcmp(np, "local3")) + syslog_facility = LOG_LOCAL3; + else if (!strcmp(np, "local4")) + syslog_facility = LOG_LOCAL4; + else if (!strcmp(np, "local5")) + syslog_facility = LOG_LOCAL5; + else if (!strcmp(np, "local6")) + syslog_facility = LOG_LOCAL6; + else if (!strcmp(np, "local7")) + syslog_facility = LOG_LOCAL7; + else { + fprintf(stderr, + "%s line %d: \"%s\" is not a recognized syslog facility\n", + config_file_pathname, st->lineno, np); + exit(1); + } +} + +static void +process_line(st, line) + struct parse_state *st; + char *line; +{ + char *cp, *np; + void (*handler)(); + struct bind_range_cfg *ipside; + 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, "gsm-ip-addr")) { + handler = handle_bind_ip; + ipside = &bind_range_gsm; + set_id = 1; + } else if (!strcmp(np, "gsm-port-range")) { + handler = handle_port_range; + ipside = &bind_range_gsm; + set_id = 2; + } else if (!strcmp(np, "pstn-ip-addr")) { + handler = handle_bind_ip; + ipside = &bind_range_pstn; + set_id = 4; + } else if (!strcmp(np, "pstn-port-range")) { + handler = handle_port_range; + ipside = &bind_range_pstn; + set_id = 8; + } else if (!strcmp(np, "syslog-facility")) { + handler = handle_syslog_facility; + ipside = NULL; + 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); + } + handler(st, np, ipside, cp); + 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 != 15) { + fprintf(stderr, "error: %s did not set all required settings\n", + config_file_pathname); + exit(1); + } +} diff -r 560a3765ab30 -r 247f4bbde24c rtp-mgr/select.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtp-mgr/select.h Mon May 27 19:42:19 2024 +0000 @@ -0,0 +1,5 @@ +/* extern declarations for select loop global variables */ + +extern fd_set select_for_read; +extern void (*select_handlers[])(); +extern void *select_data[]; diff -r 560a3765ab30 -r 247f4bbde24c rtp-mgr/sockinit.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtp-mgr/sockinit.c Mon May 27 19:42:19 2024 +0000 @@ -0,0 +1,40 @@ +/* + * This library module implements a function that helps initialize + * sockaddr for bind or connect operations on UNIX domain sockets. + * + * Back when I programmed under 4.3BSD UNIX, this operation was simple + * and straightforward - but under "modern" Unixes, it appears to be + * a complex affair, given the messy code (originally copied from + * Osmocom) that appears in FreeCalypso host tools for the rvinterf + * local socket interface. Hence I am factoring that mess out into + * its own library function this time around. + */ + +#include +#include +#include +#include +#include + +void +fill_sockaddr_un(pathname, sunp, lenp) + char *pathname; + struct sockaddr_un *sunp; + unsigned *lenp; +{ + /* local socket binding voodoo copied from osmocon */ + sunp->sun_family = AF_UNIX; + strncpy(sunp->sun_path, pathname, sizeof(sunp->sun_path)); + sunp->sun_path[sizeof(sunp->sun_path) - 1] = '\0'; + /* we use the same magic that X11 uses in Xtranssock.c for + * calculating the proper length of the sockaddr */ +#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) + sunp->sun_len = strlen(sunp->sun_path); +#endif +#if defined(BSD44SOCKETS) || defined(SUN_LEN) + *lenp = SUN_LEN(sunp); +#else + *lenp = strlen(sunp->sun_path) + + offsetof(struct sockaddr_un, sun_path) + 1; +#endif +} diff -r 560a3765ab30 -r 247f4bbde24c rtp-mgr/struct.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtp-mgr/struct.h Mon May 27 19:42:19 2024 +0000 @@ -0,0 +1,17 @@ +/* + * This header file defines internal data structures for themwi-rtp-mgr. + */ + +struct bind_range_cfg { + struct in_addr bind_ip; + unsigned port_range_start; + unsigned port_range_end; + unsigned port_next; + unsigned port_tries; +}; + +struct rtp_one_end { + int rtp_fd; + int rtcp_fd; + struct sockaddr_in bound_addr; +};