diff --git a/Knot.files b/Knot.files index 30a8344f15f5aaddbf2e62c23972b5caa41f104d..70c435eb2123f2eeae149fb6edd16c9c61931f3a 100644 --- a/Knot.files +++ b/Knot.files @@ -73,6 +73,8 @@ src/contrib/openbsd/strlcat.c src/contrib/openbsd/strlcat.h src/contrib/openbsd/strlcpy.c src/contrib/openbsd/strlcpy.h +src/contrib/popenve.c +src/contrib/popenve.h src/contrib/qp-trie/trie.c src/contrib/qp-trie/trie.h src/contrib/semaphore.c diff --git a/src/contrib/Makefile.inc b/src/contrib/Makefile.inc index 7addce2c5a92a6221ab6f5d7cd4f8d77b745d535..5b0079eca9c0dd4e29148fb020d3fa75dd14541f 100644 --- a/src/contrib/Makefile.inc +++ b/src/contrib/Makefile.inc @@ -43,6 +43,8 @@ libcontrib_la_SOURCES = \ contrib/mempattern.h \ contrib/net.c \ contrib/net.h \ + contrib/popenve.c \ + contrib/popenve.h \ contrib/qp-trie/trie.c \ contrib/qp-trie/trie.h \ contrib/semaphore.c \ diff --git a/src/contrib/popenve.c b/src/contrib/popenve.c new file mode 100644 index 0000000000000000000000000000000000000000..366e049c3c14a37ccb5e992b1c44dc1c9decc5c2 --- /dev/null +++ b/src/contrib/popenve.c @@ -0,0 +1,83 @@ +/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + 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 3 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, see <https://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "popenve.h" + +int kpopenve(const char *binfile, char *const args[], char *const env[]) +{ + int pipefds[2]; + if (pipe(pipefds) < 0) { + return -errno; + } + if (fcntl(pipefds[0], F_SETFD, FD_CLOEXEC) < 0) { + int fcntlerrno = errno; + close(pipefds[0]); + close(pipefds[1]); + return -fcntlerrno; + } + + pid_t forkpid = fork(); + if (forkpid < 0) { + int forkerrno = errno; + close(pipefds[0]); + close(pipefds[1]); + return -forkerrno; + } + + if (forkpid == 0) { +dup_stdout: + if (dup2(pipefds[1], STDOUT_FILENO) < 0) { + if (errno == EINTR) { + goto dup_stdout; + } + perror("dup_stdout"); + close(pipefds[0]); + close(pipefds[1]); + exit(98); + } + close(pipefds[1]); + + execve(binfile, args, env); + perror("execve"); + exit(99); + } + + close(pipefds[1]); + return pipefds[0]; +} + +FILE *kpopenve2(const char *binfile, char *const args[], char *const env[]) +{ + int p = kpopenve(binfile, args, env); + if (p < 0) { + errno = -p; + return NULL; + } + + FILE *res = fdopen(p, "r"); + if (res == NULL) { + int fdoerrno = errno; + close(p); + errno = fdoerrno; + } + return res; +} diff --git a/src/contrib/popenve.h b/src/contrib/popenve.h new file mode 100644 index 0000000000000000000000000000000000000000..563c9db019b911ff51631b192a2bd8481c30ea4a --- /dev/null +++ b/src/contrib/popenve.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + 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 3 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, see <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdio.h> + +/*! + * \brief Hybrid of popen() and execve(). + * + * This function is a safer altervative to popen(), it is the same to + * popen() as execve() is to system(). + * + * \param binfile Executable file to be executed. + * \param args NULL-terminated arguments; first shall be the prog name! + * \param env NULL-terminated environment variables "key=value" + * + * \retval < 0 Error occured, set to -errno. + * \return > 0 File descriptor of the pipe reading end. + */ +int kpopenve(const char *binfile, char *const args[], char *const env[]); + +/*! + * \brief Variant of kpopenve() returning FILE* + * + * \param binfile Executable file to be executed. + * \param args NULL-terminated arguments; first shall be the prog name! + * \param env NULL-terminated environment variables "key=value" + * + * \retval NULL Error occured, see errno. + * \return Pointer to open file descriptor. + */ +FILE *kpopenve2(const char *binfile, char *const args[], char *const env[]); diff --git a/src/utils/knot-xdp-gun/main.c b/src/utils/knot-xdp-gun/main.c index 74a6138f5bc0534ed731364db465e33821a06bae..8170ebf11af7f1ba8c4bef90ff94bb2b5c979623 100644 --- a/src/utils/knot-xdp-gun/main.c +++ b/src/utils/knot-xdp-gun/main.c @@ -37,6 +37,7 @@ #include "libknot/libknot.h" #include "contrib/openbsd/strlcpy.h" +#include "contrib/popenve.h" #include "utils/common/params.h" #include "load_queries.h" @@ -342,34 +343,41 @@ static bool str2mac(const char *str, uint8_t mac[]) return true; } -static int ip_route_get(const char *ip_str, const char *what, char **res) +static FILE *popen_ip(char *arg1, char *arg2, char *arg3) { - char cmd[50 + strlen(ip_str) + strlen(what)]; - (void)snprintf(cmd, sizeof(cmd), "ip route get %s | grep -o ' %s [^ ]* '", ip_str, what); + char *args[5] = { "ip", arg1, arg2, arg3, NULL }; + char *env[] = { NULL }; + return kpopenve2("/sbin/ip", args, env); +} +static int ip_route_get(const char *ip_str, const char *what, char **res) +{ errno = 0; - FILE *p = popen(cmd, "r"); + FILE *p = popen_ip("route", "get", (char *)ip_str); // hope ip_str gets not broken if (p == NULL) { return (errno != 0) ? knot_map_errno() : KNOT_ENOMEM; } - char check[16] = { 0 }, got[256] = { 0 }; - if (fscanf(p, "%15s%255s", check, got) != 2 || - strcmp(check, what) != 0) { - int ret = feof(p) ? KNOT_ENOENT : KNOT_EMALF; - pclose(p); - return ret; + char buf[256] = { 0 }; + bool hit = false; + while (fscanf(p, "%255s", buf) == 1) { + if (hit) { + *res = strdup(buf); + fclose(p); + return *res == NULL ? KNOT_ENOMEM : KNOT_EOK; + } + if (strcmp(buf, what) == 0) { + hit = true; + } } - pclose(p); - - *res = strdup(got); - return *res == NULL ? KNOT_ENOMEM : KNOT_EOK; + fclose(p); + return KNOT_ENOENT; } static int remoteIP2MAC(const char *ip_str, bool ipv6, char devname[], uint8_t remote_mac[]) { errno = 0; - FILE *p = popen(ipv6 ? "ip -6 neigh" : "arp -ne", "r"); + FILE *p = popen_ip(ipv6 ? "-6" : "-4", "neigh", NULL); if (p == NULL) { return (errno != 0) ? knot_map_errno() : KNOT_ENOMEM; } @@ -384,14 +392,14 @@ static int remoteIP2MAC(const char *ip_str, bool ipv6, char devname[], uint8_t r if (strcmp(fields[0], ip_str) != 0) { continue; } - if (!str2mac(fields[ipv6 ? 4 : 2], remote_mac)) { + if (!str2mac(fields[4], remote_mac)) { ret = KNOT_EMALF; } else { - strlcpy(devname, fields[ipv6 ? 2 : 4], IFNAMSIZ); + strlcpy(devname, fields[2], IFNAMSIZ); ret = KNOT_EOK; } } - pclose(p); + fclose(p); return ret; }