From d06ee7932646b90a88e281db45d630ee43d2e00e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Obrembski?= Date: Mon, 18 Jan 2021 19:24:21 +0100 Subject: [PATCH 1/3] Surround AC_CHECK_FILE macros in configure.ac with crosscompile checking When cross-compiling, AC_CHECK_FILE macro is not supported by autotools. Falling back to default in that case. --- configure.ac | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/configure.ac b/configure.ac index 9c0f7d09..1d4ac647 100644 --- a/configure.ac +++ b/configure.ac @@ -320,26 +320,34 @@ AS_IF([test "x$enable_proc" = "x"], [ # check for ppp if not specified AC_PATH_PROG(PPP, [ppp], [/usr/sbin/ppp], "$PATH:/sbin:/usr/sbin") AS_IF([test "x$PPP_PATH" = "x"], [ - AC_CHECK_FILE([$PPP], [ - AS_IF([test "x$PPP_PATH" = "x"], [ - PPP_PATH="$PPP" - ]) - AS_IF([test "x$with_ppp" = "x"], [ - with_ppp="yes" - ]) - ],[]) + if test "$cross_compiling" = "yes"; then + AC_MSG_NOTICE([Cross compiling, ommited PPP path check]) + else + AC_CHECK_FILE([$PPP], [ + AS_IF([test "x$PPP_PATH" = "x"], [ + PPP_PATH="$PPP" + ]) + AS_IF([test "x$with_ppp" = "x"], [ + with_ppp="yes" + ]) + ],[]) + fi ]) # check for pppd if not specified AC_PATH_PROG(PPPD, [pppd], [/usr/sbin/pppd], "$PATH:/sbin:/usr/sbin") AS_IF([test "x$PPP_PATH" = "x"], [ - AC_CHECK_FILE([$PPPD], [ - AS_IF([test "x$PPP_PATH" = "x"], [ - PPP_PATH="$PPPD" - ]) - AS_IF([test "x$with_pppd" = "x"], [ - with_pppd="yes" - ]) - ],[]) + if test "$cross_compiling" = "yes"; then + AC_MSG_NOTICE([Cross compiling, ommited PPP path check]) + else + AC_CHECK_FILE([$PPPD], [ + AS_IF([test "x$PPP_PATH" = "x"], [ + PPP_PATH="$PPPD" + ]) + AS_IF([test "x$with_pppd" = "x"], [ + with_pppd="yes" + ]) + ],[]) + fi ]) # when neither ppp nor pppd are enabled fall back to a sensible choice for the platform AS_IF([test "x$with_ppp" = "x" -a "x$with_pppd" = "x"], [ From 42e9e8d12df43adf82199f406271e915c89a29ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Obrembski?= Date: Mon, 18 Jan 2021 19:30:05 +0100 Subject: [PATCH 2/3] Added feature to customize resolv.conf file path at configure --- configure.ac | 6 ++++++ src/ipv4.c | 60 +++++++++++++++++++++++++++++----------------------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/configure.ac b/configure.ac index 1d4ac647..3851719f 100644 --- a/configure.ac +++ b/configure.ac @@ -495,6 +495,12 @@ int main(int argc, char **argv){ AC_MSG_NOTICE([HAVE_SO_BINDTODEVICE... 0]) ]) +AC_ARG_WITH([resolvconf-config-file], + AS_HELP_STRING([--with-resolvconf-config-file], + [Set the path to the resolv.conf config file. \ + Default is /etc/resolv.conf.]), + AC_DEFINE_UNQUOTED([RESOLV_CONF_FILE_PATH],["$withval"]) +) # prepare to get rid of obsolete code (FortiOS 4) AC_ARG_ENABLE([obsolete], diff --git a/src/ipv4.c b/src/ipv4.c index b8cb2ce6..c5f3ffcf 100644 --- a/src/ipv4.c +++ b/src/ipv4.c @@ -45,6 +45,10 @@ static char show_route_buffer[SHOW_ROUTE_BUFFER_SIZE]; #define ERR_IPV4_NO_SUCH_ROUTE -4 #define ERR_IPV4_PROC_NET_ROUTE -5 +#ifndef RESOLV_CONF_FILE_PATH +#define RESOLV_CONF_FILE_PATH /etc/resolv.conf +#endif + static inline const char *err_ipv4_str(int code) { if (code == ERR_IPV4_SEE_ERRNO) @@ -1120,36 +1124,36 @@ int ipv4_add_nameservers_to_resolv_conf(struct tunnel *tunnel) free(resolvconf_call); } else { #endif - log_debug("Attempting to modify /etc/resolv.conf directly.\n"); - file = fopen("/etc/resolv.conf", "r+"); + log_debug("Attempting to modify " RESOLV_CONF_FILE_PATH " directly.\n"); + file = fopen(RESOLV_CONF_FILE_PATH, "r+"); if (file == NULL) { - log_warn("Could not open /etc/resolv.conf (%s).\n", + log_warn("Could not open " RESOLV_CONF_FILE_PATH " (%s).\n", strerror(errno)); return 1; } if (fstat(fileno(file), &stat) == -1) { - log_warn("Could not stat /etc/resolv.conf (%s).\n", + log_warn("Could not stat " RESOLV_CONF_FILE_PATH " (%s).\n", strerror(errno)); goto err_close; } if (stat.st_size == 0) { - log_warn("Could not read /etc/resolv.conf (%s).\n", + log_warn("Could not read " RESOLV_CONF_FILE_PATH " (%s).\n", "Empty file"); goto err_close; } buffer = malloc(stat.st_size + 1); if (buffer == NULL) { - log_warn("Could not read /etc/resolv.conf (%s).\n", + log_warn("Could not read " RESOLV_CONF_FILE_PATH " (%s).\n", strerror(errno)); goto err_close; } // Copy all file contents at once if (fread(buffer, stat.st_size, 1, file) != 1) { - log_warn("Could not read /etc/resolv.conf.\n"); + log_warn("Could not read "RESOLV_CONF_FILE_PATH ".\n"); goto err_free; } @@ -1189,24 +1193,24 @@ int ipv4_add_nameservers_to_resolv_conf(struct tunnel *tunnel) line = strtok_r(NULL, "\n", &saveptr)) { if (strcmp(line, ns1) == 0) { tunnel->ipv4.ns1_was_there = 1; - log_debug("ns1 already present in /etc/resolv.conf.\n"); + log_debug("ns1 already present in " RESOLV_CONF_FILE_PATH ".\n"); } } if (tunnel->ipv4.ns1_was_there == 0) - log_debug("Adding \"%s\", to /etc/resolv.conf.\n", ns1); + log_debug("Adding \"%s\", to " RESOLV_CONF_FILE_PATH " .\n", ns1); for (const char *line = strtok_r(buffer, "\n", &saveptr); line != NULL; line = strtok_r(NULL, "\n", &saveptr)) { if (strcmp(line, ns2) == 0) { tunnel->ipv4.ns2_was_there = 1; - log_debug("ns2 already present in /etc/resolv.conf.\n"); + log_debug("ns2 already present in " RESOLV_CONF_FILE_PATH ".\n"); } } if (tunnel->ipv4.ns2_was_there == 0) - log_debug("Adding \"%s\", to /etc/resolv.conf.\n", ns2); + log_debug("Adding \"%s\", to " RESOLV_CONF_FILE_PATH ".\n", ns2); if (dns_suffix[0] == '\0') { tunnel->ipv4.dns_suffix_was_there = -1; @@ -1217,17 +1221,18 @@ int ipv4_add_nameservers_to_resolv_conf(struct tunnel *tunnel) if (dns_suffix[0] != '\0' && strcmp(line, dns_suffix) == 0) { tunnel->ipv4.dns_suffix_was_there = 1; - log_debug("dns_suffix already present in /etc/resolv.conf.\n"); + log_debug("dns_suffix already present in " RESOLV_CONF_FILE_PATH ".\n"); } } } if (tunnel->ipv4.dns_suffix_was_there == 0) - log_debug("Adding \"%s\", to /etc/resolv.conf.\n", dns_suffix); + log_debug("Adding \"%s\", to " RESOLV_CONF_FILE_PATH ".\n", + dns_suffix); rewind(file); if (fread(buffer, stat.st_size, 1, file) != 1) { - log_warn("Could not read /etc/resolv.conf.\n"); + log_warn("Could not read " RESOLV_CONF_FILE_PATH ".\n"); goto err_free; } @@ -1263,7 +1268,7 @@ int ipv4_add_nameservers_to_resolv_conf(struct tunnel *tunnel) if (use_resolvconf == 0) { #endif if (fclose(file)) - log_warn("Could not close /etc/resolv.conf: %s\n", + log_warn("Could not close " RESOLV_CONF_FILE_PATH": %s\n", strerror(errno)); #if HAVE_RESOLVCONF } else { @@ -1321,29 +1326,29 @@ int ipv4_del_nameservers_from_resolv_conf(struct tunnel *tunnel) } #endif - file = fopen("/etc/resolv.conf", "r+"); + file = fopen(RESOLV_CONF_FILE_PATH, "r+"); if (file == NULL) { - log_warn("Could not open /etc/resolv.conf (%s).\n", + log_warn("Could not open " RESOLV_CONF_FILE_PATH " (%s).\n", strerror(errno)); return 1; } if (fstat(fileno(file), &stat) == -1) { - log_warn("Could not stat /etc/resolv.conf (%s).\n", + log_warn("Could not stat " RESOLV_CONF_FILE_PATH " (%s).\n", strerror(errno)); goto err_close; } buffer = malloc(stat.st_size + 1); if (buffer == NULL) { - log_warn("Could not read /etc/resolv.conf (%s).\n", + log_warn("Could not read " RESOLV_CONF_FILE_PATH " (%s).\n", strerror(errno)); goto err_close; } // Copy all file contents at once if (fread(buffer, stat.st_size, 1, file) != 1) { - log_warn("Could not read /etc/resolv.conf.\n"); + log_warn("Could not read " RESOLV_CONF_FILE_PATH ".\n"); goto err_free; } @@ -1365,9 +1370,9 @@ int ipv4_del_nameservers_from_resolv_conf(struct tunnel *tunnel) strncat(dns_suffix, tunnel->ipv4.dns_suffix, MAX_DOMAIN_LENGTH); } - file = freopen("/etc/resolv.conf", "w", file); + file = freopen(RESOLV_CONF_FILE_PATH, "w", file); if (file == NULL) { - log_warn("Could not reopen /etc/resolv.conf (%s).\n", + log_warn("Could not reopen " RESOLV_CONF_FILE_PATH " (%s).\n", strerror(errno)); goto err_free; } @@ -1377,13 +1382,16 @@ int ipv4_del_nameservers_from_resolv_conf(struct tunnel *tunnel) line = strtok_r(NULL, "\n", &saveptr)) { if (ns1[0] != '\0' && strcmp(line, ns1) == 0 && (tunnel->ipv4.ns1_was_there == 0)) { - log_debug("Deleting \"%s\" from /etc/resolv.conf.\n", ns1); + log_debug("Deleting \"%s\" from " RESOLV_CONF_FILE_PATH ".\n", + ns1); } else if (ns2[0] != '\0' && strcmp(line, ns2) == 0 && (tunnel->ipv4.ns2_was_there == 0)) { - log_debug("Deleting \"%s\" from /etc/resolv.conf.\n", ns2); + log_debug("Deleting \"%s\" from " RESOLV_CONF_FILE_PATH ".\n", + ns2); } else if (dns_suffix[0] != '\0' && strcmp(line, dns_suffix) == 0 && (tunnel->ipv4.dns_suffix_was_there == 0)) { - log_debug("Deleting \"%s\" from /etc/resolv.conf.\n", dns_suffix); + log_debug("Deleting \"%s\" from " RESOLV_CONF_FILE_PATH ".\n", + dns_suffix); } else { fputs(line, file); fputs("\n", file); @@ -1396,7 +1404,7 @@ int ipv4_del_nameservers_from_resolv_conf(struct tunnel *tunnel) free(buffer); err_close: if (file && fclose(file)) - log_warn("Could not close /etc/resolv.conf (%s).\n", + log_warn("Could not close " RESOLV_CONF_FILE_PATH " (%s).\n", strerror(errno)); return ret; } From f62c839d15503953c2ab64e350f706ba703db926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Obrembski?= Date: Mon, 18 Jan 2021 19:32:09 +0100 Subject: [PATCH 3/3] Added Samba libreplace implementation for freeifaddrs and getifaddrs In case if system doesn't provide above functions, don't fail and use Samba project libreplace implementation. This allows to run app on embedded systems. --- .github/workflows/openfortivpn.yml | 8 +- Makefile.am | 4 + configure.ac | 42 +- src/ifaddrs.c | 865 +++++++++++++++++++++++++++++ src/ifaddrs.h | 38 ++ src/ipv4.c | 2 +- src/tunnel.c | 17 +- tests/lint/run.sh | 8 +- 8 files changed, 969 insertions(+), 15 deletions(-) create mode 100644 src/ifaddrs.c create mode 100644 src/ifaddrs.h diff --git a/.github/workflows/openfortivpn.yml b/.github/workflows/openfortivpn.yml index de4afb16..e1f44eb7 100644 --- a/.github/workflows/openfortivpn.yml +++ b/.github/workflows/openfortivpn.yml @@ -21,16 +21,16 @@ jobs: run: sudo apt-get install -y astyle - name: Artistic Style - run: ./tests/lint/astyle.sh $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation) + run: ./tests/lint/astyle.sh $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation | grep -v ifaddrs) - name: Linux Kernel Coding Style - run: ./tests/lint/checkpatch.sh $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation) + run: ./tests/lint/checkpatch.sh $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation | grep -v ifaddrs) - name: EOL at EOF - run: ./tests/lint/eol-at-eof.sh $(git ls-files | grep -v openssl_hostname_validation) + run: ./tests/lint/eol-at-eof.sh $(git ls-files | grep -v openssl_hostname_validation | grep -v ifaddrs) - name: Line Length - run: ./tests/lint/line_length.py $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation) + run: ./tests/lint/line_length.py $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation | grep -v ifaddrs) build: name: Build diff --git a/Makefile.am b/Makefile.am index 810d007e..735e6aab 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,6 +8,10 @@ openfortivpn_SOURCES = src/config.c src/config.h src/hdlc.c src/hdlc.h \ src/xml.h src/userinput.c src/userinput.h \ src/openssl_hostname_validation.c \ src/openssl_hostname_validation.h +if LIBREPLACE_REQUIRED +openfortivpn_SOURCES += src/ifaddrs.c +endif +openfortivpn_CFLAGS = -Wall -pedantic openfortivpn_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" \ -DPPP_PATH=\"@PPP_PATH@\" \ -DNETSTAT_PATH=\"@NETSTAT_PATH@\" \ diff --git a/configure.ac b/configure.ac index 3851719f..bdad6952 100644 --- a/configure.ac +++ b/configure.ac @@ -103,8 +103,6 @@ AC_TYPE_UINT8_T AC_CHECK_TYPES([struct termios], [], [], [#include ]) # Checks for library functions. -AC_FUNC_MALLOC -AC_FUNC_REALLOC AC_CHECK_FUNCS([ \ access \ atoi \ @@ -125,7 +123,6 @@ fputs \ fread \ free \ freeaddrinfo \ -freeifaddrs \ freopen \ fwrite \ gai_strerror \ @@ -133,7 +130,6 @@ getaddrinfo \ getchar \ getenv \ geteuid \ -getifaddrs \ getopt_long \ htons \ index \ @@ -502,6 +498,44 @@ AC_ARG_WITH([resolvconf-config-file], AC_DEFINE_UNQUOTED([RESOLV_CONF_FILE_PATH],["$withval"]) ) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +#include +#if STDC_HEADERS +#include +#include +#endif +#include +#include +#include +#include +#include +int main(int argc, char **argv){ + struct ifaddrs *ifp = NULL; + int ret = getifaddrs (&ifp); + freeifaddrs(ifp); +} +])], [ + libreplace_required=no + AC_MSG_NOTICE([HAVE_IFADDRS... 1]) +],[ + libreplace_required=yes + AC_MSG_NOTICE([HAVE_IFADDRS... 0]) +]) + +AC_ARG_ENABLE([libreplace], + [AS_HELP_STRING([--enable-libreplace], [Force to use libreplace ifaddrs implementation])], + [enable_libreplace=yes], + [enable_libreplace=no]) +AS_CASE(["$enable_libreplace"], + [yes], [ + AC_MSG_NOTICE([Force using libreplace...]) + ], + [no], [ + ], + [AC_MSG_ERROR([unknown option '$enable_libreplace' for --enable-libreplace])]) +AS_IF([test "x$enable_libreplace" != "xno" -o "x$libreplace_required" != "xno"], [AC_DEFINE([HAVE_IFADDRS],[0])], [AC_DEFINE([HAVE_IFADDRS],[1])]) +AM_CONDITIONAL([LIBREPLACE_REQUIRED], [test "x$enable_libreplace" = "xyes" -o "x$libreplace_required" = "xyes"]) + # prepare to get rid of obsolete code (FortiOS 4) AC_ARG_ENABLE([obsolete], [AS_HELP_STRING([--disable-obsolete], [disable support for FortiOS 4])],, diff --git a/src/ifaddrs.c b/src/ifaddrs.c new file mode 100644 index 00000000..0858b352 --- /dev/null +++ b/src/ifaddrs.c @@ -0,0 +1,865 @@ +/* $Id: ifaddrs.c 241182 2011-02-17 21:50:03Z $ */ +/* from USAGI: ifaddrs.c,v 1.20.2.1 2002/12/08 08:22:23 yoshfuji Exp */ + +/* + * Copyright (C)2000 YOSHIFUJI Hideaki + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#define __set_errno(x) errno = (x) + +#include +#include +#include +#include +#include +#include +#include +#include /* the L2 protocols */ +#include +#include +#include +#include "ifaddrs.h" +#include + +#ifdef _USAGI_LIBINET6 +#include "libc-compat.h" +#endif + +/* ====================================================================== */ +struct nlmsg_list +{ + struct nlmsg_list *nlm_next; + struct nlmsghdr *nlh; + int size; + time_t seq; +}; + +struct rtmaddr_ifamap +{ + void *address; + void *local; +#ifdef IFA_NETMASK + void *netmask; +#endif + void *broadcast; +#ifdef HAVE_IFADDRS_IFA_ANYCAST + void *anycast; +#endif + int address_len; + int local_len; +#ifdef IFA_NETMASK + int netmask_len; +#endif + int broadcast_len; +#ifdef HAVE_IFADDRS_IFA_ANYCAST + int anycast_len; +#endif +}; + +/* ====================================================================== */ +static size_t +ifa_sa_len (sa_family_t family, int len) +{ + size_t size; + switch (family) + { + case AF_INET: + size = sizeof (struct sockaddr_in); + break; + case AF_INET6: + size = sizeof (struct sockaddr_in6); + break; + case AF_PACKET: + size = (size_t) (((struct sockaddr_ll *) NULL)->sll_addr) + len; + if (size < sizeof (struct sockaddr_ll)) + size = sizeof (struct sockaddr_ll); + break; + default: + size = (size_t) (((struct sockaddr *) NULL)->sa_data) + len; + if (size < sizeof (struct sockaddr)) + size = sizeof (struct sockaddr); + } + return size; +} + +static void +ifa_make_sockaddr (sa_family_t family, + struct sockaddr *sa, + void *p, size_t len, uint32_t scope, uint32_t scopeid) +{ + if (sa == NULL) + return; + switch (family) + { + case AF_INET: + memcpy (&((struct sockaddr_in *) sa)->sin_addr, (char *) p, len); + break; + case AF_INET6: + memcpy (&((struct sockaddr_in6 *) sa)->sin6_addr, (char *) p, len); + if (IN6_IS_ADDR_LINKLOCAL (p) || IN6_IS_ADDR_MC_LINKLOCAL (p)) + { + ((struct sockaddr_in6 *) sa)->sin6_scope_id = scopeid; + } + break; + case AF_PACKET: + memcpy (((struct sockaddr_ll *) sa)->sll_addr, (char *) p, len); + ((struct sockaddr_ll *) sa)->sll_halen = len; + break; + default: + memcpy (sa->sa_data, p, len); + break; + } + sa->sa_family = family; +#ifdef HAVE_SOCKADDR_SA_LEN + sa->sa_len = ifa_sa_len (family, len); +#endif +} + +static struct sockaddr * +ifa_make_sockaddr_mask (sa_family_t family, + struct sockaddr *sa, uint32_t prefixlen) +{ + int i; + char *p = NULL, c; + uint32_t max_prefixlen = 0; + + if (sa == NULL) + return NULL; + switch (family) + { + case AF_INET: + memset (&((struct sockaddr_in *) sa)->sin_addr, 0, + sizeof (((struct sockaddr_in *) sa)->sin_addr)); + p = (char *) &((struct sockaddr_in *) sa)->sin_addr; + max_prefixlen = 32; + break; + case AF_INET6: + memset (&((struct sockaddr_in6 *) sa)->sin6_addr, 0, + sizeof (((struct sockaddr_in6 *) sa)->sin6_addr)); + p = (char *) &((struct sockaddr_in6 *) sa)->sin6_addr; + max_prefixlen = 128; + break; + default: + return NULL; + } + sa->sa_family = family; +#ifdef HAVE_SOCKADDR_SA_LEN + sa->sa_len = ifa_sa_len (family, len); +#endif + if (p) + { + if (prefixlen > max_prefixlen) + prefixlen = max_prefixlen; + for (i = 0; i < (prefixlen / 8); i++) + *p++ = 0xff; + c = 0xff; + c <<= (8 - (prefixlen % 8)); + *p = c; + } + return sa; +} + +/* ====================================================================== */ +static int +nl_sendreq (int sd, int request, int flags, int *seq) +{ + char reqbuf[NLMSG_ALIGN (sizeof (struct nlmsghdr)) + + NLMSG_ALIGN (sizeof (struct rtgenmsg))]; + struct sockaddr_nl nladdr; + struct nlmsghdr *req_hdr; + struct rtgenmsg *req_msg; + time_t t = time (NULL); + + if (seq) + *seq = t; + memset (&reqbuf, 0, sizeof (reqbuf)); + req_hdr = (struct nlmsghdr *) reqbuf; + req_msg = (struct rtgenmsg *) NLMSG_DATA (req_hdr); + req_hdr->nlmsg_len = NLMSG_LENGTH (sizeof (*req_msg)); + req_hdr->nlmsg_type = request; + req_hdr->nlmsg_flags = flags | NLM_F_REQUEST; + req_hdr->nlmsg_pid = 0; + req_hdr->nlmsg_seq = t; + req_msg->rtgen_family = AF_UNSPEC; + memset (&nladdr, 0, sizeof (nladdr)); + nladdr.nl_family = AF_NETLINK; + return (sendto (sd, (void *) req_hdr, req_hdr->nlmsg_len, 0, + (struct sockaddr *) &nladdr, sizeof (nladdr))); +} + +static int +nl_recvmsg (int sd, int request, int seq, + void *buf, size_t buflen, int *flags) +{ + struct msghdr msg; + struct iovec iov = { buf, buflen }; + struct sockaddr_nl nladdr; + int read_len; + + for (;;) + { + msg.msg_name = (void *) &nladdr; + msg.msg_namelen = sizeof (nladdr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + read_len = recvmsg (sd, &msg, 0); + if ((read_len < 0 && errno == EINTR) || (msg.msg_flags & MSG_TRUNC)) + continue; + if (flags) + *flags = msg.msg_flags; + break; + } + return read_len; +} + +static int +nl_getmsg (int sd, int request, int seq, pid_t pid, struct nlmsghdr **nlhp, int *done) +{ + struct nlmsghdr *nh; + size_t bufsize = 65536, lastbufsize = 0; + void *buff = NULL; + int result = 0, read_size; + int msg_flags; + for (;;) + { + void *newbuff = realloc (buff, bufsize); + if (newbuff == NULL || bufsize < lastbufsize) + { + result = -1; + break; + } + buff = newbuff; + result = read_size = + nl_recvmsg (sd, request, seq, buff, bufsize, &msg_flags); + if (read_size < 0 || (msg_flags & MSG_TRUNC)) + { + lastbufsize = bufsize; + bufsize *= 2; + continue; + } + if (read_size == 0) + break; + nh = (struct nlmsghdr *) buff; + for (nh = (struct nlmsghdr *) buff; + NLMSG_OK (nh, read_size); + nh = (struct nlmsghdr *) NLMSG_NEXT (nh, read_size)) + { + if (nh->nlmsg_pid != pid || nh->nlmsg_seq != seq) + continue; + if (nh->nlmsg_type == NLMSG_DONE) + { + (*done)++; + break; /* ok */ + } + if (nh->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA (nh); + result = -1; + if (nh->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr))) + __set_errno (EIO); + else + __set_errno (-nlerr->error); + break; + } + } + break; + } + if (result < 0) + if (buff) + { + int saved_errno = errno; + free (buff); + __set_errno (saved_errno); + } + *nlhp = (struct nlmsghdr *) buff; + return result; +} + +static int +nl_getlist (int sd, int seq, pid_t pid, + int request, + struct nlmsg_list **nlm_list, struct nlmsg_list **nlm_end) +{ + struct nlmsghdr *nlh = NULL; + int status; + int done = 0; + + status = nl_sendreq (sd, request, NLM_F_ROOT | NLM_F_MATCH, &seq); + if (status < 0) + return status; + if (seq == 0) + seq = (int) time (NULL); + while (!done) + { + status = nl_getmsg (sd, request, seq, pid, &nlh, &done); + if (status < 0) + return status; + if (nlh) + { + struct nlmsg_list *nlm_next = + (struct nlmsg_list *) malloc (sizeof (struct nlmsg_list)); + if (nlm_next == NULL) + { + int saved_errno = errno; + free (nlh); + __set_errno (saved_errno); + status = -1; + } + else + { + nlm_next->nlm_next = NULL; + nlm_next->nlh = (struct nlmsghdr *) nlh; + nlm_next->size = status; + nlm_next->seq = seq; + if (*nlm_list == NULL) + { + *nlm_list = nlm_next; + *nlm_end = nlm_next; + } + else + { + (*nlm_end)->nlm_next = nlm_next; + *nlm_end = nlm_next; + } + } + } + } + return status >= 0 ? seq : status; +} + +/* ---------------------------------------------------------------------- */ +static void +free_nlmsglist (struct nlmsg_list *nlm0) +{ + struct nlmsg_list *nlm, *nlm_next; + int saved_errno; + if (!nlm0) + return; + saved_errno = errno; + nlm = nlm0; + while (nlm) + { + if (nlm->nlh) + free (nlm->nlh); + nlm_next = nlm->nlm_next; + free(nlm); + nlm = nlm_next; + } + __set_errno (saved_errno); +} + +static void +free_data (void *data, void *ifdata) +{ + int saved_errno = errno; + if (data != NULL) + free (data); + if (ifdata != NULL) + free (ifdata); + __set_errno (saved_errno); +} + +/* ---------------------------------------------------------------------- */ +static void +nl_close (int sd) +{ + int saved_errno = errno; + if (sd >= 0) + close (sd); + __set_errno (saved_errno); +} + +/* ---------------------------------------------------------------------- */ +static int +nl_open (pid_t *pid) +{ + struct sockaddr_nl nladdr; + int sd; + + sd = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sd < 0) + return -1; + memset (&nladdr, 0, sizeof (nladdr)); + nladdr.nl_family = AF_NETLINK; + if (bind (sd, (struct sockaddr *) &nladdr, sizeof (nladdr)) < 0) + { + nl_close (sd); + return -1; + } + if (pid) + { + socklen_t len = sizeof(nladdr); + if (getsockname (sd, (struct sockaddr *) &nladdr, &len) < 0) + { + nl_close (sd); + return -1; + } + *pid = nladdr.nl_pid; + } + return sd; +} + +/* ====================================================================== */ +int +rep_getifaddrs (struct ifaddrs **ifap) +{ + int sd; + struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm; + /* - - - - - - - - - - - - - - - */ + int icnt; + size_t dlen, xlen, nlen; + uint32_t max_ifindex = 0; + + pid_t pid; + int seq; + int result; + int build; /* 0 or 1 */ + +/* ---------------------------------- */ + /* initialize */ + icnt = dlen = xlen = nlen = 0; + nlmsg_list = nlmsg_end = NULL; + + if (ifap) + *ifap = NULL; + +/* ---------------------------------- */ + /* open socket and bind */ + sd = nl_open (&pid); + if (sd < 0) + return -1; + +/* ---------------------------------- */ + /* gather info */ + if ((seq = nl_getlist (sd, 0, pid, RTM_GETLINK, &nlmsg_list, &nlmsg_end)) < 0) + { + free_nlmsglist (nlmsg_list); + nl_close (sd); + return -1; + } + if ((seq = nl_getlist (sd, seq + 1, pid, RTM_GETADDR, + &nlmsg_list, &nlmsg_end)) < 0) + { + free_nlmsglist (nlmsg_list); + nl_close (sd); + return -1; + } + +/* ---------------------------------- */ + /* Estimate size of result buffer and fill it */ + for (build = 0; build <= 1; build++) + { + struct ifaddrs *ifl = NULL, *ifa = NULL; + struct nlmsghdr *nlh, *nlh0; + void *data = NULL, *xdata = NULL, *ifdata = NULL; + char *ifname = NULL, **iflist = NULL; + uint16_t *ifflist = NULL; + struct rtmaddr_ifamap ifamap; + + if (build) + { + ifa = data = calloc (1, + NLMSG_ALIGN (sizeof (struct ifaddrs[icnt])) + + dlen + xlen + nlen); + ifdata = calloc (1, + NLMSG_ALIGN (sizeof (char *[max_ifindex + 1])) + + + NLMSG_ALIGN (sizeof (uint16_t[max_ifindex + 1]))); + if (ifap != NULL) + *ifap = (ifdata != NULL) ? ifa : NULL; + else + { + free_data (data, ifdata); + result = 0; + break; + } + if (data == NULL || ifdata == NULL) + { + free_data (data, ifdata); + result = -1; + break; + } + ifl = NULL; + data += NLMSG_ALIGN (sizeof (struct ifaddrs)) * icnt; + xdata = data + dlen; + ifname = xdata + xlen; + iflist = ifdata; + ifflist = + ((void *) iflist) + + NLMSG_ALIGN (sizeof (char *[max_ifindex + 1])); + } + + for (nlm = nlmsg_list; nlm; nlm = nlm->nlm_next) + { + int nlmlen = nlm->size; + if (!(nlh0 = nlm->nlh)) + continue; + for (nlh = nlh0; + NLMSG_OK (nlh, nlmlen); nlh = NLMSG_NEXT (nlh, nlmlen)) + { + struct ifinfomsg *ifim = NULL; + struct ifaddrmsg *ifam = NULL; + struct rtattr *rta; + + size_t nlm_struct_size = 0; + sa_family_t nlm_family = 0; + uint32_t nlm_scope = 0, nlm_index = 0; +#ifndef IFA_NETMASK + size_t sockaddr_size = 0; + uint32_t nlm_prefixlen = 0; +#endif + size_t rtasize; + + memset (&ifamap, 0, sizeof (ifamap)); + + /* check if the message is what we want */ + if (nlh->nlmsg_pid != pid || nlh->nlmsg_seq != nlm->seq) + continue; + if (nlh->nlmsg_type == NLMSG_DONE) + { + break; /* ok */ + } + switch (nlh->nlmsg_type) + { + case RTM_NEWLINK: + ifim = (struct ifinfomsg *) NLMSG_DATA (nlh); + nlm_struct_size = sizeof (*ifim); + nlm_family = ifim->ifi_family; + nlm_scope = 0; + nlm_index = ifim->ifi_index; + nlm_prefixlen = 0; + if (build) + ifflist[nlm_index] = ifa->ifa_flags = ifim->ifi_flags; + break; + case RTM_NEWADDR: + ifam = (struct ifaddrmsg *) NLMSG_DATA (nlh); + nlm_struct_size = sizeof (*ifam); + nlm_family = ifam->ifa_family; + nlm_scope = ifam->ifa_scope; + nlm_index = ifam->ifa_index; + nlm_prefixlen = ifam->ifa_prefixlen; + if (build) + ifa->ifa_flags = ifflist[nlm_index]; + break; + default: + continue; + } + + if (!build) + { + if (max_ifindex < nlm_index) + max_ifindex = nlm_index; + } + else + { + if (ifl != NULL) + ifl->ifa_next = ifa; + } + + rtasize = + NLMSG_PAYLOAD (nlh, nlmlen) - NLMSG_ALIGN (nlm_struct_size); + for (rta = + (struct rtattr *) (((char *) NLMSG_DATA (nlh)) + + NLMSG_ALIGN (nlm_struct_size)); + RTA_OK (rta, rtasize); rta = RTA_NEXT (rta, rtasize)) + { + struct sockaddr **sap = NULL; + void *rtadata = RTA_DATA (rta); + size_t rtapayload = RTA_PAYLOAD (rta); + socklen_t sa_len; + + switch (nlh->nlmsg_type) + { + case RTM_NEWLINK: + switch (rta->rta_type) + { + case IFLA_ADDRESS: + case IFLA_BROADCAST: + if (build) + { + sap = + (rta->rta_type == + IFLA_ADDRESS) ? &ifa->ifa_addr : &ifa-> + ifa_broadaddr; + *sap = (struct sockaddr *) data; + } + sa_len = ifa_sa_len (AF_PACKET, rtapayload); + if (rta->rta_type == IFLA_ADDRESS) + sockaddr_size = NLMSG_ALIGN (sa_len); + if (!build) + { + dlen += NLMSG_ALIGN (sa_len); + } + else + { + memset (*sap, 0, sa_len); + ifa_make_sockaddr (AF_PACKET, *sap, rtadata, + rtapayload, 0, 0); + ((struct sockaddr_ll *) *sap)->sll_ifindex = + nlm_index; + ((struct sockaddr_ll *) *sap)->sll_hatype = + ifim->ifi_type; + data += NLMSG_ALIGN (sa_len); + } + break; + case IFLA_IFNAME: /* Name of Interface */ + if (!build) + nlen += NLMSG_ALIGN (rtapayload + 1); + else + { + ifa->ifa_name = ifname; + if (iflist[nlm_index] == NULL) + iflist[nlm_index] = ifa->ifa_name; + strncpy (ifa->ifa_name, rtadata, rtapayload); + ifa->ifa_name[rtapayload] = '\0'; + ifname += NLMSG_ALIGN (rtapayload + 1); + } + break; + case IFLA_STATS: /* Statistics of Interface */ + if (!build) + xlen += NLMSG_ALIGN (rtapayload); + else + { + ifa->ifa_data = xdata; + memcpy (ifa->ifa_data, rtadata, rtapayload); + xdata += NLMSG_ALIGN (rtapayload); + } + break; + case IFLA_UNSPEC: + break; + case IFLA_MTU: + break; + case IFLA_LINK: + break; + case IFLA_QDISC: + break; + default: + ; + } + break; + case RTM_NEWADDR: + if (nlm_family == AF_PACKET) + break; + switch (rta->rta_type) + { + case IFA_ADDRESS: + ifamap.address = rtadata; + ifamap.address_len = rtapayload; + break; + case IFA_LOCAL: + ifamap.local = rtadata; + ifamap.local_len = rtapayload; + break; + case IFA_BROADCAST: + ifamap.broadcast = rtadata; + ifamap.broadcast_len = rtapayload; + break; +#ifdef HAVE_IFADDRS_IFA_ANYCAST + case IFA_ANYCAST: + ifamap.anycast = rtadata; + ifamap.anycast_len = rtapayload; + break; +#endif + case IFA_LABEL: + if (!build) + nlen += NLMSG_ALIGN (rtapayload + 1); + else + { + ifa->ifa_name = ifname; + if (iflist[nlm_index] == NULL) + iflist[nlm_index] = ifname; + strncpy (ifa->ifa_name, rtadata, rtapayload); + ifa->ifa_name[rtapayload] = '\0'; + ifname += NLMSG_ALIGN (rtapayload + 1); + } + break; + case IFA_UNSPEC: + break; + case IFA_CACHEINFO: + break; + default: + ; + } + } + } + if (nlh->nlmsg_type == RTM_NEWADDR && nlm_family != AF_PACKET) + { + if (!ifamap.local) + { + ifamap.local = ifamap.address; + ifamap.local_len = ifamap.address_len; + } + if (!ifamap.address) + { + ifamap.address = ifamap.local; + ifamap.address_len = ifamap.local_len; + } + if (ifamap.address_len != ifamap.local_len || + (ifamap.address != NULL && + memcmp (ifamap.address, ifamap.local, + ifamap.address_len))) + { + /* p2p; address is peer and local is ours */ + ifamap.broadcast = ifamap.address; + ifamap.broadcast_len = ifamap.address_len; + ifamap.address = ifamap.local; + ifamap.address_len = ifamap.local_len; + } + if (ifamap.address) + { +#ifndef IFA_NETMASK + sockaddr_size = + NLMSG_ALIGN (ifa_sa_len + (nlm_family, ifamap.address_len)); +#endif + if (!build) + dlen += + NLMSG_ALIGN (ifa_sa_len + (nlm_family, ifamap.address_len)); + else + { + ifa->ifa_addr = (struct sockaddr *) data; + ifa_make_sockaddr (nlm_family, ifa->ifa_addr, + ifamap.address, + ifamap.address_len, nlm_scope, + nlm_index); + data += + NLMSG_ALIGN (ifa_sa_len + (nlm_family, ifamap.address_len)); + } + } +#ifdef IFA_NETMASK + if (ifamap.netmask) + { + if (!build) + dlen += + NLMSG_ALIGN (ifa_sa_len + (nlm_family, ifamap.netmask_len)); + else + { + ifa->ifa_netmask = (struct sockaddr *) data; + ifa_make_sockaddr (nlm_family, ifa->ifa_netmask, + ifamap.netmask, + ifamap.netmask_len, nlm_scope, + nlm_index); + data += + NLMSG_ALIGN (ifa_sa_len + (nlm_family, ifamap.netmask_len)); + } + } +#endif + if (ifamap.broadcast) + { + if (!build) + dlen += + NLMSG_ALIGN (ifa_sa_len + (nlm_family, ifamap.broadcast_len)); + else + { + ifa->ifa_broadaddr = (struct sockaddr *) data; + ifa_make_sockaddr (nlm_family, ifa->ifa_broadaddr, + ifamap.broadcast, + ifamap.broadcast_len, nlm_scope, + nlm_index); + data += + NLMSG_ALIGN (ifa_sa_len + (nlm_family, ifamap.broadcast_len)); + } + } +#ifdef HAVE_IFADDRS_IFA_ANYCAST + if (ifamap.anycast) + { + if (!build) + dlen += + NLMSG_ALIGN (ifa_sa_len + (nlm_family, ifamap.anycast_len)); + else + { + ifa->ifa_anycast = (struct sockaddr *) data; + ifa_make_sockaddr (nlm_family, ifa->ifa_anyaddr, + ifamap.anycast, + ifamap.anycast_len, nlm_scope, + nlm_index); + data += + NLMSG_ALIGN (ifa_sa_len + (nlm_family, ifamap.anycast_len)); + } + } +#endif + } + if (!build) + { +#ifndef IFA_NETMASK + dlen += sockaddr_size; +#endif + icnt++; + } + else + { + if (ifa->ifa_name == NULL) + ifa->ifa_name = iflist[nlm_index]; +#ifndef IFA_NETMASK + if (ifa->ifa_addr && + ifa->ifa_addr->sa_family != AF_UNSPEC && + ifa->ifa_addr->sa_family != AF_PACKET) + { + ifa->ifa_netmask = (struct sockaddr *) data; + ifa_make_sockaddr_mask (ifa->ifa_addr->sa_family, + ifa->ifa_netmask, + nlm_prefixlen); + } + data += sockaddr_size; +#endif + ifl = ifa++; + } + } + } + if (!build) + { + if (icnt == 0 && (dlen + nlen + xlen == 0)) + { + if (ifap != NULL) + *ifap = NULL; + break; /* cannot found any addresses */ + } + } + else + free_data (NULL, ifdata); + } + +/* ---------------------------------- */ + /* Finalize */ + free_nlmsglist (nlmsg_list); + nl_close (sd); + return 0; +} + +/* ---------------------------------------------------------------------- */ +void +rep_freeifaddrs (struct ifaddrs *ifa) +{ + free (ifa); +} diff --git a/src/ifaddrs.h b/src/ifaddrs.h new file mode 100644 index 00000000..b1bbc4d8 --- /dev/null +++ b/src/ifaddrs.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 MichaƂ Obrembski + * + * 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 . + * + * In addition, as a special exception, the copyright holders give permission + * to link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects for all of the + * code used other than OpenSSL. If you modify file(s) with this exception, + * you may extend this exception to your version of the file(s), but you are + * not obligated to do so. If you do not wish to do so, delete this exception + * statement from your version. If you delete this exception statement from + * all source files in the program, then also delete it here. + */ + +#ifndef OPENFORTIVPN_IFADDRS_H +#define OPENFORTIVPN_IFADDRS_H + +/* Include original header with struct ifaddrs */ +#include + +extern void rep_freeifaddrs (struct ifaddrs *ifa); +extern int rep_getifaddrs(struct ifaddrs **ifap); + +#endif diff --git a/src/ipv4.c b/src/ipv4.c index c5f3ffcf..c5b3e6ba 100644 --- a/src/ipv4.c +++ b/src/ipv4.c @@ -46,7 +46,7 @@ static char show_route_buffer[SHOW_ROUTE_BUFFER_SIZE]; #define ERR_IPV4_PROC_NET_ROUTE -5 #ifndef RESOLV_CONF_FILE_PATH -#define RESOLV_CONF_FILE_PATH /etc/resolv.conf +#define RESOLV_CONF_FILE_PATH "/etc/resolv.conf" #endif static inline const char *err_ipv4_str(int code) diff --git a/src/tunnel.c b/src/tunnel.c index 445e85ac..45d6a32e 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -67,7 +67,9 @@ #include #include #include - +#if !HAVE_IFADDRS +#include "ifaddrs.h" +#endif struct ofv_varr { unsigned int cap; // current capacity @@ -503,8 +505,11 @@ int ppp_interface_is_up(struct tunnel *tunnel) struct ifaddrs *ifap, *ifa; log_debug("Got Address: %s\n", inet_ntoa(tunnel->ipv4.ip_addr)); - +#if HAVE_IFADDRS if (getifaddrs(&ifap)) { +#else + if (rep_getifaddrs(&ifap)) { +#endif log_error("getifaddrs: %s\n", strerror(errno)); return 0; } @@ -532,13 +537,21 @@ int ppp_interface_is_up(struct tunnel *tunnel) if (tunnel->ipv4.ip_addr.s_addr == if_ip_addr.s_addr) { strncpy(tunnel->ppp_iface, ifa->ifa_name, ROUTE_IFACE_LEN - 1); +#if HAVE_IFADDRS freeifaddrs(ifap); +#else + rep_freeifaddrs(ifap); +#endif return 1; } } } } +#if HAVE_IFADDRS freeifaddrs(ifap); +#else + rep_freeifaddrs(ifap); +#endif return 0; } diff --git a/tests/lint/run.sh b/tests/lint/run.sh index 2e2e574a..bdd4c133 100755 --- a/tests/lint/run.sh +++ b/tests/lint/run.sh @@ -3,12 +3,12 @@ rc=0 -./tests/lint/eol-at-eof.sh $(git ls-files | grep -v openssl_hostname_validation) || rc=1 +./tests/lint/eol-at-eof.sh $(git ls-files | grep -v openssl_hostname_validation | grep -v ifaddrs) || rc=1 -./tests/lint/line_length.py $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation) || rc=1 +./tests/lint/line_length.py $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation | grep -v ifaddrs) || rc=1 -./tests/lint/astyle.sh $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation) || rc=1 +./tests/lint/astyle.sh $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation | grep -v ifaddrs) || rc=1 -./tests/lint/checkpatch.sh $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation) || rc=1 +./tests/lint/checkpatch.sh $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation | grep -v ifaddrs) || rc=1 exit $rc