diff options
Diffstat (limited to 'tacplus-daemon/utils.c')
-rw-r--r-- | tacplus-daemon/utils.c | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/tacplus-daemon/utils.c b/tacplus-daemon/utils.c new file mode 100644 index 0000000..442d259 --- /dev/null +++ b/tacplus-daemon/utils.c @@ -0,0 +1,330 @@ +/* + TACACS+ D-Bus Daemon code + + Copyright (c) 2018-2019 AT&T Intellectual Property. + Copyright (c) 2015-2016 Brocade Communications Systems, Inc. + + SPDX-License-Identifier: GPL-2.0-only +*/ + +#include <syslog.h> +#include <stdlib.h> +#include <math.h> +#include <net/if.h> +#include <ifaddrs.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <utmpx.h> + +#include "utils.h" + +bool sockaddr_addr_equal (const struct sockaddr *sa1, const struct sockaddr *sa2) +{ + if (sa1->sa_family != sa2->sa_family) + return false; + + if (sa1->sa_family == AF_INET) { + struct in_addr *sin1 = &((struct sockaddr_in *)sa1)->sin_addr; + struct in_addr *sin2 = &((struct sockaddr_in *)sa2)->sin_addr; + + return sin1->s_addr == sin2->s_addr ? true : false; + } + else if (sa1->sa_family == AF_INET6) { + struct in6_addr *sin1 = &((struct sockaddr_in6 *)sa1)->sin6_addr; + struct in6_addr *sin2 = &((struct sockaddr_in6 *)sa2)->sin6_addr; + + return memcmp(sin1, sin2, sizeof *sin1) == 0 ? true : false; + } + + return false; +} + +struct addrinfo *tacplus_addrinfo(const char *opt_server, const char *opt_port) { + struct addrinfo *result = NULL; + static const struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM + }; + + int err = getaddrinfo(opt_server, opt_port, &hints, &result); + + if (err != 0) { + syslog(LOG_ERR, "resolving %s:%s error: %s", + strOrNil(opt_server), strOrNil(opt_port), + gai_strerror(err)); + /* TODO: error handling */ + return NULL; + } + + return result; +} + +char *addrinfo_to_string(const struct addrinfo *addr) +{ + char addr_str[INET6_ADDRSTRLEN]; + + if (getnameinfo(addr->ai_addr, addr->ai_addrlen, addr_str, + INET6_ADDRSTRLEN, 0, 0, NI_NUMERICHOST) == 0) + return strdup(addr_str); + else + syslog(LOG_DEBUG, "Could not convert address to string"); + + return NULL; +} + +uint16_t get_addrinfo_port(const struct addrinfo *ai) +{ + struct sockaddr *sa = ai->ai_addr; + + if (sa->sa_family == AF_INET) + return ntohs(((struct sockaddr_in *)sa)->sin_port); + else if (sa->sa_family == AF_INET6) + return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); + else + return 0; +} + +int is_sockaddr_loopback(struct sockaddr *saddr) +{ + switch (saddr->sa_family) { + case AF_INET: + return IS_INADDR_LOOPBACK(((struct sockaddr_in *)saddr)->sin_addr); + case AF_INET6: + return IS_IN6ADDR_LOOPBACK(((struct sockaddr_in6 *)saddr)->sin6_addr); + } + + return 0; +} + +struct addrinfo *get_interface_addrinfo(const char *ifname, int af) +{ + struct ifaddrs *ifas_head = NULL, *ifa; + struct addrinfo *info = NULL; + socklen_t addrlen; + int ret; + + if ((ret = getifaddrs(&ifas_head))) { + syslog(LOG_WARNING, "getifaddrs() failed (%i): %s", + ret, strerror(errno)); + return NULL; + } + + for (ifa = ifas_head; ifa; ifa = ifa->ifa_next) { + if (! ifa->ifa_addr) + continue; + + if (ifa->ifa_addr->sa_family != af) + continue; + + if (strncmp(ifname, ifa->ifa_name, IFNAMSIZ)) + continue; + + if (is_sockaddr_loopback(ifa->ifa_addr)) + continue; + + if (! (info = (struct addrinfo *) calloc(1, sizeof(struct addrinfo)))) { + syslog(LOG_ERR, "get_interface_addrinfo(): addrinfo " + "memory allocation failure"); + goto finish; + } + + addrlen = af == AF_INET ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6); + + if (! (info->ai_addr = (struct sockaddr *) malloc(addrlen))) { + syslog(LOG_ERR, "get_interface_addrinfo(): sockaddr " + "memory allocation failure"); + free(info); + goto finish; + } + + memcpy(info->ai_addr, ifa->ifa_addr, addrlen); + info->ai_family = ifa->ifa_addr->sa_family; + info->ai_addrlen = addrlen; + break; + } + + if (! info) + syslog(LOG_DEBUG, "Interface %s does not exist or has no " + "suitable addresses", ifname); + +finish: + freeifaddrs(ifas_head); + return info; +} + +void free_interface_addrinfo(struct addrinfo **info) +{ + if (! info || !*info) + return; + + free((*info)->ai_addr); + free(*info); + *info = NULL; +} + +int is_interface_up(const char *ifname) +{ + struct ifreq req = {}; + int fd; + + fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (fd < 0) { + syslog(LOG_ERR, "is_interface_up(%s): failed to open socket (%u): %s", + ifname, errno, strerror(errno)); + return -1; + } + + strncat(req.ifr_name, ifname, sizeof(req.ifr_name)-1); + if (ioctl(fd, SIOCGIFFLAGS, &req) < 0) { + syslog(LOG_WARNING, "is_interface_up(%s): could not get interface " + "status (%u): %s", req.ifr_name, errno, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return (req.ifr_flags & IFF_UP) ? 1 : 0; +} + +void +cur_mono_time(struct timespec *ts) +{ + if (clock_gettime(CLOCK_MONOTONIC_RAW, ts) < 0) { + syslog(LOG_ERR, "Error getting current time: %s", strerror(errno)); + SET_TIMESPEC_VALS(*ts, 0, 0); + } + + timespec_normalise(ts); +} + +/* + * Adjust the timespec spec to ensure that the nanosecond portion is in the + * range 0-999,999,999. + */ +struct timespec * +timespec_normalise(struct timespec *spec) +{ + while (spec->tv_nsec <= -SEC_TO_NSECS) { + spec->tv_sec--; + spec->tv_nsec += SEC_TO_NSECS; + } + + while (spec->tv_nsec < 0) { + spec->tv_sec--; + spec->tv_nsec = SEC_TO_NSECS + spec->tv_nsec; + } + + while (spec->tv_nsec >= SEC_TO_NSECS) { + spec->tv_sec++; + spec->tv_nsec -= SEC_TO_NSECS; + } + + return spec; +} + +/* + * Subtract time b from a, placing the result in result + */ +struct timespec * +timespec_sub(const struct timespec *a, const struct timespec *b, + struct timespec *result) +{ + result->tv_sec = a->tv_sec - b->tv_sec; + result->tv_nsec = a->tv_nsec - b->tv_nsec; + return timespec_normalise(result); +} + +/* + * Return -1, 0, or 1 if a represents a time less than, equal to, or + * greater than the time represented by b, respectively. + */ +int +timespec_cmp(const struct timespec *a, const struct timespec *b) +{ + if (a->tv_sec > b->tv_sec) + return 1; + else if (a->tv_sec < b->tv_sec) + return -1; + + if (a->tv_nsec > b->tv_nsec) + return 1; + else if (a->tv_nsec < b->tv_nsec) + return -1; + + /* timespecs equal */ + return 0; +} + +/* + * Get the user's remote login address for a given TTY + * + * WARNING: this function is not thread safe due to the following calls: + * - setutxent() + * - getutxline() + * - endutxent() + */ +char * +get_tty_login_addr(const char *tty) +{ + struct utmpx tty_utmp = {0}; + + if (!tty) { + return NULL; + } + + strncpy(tty_utmp.ut_line, tty, sizeof tty_utmp.ut_line); + + setutxent(); + struct utmpx *up = getutxline(&tty_utmp); + endutxent(); + if (!up || strlen(up->ut_host) == 0) + return NULL; + + return strdup(up->ut_host); +} + +int +new_cb_timer(timer_t *timer, void (*cb) (union sigval), union sigval *user) +{ + struct sigevent se = { + .sigev_notify = SIGEV_THREAD, + .sigev_notify_function = cb + }; + + if (user) + se.sigev_value = *user; + + int ret = timer_create(CLOCK_MONOTONIC, &se, timer); + if (ret < 0) + syslog(LOG_ERR, "timer_create() failed (%d): %s", ret, strerror(errno)); + + return ret; +} + +int +set_timer(timer_t timer, const struct itimerspec *it) +{ + struct itimerspec discard; + + int ret = timer_settime(timer, 0, it, &discard); + if (ret < 0) + syslog(LOG_ERR, "timer_settime() failed (%d): %s", ret, strerror(errno)); + + return ret; +} + +int +expire_timer(timer_t timer) +{ + struct itimerspec it = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 1 + }; + + return set_timer(timer, &it); +} |