From: Frank Lichtenheld <fr...@lichtenheld.com>

This extracts a lot of the helper functions that do not
actually work on sockets, but instead on addresses or
similar.

This includes

- openvpn_getaddrinfo and related functions
- print_*
- setenv_*

And lots of the inline functions.

This move will make it easier to add unit tests
for these moved functions.

Change-Id: I7393459b975fb9b3e0a42743f58645f769d1be5a
Signed-off-by: Frank Lichtenheld <fr...@lichtenheld.com>
Acked-by: Gert Doering <g...@greenie.muc.de>
---

This change was reviewed on Gerrit and approved by at least one
developer. I request to merge it to master.

Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1159
This mail reflects revision 1 of this Change.

Acked-by according to Gerrit (reflected above):
Gert Doering <g...@greenie.muc.de>

        
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3866e21..45044af 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -555,6 +555,8 @@
     src/openvpn/sig.h
     src/openvpn/socket.c
     src/openvpn/socket.h
+    src/openvpn/socket_util.c
+    src/openvpn/socket_util.h
     src/openvpn/socks.c
     src/openvpn/socks.h
     src/openvpn/ssl.c
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 1e17c3f..217f897 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -128,6 +128,7 @@
        shaper.c shaper.h \
        sig.c sig.h \
        socket.c socket.h \
+       socket_util.c socket_util.h \
        socks.c socks.h \
        ssl.c ssl.h  ssl_backend.h \
        ssl_openssl.c ssl_openssl.h \
diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c
index a303285..48a2057 100644
--- a/src/openvpn/clinat.c
+++ b/src/openvpn/clinat.c
@@ -28,7 +28,7 @@
 
 #include "clinat.h"
 #include "proto.h"
-#include "socket.h"
+#include "socket_util.h"
 #include "memdbg.h"
 
 static bool
diff --git a/src/openvpn/dhcp.c b/src/openvpn/dhcp.c
index 0a7689f..7abade5 100644
--- a/src/openvpn/dhcp.c
+++ b/src/openvpn/dhcp.c
@@ -27,7 +27,7 @@
 #include "syshead.h"
 
 #include "dhcp.h"
-#include "socket.h"
+#include "socket_util.h"
 #include "error.h"
 
 #include "memdbg.h"
diff --git a/src/openvpn/dns.c b/src/openvpn/dns.c
index 8e28c2b..8554089 100644
--- a/src/openvpn/dns.c
+++ b/src/openvpn/dns.c
@@ -27,7 +27,7 @@
 #include "syshead.h"
 
 #include "dns.h"
-#include "socket.h"
+#include "socket_util.h"
 #include "options.h"
 #include "run_command.h"
 
diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h
index 083caf5..b8892c8 100644
--- a/src/openvpn/manage.h
+++ b/src/openvpn/manage.h
@@ -48,7 +48,7 @@
 
 #include "misc.h"
 #include "event.h"
-#include "socket.h"
+#include "socket_util.h"
 #include "mroute.h"
 
 #define MANAGEMENT_VERSION                  5
diff --git a/src/openvpn/misc.c b/src/openvpn/misc.c
index 17f7706..59bf52b 100644
--- a/src/openvpn/misc.c
+++ b/src/openvpn/misc.c
@@ -72,27 +72,6 @@
 #endif
 }
 
-/*
- * Prepend a random string to hostname to prevent DNS caching.
- * For example, foo.bar.gov would be modified to <random-chars>.foo.bar.gov.
- * Of course, this requires explicit support in the DNS server (wildcard).
- */
-const char *
-hostname_randomize(const char *hostname, struct gc_arena *gc)
-{
-#define n_rnd_bytes 6
-
-    uint8_t rnd_bytes[n_rnd_bytes];
-    const char *rnd_str;
-    struct buffer hname = alloc_buf_gc(strlen(hostname) + sizeof(rnd_bytes) * 
2 + 4, gc);
-
-    prng_bytes(rnd_bytes, sizeof(rnd_bytes));
-    rnd_str = format_hex_ex(rnd_bytes, sizeof(rnd_bytes), 40, 0, NULL, gc);
-    buf_printf(&hname, "%s.%s", rnd_str, hostname);
-    return BSTR(&hname);
-#undef n_rnd_bytes
-}
-
 #ifdef ENABLE_MANAGEMENT
 /* Get username/password from the management interface */
 static bool
diff --git a/src/openvpn/misc.h b/src/openvpn/misc.h
index 1b10cd9..e87fd85 100644
--- a/src/openvpn/misc.h
+++ b/src/openvpn/misc.h
@@ -44,9 +44,6 @@
 
 const char **make_extended_arg_array(char **p, bool is_inline, struct gc_arena 
*gc);
 
-/* prepend a random prefix to hostname */
-const char *hostname_randomize(const char *hostname, struct gc_arena *gc);
-
 /*
  * Get and store a username/password
  */
diff --git a/src/openvpn/mroute.c b/src/openvpn/mroute.c
index a598037..ab01874 100644
--- a/src/openvpn/mroute.c
+++ b/src/openvpn/mroute.c
@@ -30,7 +30,7 @@
 #include "mroute.h"
 #include "proto.h"
 #include "error.h"
-#include "socket.h"
+#include "socket_util.h"
 
 #include "memdbg.h"
 
diff --git a/src/openvpn/mss.h b/src/openvpn/mss.h
index 05f12a7..bcbb5a8 100644
--- a/src/openvpn/mss.h
+++ b/src/openvpn/mss.h
@@ -26,6 +26,7 @@
 #include "proto.h"
 #include "error.h"
 #include "mtu.h"
+#include "socket.h"
 #include "ssl_common.h"
 
 void mss_fixup_ipv4(struct buffer *buf, uint16_t maxmss);
diff --git a/src/openvpn/networking_iproute2.c 
b/src/openvpn/networking_iproute2.c
index 0635b5d..e9be3a4 100644
--- a/src/openvpn/networking_iproute2.c
+++ b/src/openvpn/networking_iproute2.c
@@ -30,7 +30,7 @@
 #include "misc.h"
 #include "openvpn.h"
 #include "run_command.h"
-#include "socket.h"
+#include "socket_util.h"
 
 #include <stdbool.h>
 #include <netinet/in.h>
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 648d526..74946a4 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -45,7 +45,7 @@
 #include "ssl_ncp.h"
 #include "options.h"
 #include "misc.h"
-#include "socket.h"
+#include "socket_util.h"
 #include "packet_id.h"
 #include "pkcs11.h"
 #include "win32.h"
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index 4fa6800..44f3fc9 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -33,7 +33,7 @@
 #include "mtu.h"
 #include "route.h"
 #include "tun.h"
-#include "socket.h"
+#include "socket_util.h"
 #include "plugin.h"
 #include "manage.h"
 #include "proxy.h"
diff --git a/src/openvpn/pool.c b/src/openvpn/pool.c
index a41364a..fde6cea 100644
--- a/src/openvpn/pool.c
+++ b/src/openvpn/pool.c
@@ -29,7 +29,7 @@
 #include "pool.h"
 #include "buffer.h"
 #include "error.h"
-#include "socket.h"
+#include "socket_util.h"
 #include "otime.h"
 
 #include "memdbg.h"
diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c
index 8fc10bc..306170c 100644
--- a/src/openvpn/socket.c
+++ b/src/openvpn/socket.c
@@ -405,428 +405,6 @@
     throw_signal_soft(SIGHUP, "Preresolving failed");
 }
 
-/**
- * Small helper function for openvpn_getaddrinfo to print the address
- * family when resolving fails
- */
-static const char *
-getaddrinfo_addr_family_name(int af)
-{
-    switch (af)
-    {
-        case AF_INET:
-            return "[AF_INET]";
-
-        case AF_INET6:
-            return "[AF_INET6]";
-    }
-    return "";
-}
-
-/*
- * Translate IPv4/IPv6 addr or hostname into struct addrinfo
- * If resolve error, try again for resolve_retry_seconds seconds.
- */
-int
-openvpn_getaddrinfo(unsigned int flags, const char *hostname, const char 
*servname,
-                    int resolve_retry_seconds, struct signal_info *sig_info, 
int ai_family,
-                    struct addrinfo **res)
-{
-    struct addrinfo hints;
-    int status;
-    struct signal_info sigrec = { 0 };
-    int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS;
-    struct gc_arena gc = gc_new();
-    const char *print_hostname;
-    const char *print_servname;
-
-    ASSERT(res);
-
-    ASSERT(hostname || servname);
-    ASSERT(!(flags & GETADDR_HOST_ORDER));
-
-    if (servname)
-    {
-        print_servname = servname;
-    }
-    else
-    {
-        print_servname = "";
-    }
-
-    if (flags & GETADDR_MSG_VIRT_OUT)
-    {
-        msglevel |= M_MSG_VIRT_OUT;
-    }
-
-    if ((flags & (GETADDR_FATAL_ON_SIGNAL | GETADDR_WARN_ON_SIGNAL)) && 
!sig_info)
-    {
-        sig_info = &sigrec;
-    }
-
-    /* try numeric ip addr first */
-    CLEAR(hints);
-    hints.ai_flags = AI_NUMERICHOST;
-
-    if (flags & GETADDR_PASSIVE)
-    {
-        hints.ai_flags |= AI_PASSIVE;
-    }
-
-    if (flags & GETADDR_DATAGRAM)
-    {
-        hints.ai_socktype = SOCK_DGRAM;
-    }
-    else
-    {
-        hints.ai_socktype = SOCK_STREAM;
-    }
-
-    /* if hostname is not set, we want to bind to 'ANY', with
-     * the correct address family - v4-only or v6/v6-dual-stack */
-    if (!hostname)
-    {
-        hints.ai_family = ai_family;
-    }
-
-    status = getaddrinfo(hostname, servname, &hints, res);
-
-    if (status != 0)                      /* parse as numeric address failed? 
*/
-    {
-        const int fail_wait_interval = 5; /* seconds */
-        /* Add +4 to cause integer division rounding up (1 + 4) = 5, (0+4)/5=0 
*/
-        int resolve_retries =
-            (flags & GETADDR_TRY_ONCE) ? 1 : ((resolve_retry_seconds + 4) / 
fail_wait_interval);
-        const char *fmt;
-        int level = 0;
-
-        /* this is not a numeric IP, therefore force resolution using the
-         * provided ai_family */
-        hints.ai_family = ai_family;
-
-        if (hostname && (flags & GETADDR_RANDOMIZE))
-        {
-            hostname = hostname_randomize(hostname, &gc);
-        }
-
-        if (hostname)
-        {
-            print_hostname = hostname;
-        }
-        else
-        {
-            print_hostname = "undefined";
-        }
-
-        fmt = "RESOLVE: Cannot resolve host address: %s:%s%s (%s)";
-        if ((flags & GETADDR_MENTION_RESOLVE_RETRY) && !resolve_retry_seconds)
-        {
-            fmt = "RESOLVE: Cannot resolve host address: %s:%s%s (%s)"
-                  "(I would have retried this name query if you had "
-                  "specified the --resolv-retry option.)";
-        }
-
-        if (!(flags & GETADDR_RESOLVE) || status == EAI_FAIL)
-        {
-            msg(msglevel, "RESOLVE: Cannot parse IP address: %s:%s (%s)", 
print_hostname,
-                print_servname, gai_strerror(status));
-            goto done;
-        }
-
-#ifdef ENABLE_MANAGEMENT
-        if (flags & GETADDR_UPDATE_MANAGEMENT_STATE)
-        {
-            if (management)
-            {
-                management_set_state(management, OPENVPN_STATE_RESOLVE, NULL, 
NULL, NULL, NULL,
-                                     NULL);
-            }
-        }
-#endif
-
-        /*
-         * Resolve hostname
-         */
-        while (true)
-        {
-#ifndef _WIN32
-            /* force resolv.conf reload */
-            res_init();
-#endif
-            /* try hostname lookup */
-            hints.ai_flags &= ~AI_NUMERICHOST;
-            dmsg(D_SOCKET_DEBUG, "GETADDRINFO flags=0x%04x ai_family=%d 
ai_socktype=%d", flags,
-                 hints.ai_family, hints.ai_socktype);
-            status = getaddrinfo(hostname, servname, &hints, res);
-
-            if (sig_info)
-            {
-                get_signal(&sig_info->signal_received);
-                if (sig_info->signal_received) /* were we interrupted by a 
signal? */
-                {
-                    /* why are we overwriting SIGUSR1 ? */
-                    if (signal_reset(sig_info, SIGUSR1) == SIGUSR1) /* ignore 
SIGUSR1 */
-                    {
-                        msg(level, "RESOLVE: Ignored SIGUSR1 signal received 
during "
-                                   "DNS resolution attempt");
-                    }
-                    else
-                    {
-                        /* turn success into failure (interrupted syscall) */
-                        if (0 == status)
-                        {
-                            ASSERT(res);
-                            freeaddrinfo(*res);
-                            *res = NULL;
-                            status = EAI_AGAIN; /* = temporary failure */
-                            errno = EINTR;
-                        }
-                        goto done;
-                    }
-                }
-            }
-
-            /* success? */
-            if (0 == status)
-            {
-                break;
-            }
-
-            /* resolve lookup failed, should we
-             * continue or fail? */
-            level = msglevel;
-            if (resolve_retries > 0)
-            {
-                level = D_RESOLVE_ERRORS;
-            }
-
-            msg(level, fmt, print_hostname, print_servname, 
getaddrinfo_addr_family_name(ai_family),
-                gai_strerror(status));
-
-            if (--resolve_retries <= 0)
-            {
-                goto done;
-            }
-
-            management_sleep(fail_wait_interval);
-        }
-
-        ASSERT(res);
-
-        /* hostname resolve succeeded */
-
-        /*
-         * Do not choose an IP Addresse by random or change the order *
-         * of IP addresses, doing so will break RFC 3484 address selection *
-         */
-    }
-    else
-    {
-        /* IP address parse succeeded */
-        if (flags & GETADDR_RANDOMIZE)
-        {
-            msg(M_WARN, "WARNING: ignoring --remote-random-hostname because 
the "
-                        "hostname is an IP address");
-        }
-    }
-
-done:
-    if (sig_info && sig_info->signal_received)
-    {
-        int level = 0;
-        if (flags & GETADDR_FATAL_ON_SIGNAL)
-        {
-            level = M_FATAL;
-        }
-        else if (flags & GETADDR_WARN_ON_SIGNAL)
-        {
-            level = M_WARN;
-        }
-        msg(level, "RESOLVE: signal received during DNS resolution attempt");
-    }
-
-    gc_free(&gc);
-    return status;
-}
-
-/*
- * We do our own inet_aton because the glibc function
- * isn't very good about error checking.
- */
-int
-openvpn_inet_aton(const char *dotted_quad, struct in_addr *addr)
-{
-    unsigned int a, b, c, d;
-
-    CLEAR(*addr);
-    if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) == 4)
-    {
-        if (a < 256 && b < 256 && c < 256 && d < 256)
-        {
-            addr->s_addr = htonl(a << 24 | b << 16 | c << 8 | d);
-            return OIA_IP; /* good dotted quad */
-        }
-    }
-    if (string_class(dotted_quad, CC_DIGIT | CC_DOT, 0))
-    {
-        return OIA_ERROR; /* probably a badly formatted dotted quad */
-    }
-    else
-    {
-        return OIA_HOSTNAME; /* probably a hostname */
-    }
-}
-
-bool
-ip_addr_dotted_quad_safe(const char *dotted_quad)
-{
-    /* verify non-NULL */
-    if (!dotted_quad)
-    {
-        return false;
-    }
-
-    /* verify length is within limits */
-    if (strlen(dotted_quad) > 15)
-    {
-        return false;
-    }
-
-    /* verify that all chars are either numeric or '.' and that no numeric
-     * substring is greater than 3 chars */
-    {
-        int nnum = 0;
-        const char *p = dotted_quad;
-        int c;
-
-        while ((c = *p++))
-        {
-            if (c >= '0' && c <= '9')
-            {
-                ++nnum;
-                if (nnum > 3)
-                {
-                    return false;
-                }
-            }
-            else if (c == '.')
-            {
-                nnum = 0;
-            }
-            else
-            {
-                return false;
-            }
-        }
-    }
-
-    /* verify that string will convert to IP address */
-    {
-        struct in_addr a;
-        return openvpn_inet_aton(dotted_quad, &a) == OIA_IP;
-    }
-}
-
-bool
-ipv6_addr_safe(const char *ipv6_text_addr)
-{
-    /* verify non-NULL */
-    if (!ipv6_text_addr)
-    {
-        return false;
-    }
-
-    /* verify length is within limits */
-    if (strlen(ipv6_text_addr) > INET6_ADDRSTRLEN)
-    {
-        return false;
-    }
-
-    /* verify that string will convert to IPv6 address */
-    {
-        struct in6_addr a6;
-        return inet_pton(AF_INET6, ipv6_text_addr, &a6) == 1;
-    }
-}
-
-static bool
-dns_addr_safe(const char *addr)
-{
-    if (addr)
-    {
-        const size_t len = strlen(addr);
-        return len > 0 && len <= 255 && string_class(addr, CC_ALNUM | CC_DASH 
| CC_DOT, 0);
-    }
-    else
-    {
-        return false;
-    }
-}
-
-bool
-ip_or_dns_addr_safe(const char *addr, const bool allow_fqdn)
-{
-    if (ip_addr_dotted_quad_safe(addr))
-    {
-        return true;
-    }
-    else if (allow_fqdn)
-    {
-        return dns_addr_safe(addr);
-    }
-    else
-    {
-        return false;
-    }
-}
-
-bool
-mac_addr_safe(const char *mac_addr)
-{
-    /* verify non-NULL */
-    if (!mac_addr)
-    {
-        return false;
-    }
-
-    /* verify length is within limits */
-    if (strlen(mac_addr) > 17)
-    {
-        return false;
-    }
-
-    /* verify that all chars are either alphanumeric or ':' and that no
-     * alphanumeric substring is greater than 2 chars */
-    {
-        int nnum = 0;
-        const char *p = mac_addr;
-        int c;
-
-        while ((c = *p++))
-        {
-            if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' 
&& c <= 'F'))
-            {
-                ++nnum;
-                if (nnum > 2)
-                {
-                    return false;
-                }
-            }
-            else if (c == ':')
-            {
-                nnum = 0;
-            }
-            else
-            {
-                return false;
-            }
-        }
-    }
-
-    /* error-checking is left to script invoked in lladdr.c */
-    return true;
-}
-
 static int
 socket_get_sndbuf(socket_descriptor_t sd)
 {
@@ -2670,467 +2248,6 @@
 #endif
 }
 
-/*
- * Format IP addresses in ascii
- */
-
-const char *
-print_sockaddr_ex(const struct sockaddr *sa, const char *separator, const 
unsigned int flags,
-                  struct gc_arena *gc)
-{
-    struct buffer out = alloc_buf_gc(128, gc);
-    bool addr_is_defined = false;
-    char hostaddr[NI_MAXHOST] = "";
-    char servname[NI_MAXSERV] = "";
-    int status;
-
-    socklen_t salen = 0;
-    switch (sa->sa_family)
-    {
-        case AF_INET:
-            if (!(flags & PS_DONT_SHOW_FAMILY))
-            {
-                buf_puts(&out, "[AF_INET]");
-            }
-            salen = sizeof(struct sockaddr_in);
-            addr_is_defined = ((struct sockaddr_in *)sa)->sin_addr.s_addr != 0;
-            break;
-
-        case AF_INET6:
-            if (!(flags & PS_DONT_SHOW_FAMILY))
-            {
-                buf_puts(&out, "[AF_INET6]");
-            }
-            salen = sizeof(struct sockaddr_in6);
-            addr_is_defined = !IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 
*)sa)->sin6_addr);
-            break;
-
-        case AF_UNSPEC:
-            if (!(flags & PS_DONT_SHOW_FAMILY))
-            {
-                return "[AF_UNSPEC]";
-            }
-            else
-            {
-                return "";
-            }
-
-        default:
-            ASSERT(0);
-    }
-
-    status = getnameinfo(sa, salen, hostaddr, sizeof(hostaddr), servname, 
sizeof(servname),
-                         NI_NUMERICHOST | NI_NUMERICSERV);
-
-    if (status != 0)
-    {
-        buf_printf(&out, "[nameinfo() err: %s]", gai_strerror(status));
-        return BSTR(&out);
-    }
-
-    if (!(flags & PS_DONT_SHOW_ADDR))
-    {
-        if (addr_is_defined)
-        {
-            buf_puts(&out, hostaddr);
-        }
-        else
-        {
-            buf_puts(&out, "[undef]");
-        }
-    }
-
-    if ((flags & PS_SHOW_PORT) || (flags & PS_SHOW_PORT_IF_DEFINED))
-    {
-        if (separator)
-        {
-            buf_puts(&out, separator);
-        }
-
-        buf_puts(&out, servname);
-    }
-
-    return BSTR(&out);
-}
-
-const char *
-print_link_socket_actual(const struct link_socket_actual *act, struct gc_arena 
*gc)
-{
-    return print_link_socket_actual_ex(act, ":", PS_SHOW_PORT | 
PS_SHOW_PKTINFO, gc);
-}
-
-#ifndef IF_NAMESIZE
-#define IF_NAMESIZE 16
-#endif
-
-const char *
-print_link_socket_actual_ex(const struct link_socket_actual *act, const char 
*separator,
-                            const unsigned int flags, struct gc_arena *gc)
-{
-    if (act)
-    {
-        struct buffer out = alloc_buf_gc(128, gc);
-        buf_printf(&out, "%s", print_sockaddr_ex(&act->dest.addr.sa, 
separator, flags, gc));
-#if ENABLE_IP_PKTINFO
-        char ifname[IF_NAMESIZE] = "[undef]";
-
-        if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act))
-        {
-            switch (act->dest.addr.sa.sa_family)
-            {
-                case AF_INET:
-                {
-                    struct openvpn_sockaddr sa;
-                    CLEAR(sa);
-                    sa.addr.in4.sin_family = AF_INET;
-#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
-                    sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst;
-                    if_indextoname(act->pi.in4.ipi_ifindex, ifname);
-#elif defined(IP_RECVDSTADDR)
-                    sa.addr.in4.sin_addr = act->pi.in4;
-                    ifname[0] = 0;
-#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */
-#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix 
syshead.h)
-#endif
-                    buf_printf(&out, " (via %s%%%s)",
-                               print_sockaddr_ex(&sa.addr.sa, separator, 0, 
gc), ifname);
-                }
-                break;
-
-                case AF_INET6:
-                {
-                    struct sockaddr_in6 sin6;
-                    char buf[INET6_ADDRSTRLEN] = "[undef]";
-                    CLEAR(sin6);
-                    sin6.sin6_family = AF_INET6;
-                    sin6.sin6_addr = act->pi.in6.ipi6_addr;
-                    if_indextoname(act->pi.in6.ipi6_ifindex, ifname);
-                    if (getnameinfo((struct sockaddr *)&sin6, sizeof(struct 
sockaddr_in6), buf,
-                                    sizeof(buf), NULL, 0, NI_NUMERICHOST)
-                        == 0)
-                    {
-                        buf_printf(&out, " (via %s%%%s)", buf, ifname);
-                    }
-                    else
-                    {
-                        buf_printf(&out, " (via [getnameinfo() err]%%%s)", 
ifname);
-                    }
-                }
-                break;
-            }
-        }
-#endif /* if ENABLE_IP_PKTINFO */
-        return BSTR(&out);
-    }
-    else
-    {
-        return "[NULL]";
-    }
-}
-
-/*
- * Convert an in_addr_t in host byte order
- * to an ascii dotted quad.
- */
-const char *
-print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc)
-{
-    struct in_addr ia;
-    char *out = gc_malloc(INET_ADDRSTRLEN, true, gc);
-
-    if (addr || !(flags & IA_EMPTY_IF_UNDEF))
-    {
-        CLEAR(ia);
-        ia.s_addr = (flags & IA_NET_ORDER) ? addr : htonl(addr);
-
-        inet_ntop(AF_INET, &ia, out, INET_ADDRSTRLEN);
-    }
-    return out;
-}
-
-/*
- * Convert an in6_addr in host byte order
- * to an ascii representation of an IPv6 address
- */
-const char *
-print_in6_addr(struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
-{
-    char *out = gc_malloc(INET6_ADDRSTRLEN, true, gc);
-
-    if (memcmp(&a6, &in6addr_any, sizeof(a6)) != 0 || !(flags & 
IA_EMPTY_IF_UNDEF))
-    {
-        inet_ntop(AF_INET6, &a6, out, INET6_ADDRSTRLEN);
-    }
-    return out;
-}
-
-/*
- * Convert an in_port_t in host byte order to a string
- */
-const char *
-print_in_port_t(in_port_t port, struct gc_arena *gc)
-{
-    struct buffer buffer = alloc_buf_gc(8, gc);
-    buf_printf(&buffer, "%hu", port);
-    return BSTR(&buffer);
-}
-
-/* add some offset to an ipv6 address
- * (add in steps of 8 bits, taking overflow into next round)
- */
-struct in6_addr
-add_in6_addr(struct in6_addr base, uint32_t add)
-{
-    int i;
-
-    for (i = 15; i >= 0 && add > 0; i--)
-    {
-        register int carry;
-        register uint32_t h;
-
-        h = (unsigned char)base.s6_addr[i];
-        base.s6_addr[i] = (h + add) & UINT8_MAX;
-
-        /* using explicit carry for the 8-bit additions will catch
-         * 8-bit and(!) 32-bit overruns nicely
-         */
-        carry = ((h & 0xff) + (add & 0xff)) >> 8;
-        add = (add >> 8) + carry;
-    }
-    return base;
-}
-
-/* set environmental variables for ip/port in *addr */
-void
-setenv_sockaddr(struct env_set *es, const char *name_prefix, const struct 
openvpn_sockaddr *addr,
-                const unsigned int flags)
-{
-    char name_buf[256];
-
-    char buf[INET6_ADDRSTRLEN];
-    switch (addr->addr.sa.sa_family)
-    {
-        case AF_INET:
-            if (flags & SA_IP_PORT)
-            {
-                snprintf(name_buf, sizeof(name_buf), "%s_ip", name_prefix);
-            }
-            else
-            {
-                snprintf(name_buf, sizeof(name_buf), "%s", name_prefix);
-            }
-
-            inet_ntop(AF_INET, &addr->addr.in4.sin_addr, buf, sizeof(buf));
-            setenv_str(es, name_buf, buf);
-
-            if ((flags & SA_IP_PORT) && addr->addr.in4.sin_port)
-            {
-                snprintf(name_buf, sizeof(name_buf), "%s_port", name_prefix);
-                setenv_int(es, name_buf, ntohs(addr->addr.in4.sin_port));
-            }
-            break;
-
-        case AF_INET6:
-            if (IN6_IS_ADDR_V4MAPPED(&addr->addr.in6.sin6_addr))
-            {
-                struct in_addr ia;
-                memcpy(&ia.s_addr, &addr->addr.in6.sin6_addr.s6_addr[12], 
sizeof(ia.s_addr));
-                snprintf(name_buf, sizeof(name_buf), "%s_ip", name_prefix);
-                inet_ntop(AF_INET, &ia, buf, sizeof(buf));
-            }
-            else
-            {
-                snprintf(name_buf, sizeof(name_buf), "%s_ip6", name_prefix);
-                inet_ntop(AF_INET6, &addr->addr.in6.sin6_addr, buf, 
sizeof(buf));
-            }
-            setenv_str(es, name_buf, buf);
-
-            if ((flags & SA_IP_PORT) && addr->addr.in6.sin6_port)
-            {
-                snprintf(name_buf, sizeof(name_buf), "%s_port", name_prefix);
-                setenv_int(es, name_buf, ntohs(addr->addr.in6.sin6_port));
-            }
-            break;
-    }
-}
-
-void
-setenv_in_addr_t(struct env_set *es, const char *name_prefix, in_addr_t addr,
-                 const unsigned int flags)
-{
-    if (addr || !(flags & SA_SET_IF_NONZERO))
-    {
-        struct openvpn_sockaddr si;
-        CLEAR(si);
-        si.addr.in4.sin_family = AF_INET;
-        si.addr.in4.sin_addr.s_addr = htonl(addr);
-        setenv_sockaddr(es, name_prefix, &si, flags);
-    }
-}
-
-void
-setenv_in6_addr(struct env_set *es, const char *name_prefix, const struct 
in6_addr *addr,
-                const unsigned int flags)
-{
-    if (!IN6_IS_ADDR_UNSPECIFIED(addr) || !(flags & SA_SET_IF_NONZERO))
-    {
-        struct openvpn_sockaddr si;
-        CLEAR(si);
-        si.addr.in6.sin6_family = AF_INET6;
-        si.addr.in6.sin6_addr = *addr;
-        setenv_sockaddr(es, name_prefix, &si, flags);
-    }
-}
-
-void
-setenv_link_socket_actual(struct env_set *es, const char *name_prefix,
-                          const struct link_socket_actual *act, const unsigned 
int flags)
-{
-    setenv_sockaddr(es, name_prefix, &act->dest, flags);
-}
-
-/*
- * Convert protocol names between index and ascii form.
- */
-
-struct proto_names
-{
-    const char *short_form;
-    const char *display_form;
-    sa_family_t proto_af;
-    int proto;
-};
-
-/* Indexed by PROTO_x */
-static const struct proto_names proto_names[] = {
-    { "proto-uninitialized", "proto-NONE", AF_UNSPEC, PROTO_NONE },
-    /* try IPv4 and IPv6 (client), bind dual-stack (server) */
-    { "udp", "UDP", AF_UNSPEC, PROTO_UDP },
-    { "tcp-server", "TCP_SERVER", AF_UNSPEC, PROTO_TCP_SERVER },
-    { "tcp-client", "TCP_CLIENT", AF_UNSPEC, PROTO_TCP_CLIENT },
-    { "tcp", "TCP", AF_UNSPEC, PROTO_TCP },
-    /* force IPv4 */
-    { "udp4", "UDPv4", AF_INET, PROTO_UDP },
-    { "tcp4-server", "TCPv4_SERVER", AF_INET, PROTO_TCP_SERVER },
-    { "tcp4-client", "TCPv4_CLIENT", AF_INET, PROTO_TCP_CLIENT },
-    { "tcp4", "TCPv4", AF_INET, PROTO_TCP },
-    /* force IPv6 */
-    { "udp6", "UDPv6", AF_INET6, PROTO_UDP },
-    { "tcp6-server", "TCPv6_SERVER", AF_INET6, PROTO_TCP_SERVER },
-    { "tcp6-client", "TCPv6_CLIENT", AF_INET6, PROTO_TCP_CLIENT },
-    { "tcp6", "TCPv6", AF_INET6, PROTO_TCP },
-};
-
-int
-ascii2proto(const char *proto_name)
-{
-    for (size_t i = 0; i < SIZE(proto_names); ++i)
-    {
-        if (!strcmp(proto_name, proto_names[i].short_form))
-        {
-            return proto_names[i].proto;
-        }
-    }
-    return -1;
-}
-
-sa_family_t
-ascii2af(const char *proto_name)
-{
-    for (size_t i = 0; i < SIZE(proto_names); ++i)
-    {
-        if (!strcmp(proto_name, proto_names[i].short_form))
-        {
-            return proto_names[i].proto_af;
-        }
-    }
-    return 0;
-}
-
-const char *
-proto2ascii(int proto, sa_family_t af, bool display_form)
-{
-    for (size_t i = 0; i < SIZE(proto_names); ++i)
-    {
-        if (proto_names[i].proto_af == af && proto_names[i].proto == proto)
-        {
-            if (display_form)
-            {
-                return proto_names[i].display_form;
-            }
-            else
-            {
-                return proto_names[i].short_form;
-            }
-        }
-    }
-
-    return "[unknown protocol]";
-}
-
-const char *
-proto2ascii_all(struct gc_arena *gc)
-{
-    struct buffer out = alloc_buf_gc(256, gc);
-
-    for (size_t i = 0; i < SIZE(proto_names); ++i)
-    {
-        if (i)
-        {
-            buf_printf(&out, " ");
-        }
-        buf_printf(&out, "[%s]", proto_names[i].short_form);
-    }
-    return BSTR(&out);
-}
-
-const char *
-addr_family_name(int af)
-{
-    switch (af)
-    {
-        case AF_INET:
-            return "AF_INET";
-
-        case AF_INET6:
-            return "AF_INET6";
-    }
-    return "AF_UNSPEC";
-}
-
-/*
- * Given a local proto, return local proto
- * if !remote, or compatible remote proto
- * if remote.
- *
- * This is used for options compatibility
- * checking.
- *
- * IPv6 and IPv4 protocols are comptabile but OpenVPN
- * has always sent UDPv4, TCPv4 over the wire. Keep these
- * strings for backward compatibility
- */
-const char *
-proto_remote(int proto, bool remote)
-{
-    ASSERT(proto >= 0 && proto < PROTO_N);
-    if (proto == PROTO_UDP)
-    {
-        return "UDPv4";
-    }
-
-    if ((remote && proto == PROTO_TCP_CLIENT) || (!remote && proto == 
PROTO_TCP_SERVER))
-    {
-        return "TCPv4_SERVER";
-    }
-    if ((remote && proto == PROTO_TCP_SERVER) || (!remote && proto == 
PROTO_TCP_CLIENT))
-    {
-        return "TCPv4_CLIENT";
-    }
-
-    ASSERT(0);
-    return ""; /* Make the compiler happy */
-}
 
 /*
  * Bad incoming address lengths that differ from what
diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h
index ed37590..cce9183 100644
--- a/src/openvpn/socket.h
+++ b/src/openvpn/socket.h
@@ -34,6 +34,7 @@
 #include "socks.h"
 #include "misc.h"
 #include "tun.h"
+#include "socket_util.h"
 
 /*
  * OpenVPN's default port number as assigned by IANA.
@@ -60,18 +61,6 @@
 /* convert a packet_size_type from network to host order */
 #define ntohps(x) ntohs(x)
 
-/* OpenVPN sockaddr struct */
-struct openvpn_sockaddr
-{
-    /*int dummy;*/ /* add offset to force a bug if sa not explicitly 
dereferenced */
-    union
-    {
-        struct sockaddr sa;
-        struct sockaddr_in in4;
-        struct sockaddr_in6 in6;
-    } addr;
-};
-
 /* struct to hold preresolved host names */
 struct cached_dns_entry
 {
@@ -83,25 +72,6 @@
     struct cached_dns_entry *next;
 };
 
-/* actual address of remote, based on source address of received packets */
-struct link_socket_actual
-{
-    /*int dummy;*/ /* add offset to force a bug if dest not explicitly 
dereferenced */
-
-    struct openvpn_sockaddr dest;
-#if ENABLE_IP_PKTINFO
-    union
-    {
-#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
-        struct in_pktinfo in4;
-#elif defined(IP_RECVDSTADDR)
-        struct in_addr in4;
-#endif
-        struct in6_pktinfo in6;
-    } pi;
-#endif
-};
-
 /* IP addresses which are persistent across SIGUSR1s */
 struct link_socket_addr
 {
@@ -349,58 +319,6 @@
 
 void sd_close(socket_descriptor_t *sd);
 
-#define PS_SHOW_PORT_IF_DEFINED (1 << 0)
-#define PS_SHOW_PORT            (1 << 1)
-#define PS_SHOW_PKTINFO         (1 << 2)
-#define PS_DONT_SHOW_ADDR       (1 << 3)
-#define PS_DONT_SHOW_FAMILY     (1 << 4)
-
-const char *print_sockaddr_ex(const struct sockaddr *addr, const char 
*separator,
-                              const unsigned int flags, struct gc_arena *gc);
-
-static inline const char *
-print_openvpn_sockaddr(const struct openvpn_sockaddr *addr, struct gc_arena 
*gc)
-{
-    return print_sockaddr_ex(&addr->addr.sa, ":", PS_SHOW_PORT, gc);
-}
-
-static inline const char *
-print_sockaddr(const struct sockaddr *addr, struct gc_arena *gc)
-{
-    return print_sockaddr_ex(addr, ":", PS_SHOW_PORT, gc);
-}
-
-
-const char *print_link_socket_actual_ex(const struct link_socket_actual *act, 
const char *separator,
-                                        const unsigned int flags, struct 
gc_arena *gc);
-
-const char *print_link_socket_actual(const struct link_socket_actual *act, 
struct gc_arena *gc);
-
-
-#define IA_EMPTY_IF_UNDEF (1 << 0)
-#define IA_NET_ORDER      (1 << 1)
-const char *print_in_addr_t(in_addr_t addr, unsigned int flags, struct 
gc_arena *gc);
-
-const char *print_in6_addr(struct in6_addr addr6, unsigned int flags, struct 
gc_arena *gc);
-
-const char *print_in_port_t(in_port_t port, struct gc_arena *gc);
-
-struct in6_addr add_in6_addr(struct in6_addr base, uint32_t add);
-
-#define SA_IP_PORT        (1 << 0)
-#define SA_SET_IF_NONZERO (1 << 1)
-void setenv_sockaddr(struct env_set *es, const char *name_prefix,
-                     const struct openvpn_sockaddr *addr, const unsigned int 
flags);
-
-void setenv_in_addr_t(struct env_set *es, const char *name_prefix, in_addr_t 
addr,
-                      const unsigned int flags);
-
-void setenv_in6_addr(struct env_set *es, const char *name_prefix, const struct 
in6_addr *addr,
-                     const unsigned int flags);
-
-void setenv_link_socket_actual(struct env_set *es, const char *name_prefix,
-                               const struct link_socket_actual *act, const 
unsigned int flags);
-
 void bad_address_length(int actual, int expected);
 
 /* IPV4_INVALID_ADDR: returned by link_socket_current_remote()
@@ -432,21 +350,6 @@
  * Low-level functions
  */
 
-/* return values of openvpn_inet_aton */
-#define OIA_HOSTNAME 0
-#define OIA_IP       1
-#define OIA_ERROR    -1
-int openvpn_inet_aton(const char *dotted_quad, struct in_addr *addr);
-
-/* integrity validation on pulled options */
-bool ip_addr_dotted_quad_safe(const char *dotted_quad);
-
-bool ip_or_dns_addr_safe(const char *addr, const bool allow_fqdn);
-
-bool mac_addr_safe(const char *mac_addr);
-
-bool ipv6_addr_safe(const char *ipv6_text_addr);
-
 socket_descriptor_t create_socket_tcp(struct addrinfo *);
 
 socket_descriptor_t socket_do_accept(socket_descriptor_t sd, struct 
link_socket_actual *act,
@@ -472,134 +375,6 @@
 
 #endif /* if UNIX_SOCK_SUPPORT */
 
-/*
- * DNS resolution
- */
-
-#define GETADDR_RESOLVE                 (1 << 0)
-#define GETADDR_FATAL                   (1 << 1)
-#define GETADDR_HOST_ORDER              (1 << 2)
-#define GETADDR_MENTION_RESOLVE_RETRY   (1 << 3)
-#define GETADDR_FATAL_ON_SIGNAL         (1 << 4)
-#define GETADDR_WARN_ON_SIGNAL          (1 << 5)
-#define GETADDR_MSG_VIRT_OUT            (1 << 6)
-#define GETADDR_TRY_ONCE                (1 << 7)
-#define GETADDR_UPDATE_MANAGEMENT_STATE (1 << 8)
-#define GETADDR_RANDOMIZE               (1 << 9)
-#define GETADDR_PASSIVE                 (1 << 10)
-#define GETADDR_DATAGRAM                (1 << 11)
-
-#define GETADDR_CACHE_MASK (GETADDR_DATAGRAM | GETADDR_PASSIVE)
-
-/**
- * Translate an IPv4 addr or hostname from string form to in_addr_t
- *
- * In case of resolve error, it will try again for
- * resolve_retry_seconds seconds.
- */
-in_addr_t getaddr(unsigned int flags, const char *hostname, int 
resolve_retry_seconds,
-                  bool *succeeded, struct signal_info *sig_info);
-
-/**
- * Translate an IPv6 addr or hostname from string form to in6_addr
- */
-bool get_ipv6_addr(const char *hostname, struct in6_addr *network, unsigned 
int *netbits,
-                   int msglevel);
-
-int openvpn_getaddrinfo(unsigned int flags, const char *hostname, const char 
*servname,
-                        int resolve_retry_seconds, struct signal_info 
*sig_info, int ai_family,
-                        struct addrinfo **res);
-
-/*
- * Transport protocol naming and other details.
- */
-
-/*
- * Use enum's instead of #define to allow for easier
- * optional proto support
- */
-enum proto_num
-{
-    PROTO_NONE, /* catch for uninitialized */
-    PROTO_UDP,
-    PROTO_TCP,
-    PROTO_TCP_SERVER,
-    PROTO_TCP_CLIENT,
-    PROTO_N
-};
-
-static inline bool
-proto_is_net(int proto)
-{
-    ASSERT(proto >= 0 && proto < PROTO_N);
-    return proto != PROTO_NONE;
-}
-
-/**
- * @brief Returns if the protocol being used is UDP
- */
-static inline bool
-proto_is_udp(int proto)
-{
-    ASSERT(proto >= 0 && proto < PROTO_N);
-    return proto == PROTO_UDP;
-}
-
-/**
- * @brief Return if the protocol is datagram (UDP)
- *
- */
-static inline bool
-proto_is_dgram(int proto)
-{
-    return proto_is_udp(proto);
-}
-
-/**
- * @brief returns if the proto is a TCP variant (tcp-server, tcp-client or tcp)
- */
-static inline bool
-proto_is_tcp(int proto)
-{
-    ASSERT(proto >= 0 && proto < PROTO_N);
-    return proto == PROTO_TCP_CLIENT || proto == PROTO_TCP_SERVER;
-}
-
-
-int ascii2proto(const char *proto_name);
-
-sa_family_t ascii2af(const char *proto_name);
-
-const char *proto2ascii(int proto, sa_family_t af, bool display_form);
-
-const char *proto2ascii_all(struct gc_arena *gc);
-
-const char *proto_remote(int proto, bool remote);
-
-const char *addr_family_name(int af);
-
-/*
- * Overhead added to packets by various protocols.
- */
-static inline int
-datagram_overhead(sa_family_t af, int proto)
-{
-    int overhead = 0;
-    overhead += (proto == PROTO_UDP) ? 8 : 20;
-    overhead += (af == AF_INET) ? 20 : 40;
-    return overhead;
-}
-
-/*
- * Misc inline functions
- */
-
-static inline bool
-link_socket_proto_connection_oriented(int proto)
-{
-    return !proto_is_dgram(proto);
-}
-
 static inline bool
 link_socket_connection_oriented(const struct link_socket *sock)
 {
@@ -613,242 +388,6 @@
     }
 }
 
-static inline bool
-addr_defined(const struct openvpn_sockaddr *addr)
-{
-    if (!addr)
-    {
-        return 0;
-    }
-    switch (addr->addr.sa.sa_family)
-    {
-        case AF_INET:
-            return addr->addr.in4.sin_addr.s_addr != 0;
-
-        case AF_INET6:
-            return !IN6_IS_ADDR_UNSPECIFIED(&addr->addr.in6.sin6_addr);
-
-        default:
-            return 0;
-    }
-}
-
-static inline bool
-addr_local(const struct sockaddr *addr)
-{
-    if (!addr)
-    {
-        return false;
-    }
-    switch (addr->sa_family)
-    {
-        case AF_INET:
-            return ((const struct sockaddr_in *)addr)->sin_addr.s_addr == 
htonl(INADDR_LOOPBACK);
-
-        case AF_INET6:
-            return IN6_IS_ADDR_LOOPBACK(&((const struct sockaddr_in6 
*)addr)->sin6_addr);
-
-        default:
-            return false;
-    }
-}
-
-
-static inline bool
-addr_defined_ipi(const struct link_socket_actual *lsa)
-{
-#if ENABLE_IP_PKTINFO
-    if (!lsa)
-    {
-        return 0;
-    }
-    switch (lsa->dest.addr.sa.sa_family)
-    {
-#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
-        case AF_INET:
-            return lsa->pi.in4.ipi_spec_dst.s_addr != 0;
-
-#elif defined(IP_RECVDSTADDR)
-        case AF_INET:
-            return lsa->pi.in4.s_addr != 0;
-
-#endif
-        case AF_INET6:
-            return !IN6_IS_ADDR_UNSPECIFIED(&lsa->pi.in6.ipi6_addr);
-
-        default:
-            return 0;
-    }
-#else /* if ENABLE_IP_PKTINFO */
-    ASSERT(0);
-#endif
-    return false;
-}
-
-static inline bool
-link_socket_actual_defined(const struct link_socket_actual *act)
-{
-    return act && addr_defined(&act->dest);
-}
-
-static inline bool
-addr_match(const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr 
*a2)
-{
-    switch (a1->addr.sa.sa_family)
-    {
-        case AF_INET:
-            return a1->addr.in4.sin_addr.s_addr == 
a2->addr.in4.sin_addr.s_addr;
-
-        case AF_INET6:
-            return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, 
&a2->addr.in6.sin6_addr);
-    }
-    ASSERT(0);
-    return false;
-}
-
-static inline bool
-addrlist_match(const struct openvpn_sockaddr *a1, const struct addrinfo 
*addrlist)
-{
-    const struct addrinfo *curele;
-    for (curele = addrlist; curele; curele = curele->ai_next)
-    {
-        switch (a1->addr.sa.sa_family)
-        {
-            case AF_INET:
-                if (a1->addr.in4.sin_addr.s_addr
-                    == ((struct sockaddr_in 
*)curele->ai_addr)->sin_addr.s_addr)
-                {
-                    return true;
-                }
-                break;
-
-            case AF_INET6:
-                if (IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr,
-                                       &((struct sockaddr_in6 
*)curele->ai_addr)->sin6_addr))
-                {
-                    return true;
-                }
-                break;
-
-            default:
-                ASSERT(0);
-        }
-    }
-    return false;
-}
-
-static inline bool
-addrlist_port_match(const struct openvpn_sockaddr *a1, const struct addrinfo 
*a2)
-{
-    const struct addrinfo *curele;
-    for (curele = a2; curele; curele = curele->ai_next)
-    {
-        switch (a1->addr.sa.sa_family)
-        {
-            case AF_INET:
-                if (curele->ai_family == AF_INET
-                    && a1->addr.in4.sin_addr.s_addr
-                           == ((struct sockaddr_in 
*)curele->ai_addr)->sin_addr.s_addr
-                    && a1->addr.in4.sin_port == ((struct sockaddr_in 
*)curele->ai_addr)->sin_port)
-                {
-                    return true;
-                }
-                break;
-
-            case AF_INET6:
-                if (curele->ai_family == AF_INET6
-                    && IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr,
-                                          &((struct sockaddr_in6 
*)curele->ai_addr)->sin6_addr)
-                    && a1->addr.in6.sin6_port
-                           == ((struct sockaddr_in6 
*)curele->ai_addr)->sin6_port)
-                {
-                    return true;
-                }
-                break;
-
-            default:
-                ASSERT(0);
-        }
-    }
-    return false;
-}
-
-
-static inline bool
-addr_port_match(const struct openvpn_sockaddr *a1, const struct 
openvpn_sockaddr *a2)
-{
-    switch (a1->addr.sa.sa_family)
-    {
-        case AF_INET:
-            return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr
-                   && a1->addr.in4.sin_port == a2->addr.in4.sin_port;
-
-        case AF_INET6:
-            return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, 
&a2->addr.in6.sin6_addr)
-                   && a1->addr.in6.sin6_port == a2->addr.in6.sin6_port;
-    }
-    ASSERT(0);
-    return false;
-}
-
-static inline bool
-addr_match_proto(const struct openvpn_sockaddr *a1, const struct 
openvpn_sockaddr *a2,
-                 const int proto)
-{
-    return link_socket_proto_connection_oriented(proto) ? addr_match(a1, a2)
-                                                        : addr_port_match(a1, 
a2);
-}
-
-
-static inline bool
-addrlist_match_proto(const struct openvpn_sockaddr *a1, struct addrinfo 
*addr_list, const int proto)
-{
-    return link_socket_proto_connection_oriented(proto) ? addrlist_match(a1, 
addr_list)
-                                                        : 
addrlist_port_match(a1, addr_list);
-}
-
-static inline void
-addr_zero_host(struct openvpn_sockaddr *addr)
-{
-    switch (addr->addr.sa.sa_family)
-    {
-        case AF_INET:
-            addr->addr.in4.sin_addr.s_addr = 0;
-            break;
-
-        case AF_INET6:
-            memset(&addr->addr.in6.sin6_addr, 0, sizeof(struct in6_addr));
-            break;
-    }
-}
-
-static inline int
-af_addr_size(sa_family_t af)
-{
-    switch (af)
-    {
-        case AF_INET:
-            return sizeof(struct sockaddr_in);
-
-        case AF_INET6:
-            return sizeof(struct sockaddr_in6);
-
-        default:
-#if 0
-            /* could be called from socket_do_accept() with empty addr */
-            msg(M_ERR, "Bad address family: %d\n", af);
-            ASSERT(0);
-#endif
-            return 0;
-    }
-}
-
-static inline bool
-link_socket_actual_match(const struct link_socket_actual *a1, const struct 
link_socket_actual *a2)
-{
-    return addr_port_match(&a1->dest, &a2->dest);
-}
-
 #if PORT_SHARE
 
 static inline bool
diff --git a/src/openvpn/socket_util.c b/src/openvpn/socket_util.c
new file mode 100644
index 0000000..13f8c32
--- /dev/null
+++ b/src/openvpn/socket_util.c
@@ -0,0 +1,936 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2025 OpenVPN Inc <sa...@openvpn.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "syshead.h"
+
+#include "socket_util.h"
+#include "crypto.h"
+#include "manage.h"
+
+/*
+ * Format IP addresses in ascii
+ */
+
+const char *
+print_sockaddr_ex(const struct sockaddr *sa, const char *separator, const 
unsigned int flags,
+                  struct gc_arena *gc)
+{
+    struct buffer out = alloc_buf_gc(128, gc);
+    bool addr_is_defined = false;
+    char hostaddr[NI_MAXHOST] = "";
+    char servname[NI_MAXSERV] = "";
+    int status;
+
+    socklen_t salen = 0;
+    switch (sa->sa_family)
+    {
+        case AF_INET:
+            if (!(flags & PS_DONT_SHOW_FAMILY))
+            {
+                buf_puts(&out, "[AF_INET]");
+            }
+            salen = sizeof(struct sockaddr_in);
+            addr_is_defined = ((struct sockaddr_in *)sa)->sin_addr.s_addr != 0;
+            break;
+
+        case AF_INET6:
+            if (!(flags & PS_DONT_SHOW_FAMILY))
+            {
+                buf_puts(&out, "[AF_INET6]");
+            }
+            salen = sizeof(struct sockaddr_in6);
+            addr_is_defined = !IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 
*)sa)->sin6_addr);
+            break;
+
+        case AF_UNSPEC:
+            if (!(flags & PS_DONT_SHOW_FAMILY))
+            {
+                return "[AF_UNSPEC]";
+            }
+            else
+            {
+                return "";
+            }
+
+        default:
+            ASSERT(0);
+    }
+
+    status = getnameinfo(sa, salen, hostaddr, sizeof(hostaddr), servname, 
sizeof(servname),
+                         NI_NUMERICHOST | NI_NUMERICSERV);
+
+    if (status != 0)
+    {
+        buf_printf(&out, "[nameinfo() err: %s]", gai_strerror(status));
+        return BSTR(&out);
+    }
+
+    if (!(flags & PS_DONT_SHOW_ADDR))
+    {
+        if (addr_is_defined)
+        {
+            buf_puts(&out, hostaddr);
+        }
+        else
+        {
+            buf_puts(&out, "[undef]");
+        }
+    }
+
+    if ((flags & PS_SHOW_PORT) || (flags & PS_SHOW_PORT_IF_DEFINED))
+    {
+        if (separator)
+        {
+            buf_puts(&out, separator);
+        }
+
+        buf_puts(&out, servname);
+    }
+
+    return BSTR(&out);
+}
+
+const char *
+print_link_socket_actual(const struct link_socket_actual *act, struct gc_arena 
*gc)
+{
+    return print_link_socket_actual_ex(act, ":", PS_SHOW_PORT | 
PS_SHOW_PKTINFO, gc);
+}
+
+#ifndef IF_NAMESIZE
+#define IF_NAMESIZE 16
+#endif
+
+const char *
+print_link_socket_actual_ex(const struct link_socket_actual *act, const char 
*separator,
+                            const unsigned int flags, struct gc_arena *gc)
+{
+    if (act)
+    {
+        struct buffer out = alloc_buf_gc(128, gc);
+        buf_printf(&out, "%s", print_sockaddr_ex(&act->dest.addr.sa, 
separator, flags, gc));
+#if ENABLE_IP_PKTINFO
+        char ifname[IF_NAMESIZE] = "[undef]";
+
+        if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act))
+        {
+            switch (act->dest.addr.sa.sa_family)
+            {
+                case AF_INET:
+                {
+                    struct openvpn_sockaddr sa;
+                    CLEAR(sa);
+                    sa.addr.in4.sin_family = AF_INET;
+#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
+                    sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst;
+                    if_indextoname(act->pi.in4.ipi_ifindex, ifname);
+#elif defined(IP_RECVDSTADDR)
+                    sa.addr.in4.sin_addr = act->pi.in4;
+                    ifname[0] = 0;
+#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */
+#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix 
syshead.h)
+#endif
+                    buf_printf(&out, " (via %s%%%s)",
+                               print_sockaddr_ex(&sa.addr.sa, separator, 0, 
gc), ifname);
+                }
+                break;
+
+                case AF_INET6:
+                {
+                    struct sockaddr_in6 sin6;
+                    char buf[INET6_ADDRSTRLEN] = "[undef]";
+                    CLEAR(sin6);
+                    sin6.sin6_family = AF_INET6;
+                    sin6.sin6_addr = act->pi.in6.ipi6_addr;
+                    if_indextoname(act->pi.in6.ipi6_ifindex, ifname);
+                    if (getnameinfo((struct sockaddr *)&sin6, sizeof(struct 
sockaddr_in6), buf,
+                                    sizeof(buf), NULL, 0, NI_NUMERICHOST)
+                        == 0)
+                    {
+                        buf_printf(&out, " (via %s%%%s)", buf, ifname);
+                    }
+                    else
+                    {
+                        buf_printf(&out, " (via [getnameinfo() err]%%%s)", 
ifname);
+                    }
+                }
+                break;
+            }
+        }
+#endif /* if ENABLE_IP_PKTINFO */
+        return BSTR(&out);
+    }
+    else
+    {
+        return "[NULL]";
+    }
+}
+
+/*
+ * Convert an in_addr_t in host byte order
+ * to an ascii dotted quad.
+ */
+const char *
+print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc)
+{
+    struct in_addr ia;
+    char *out = gc_malloc(INET_ADDRSTRLEN, true, gc);
+
+    if (addr || !(flags & IA_EMPTY_IF_UNDEF))
+    {
+        CLEAR(ia);
+        ia.s_addr = (flags & IA_NET_ORDER) ? addr : htonl(addr);
+
+        inet_ntop(AF_INET, &ia, out, INET_ADDRSTRLEN);
+    }
+    return out;
+}
+
+/*
+ * Convert an in6_addr in host byte order
+ * to an ascii representation of an IPv6 address
+ */
+const char *
+print_in6_addr(struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
+{
+    char *out = gc_malloc(INET6_ADDRSTRLEN, true, gc);
+
+    if (memcmp(&a6, &in6addr_any, sizeof(a6)) != 0 || !(flags & 
IA_EMPTY_IF_UNDEF))
+    {
+        inet_ntop(AF_INET6, &a6, out, INET6_ADDRSTRLEN);
+    }
+    return out;
+}
+
+/*
+ * Convert an in_port_t in host byte order to a string
+ */
+const char *
+print_in_port_t(in_port_t port, struct gc_arena *gc)
+{
+    struct buffer buffer = alloc_buf_gc(8, gc);
+    buf_printf(&buffer, "%hu", port);
+    return BSTR(&buffer);
+}
+
+/* add some offset to an ipv6 address
+ * (add in steps of 8 bits, taking overflow into next round)
+ */
+struct in6_addr
+add_in6_addr(struct in6_addr base, uint32_t add)
+{
+    int i;
+
+    for (i = 15; i >= 0 && add > 0; i--)
+    {
+        register int carry;
+        register uint32_t h;
+
+        h = (unsigned char)base.s6_addr[i];
+        base.s6_addr[i] = (h + add) & UINT8_MAX;
+
+        /* using explicit carry for the 8-bit additions will catch
+         * 8-bit and(!) 32-bit overruns nicely
+         */
+        carry = ((h & 0xff) + (add & 0xff)) >> 8;
+        add = (add >> 8) + carry;
+    }
+    return base;
+}
+
+/* set environmental variables for ip/port in *addr */
+void
+setenv_sockaddr(struct env_set *es, const char *name_prefix, const struct 
openvpn_sockaddr *addr,
+                const unsigned int flags)
+{
+    char name_buf[256];
+
+    char buf[INET6_ADDRSTRLEN];
+    switch (addr->addr.sa.sa_family)
+    {
+        case AF_INET:
+            if (flags & SA_IP_PORT)
+            {
+                snprintf(name_buf, sizeof(name_buf), "%s_ip", name_prefix);
+            }
+            else
+            {
+                snprintf(name_buf, sizeof(name_buf), "%s", name_prefix);
+            }
+
+            inet_ntop(AF_INET, &addr->addr.in4.sin_addr, buf, sizeof(buf));
+            setenv_str(es, name_buf, buf);
+
+            if ((flags & SA_IP_PORT) && addr->addr.in4.sin_port)
+            {
+                snprintf(name_buf, sizeof(name_buf), "%s_port", name_prefix);
+                setenv_int(es, name_buf, ntohs(addr->addr.in4.sin_port));
+            }
+            break;
+
+        case AF_INET6:
+            if (IN6_IS_ADDR_V4MAPPED(&addr->addr.in6.sin6_addr))
+            {
+                struct in_addr ia;
+                memcpy(&ia.s_addr, &addr->addr.in6.sin6_addr.s6_addr[12], 
sizeof(ia.s_addr));
+                snprintf(name_buf, sizeof(name_buf), "%s_ip", name_prefix);
+                inet_ntop(AF_INET, &ia, buf, sizeof(buf));
+            }
+            else
+            {
+                snprintf(name_buf, sizeof(name_buf), "%s_ip6", name_prefix);
+                inet_ntop(AF_INET6, &addr->addr.in6.sin6_addr, buf, 
sizeof(buf));
+            }
+            setenv_str(es, name_buf, buf);
+
+            if ((flags & SA_IP_PORT) && addr->addr.in6.sin6_port)
+            {
+                snprintf(name_buf, sizeof(name_buf), "%s_port", name_prefix);
+                setenv_int(es, name_buf, ntohs(addr->addr.in6.sin6_port));
+            }
+            break;
+    }
+}
+
+void
+setenv_in_addr_t(struct env_set *es, const char *name_prefix, in_addr_t addr,
+                 const unsigned int flags)
+{
+    if (addr || !(flags & SA_SET_IF_NONZERO))
+    {
+        struct openvpn_sockaddr si;
+        CLEAR(si);
+        si.addr.in4.sin_family = AF_INET;
+        si.addr.in4.sin_addr.s_addr = htonl(addr);
+        setenv_sockaddr(es, name_prefix, &si, flags);
+    }
+}
+
+void
+setenv_in6_addr(struct env_set *es, const char *name_prefix, const struct 
in6_addr *addr,
+                const unsigned int flags)
+{
+    if (!IN6_IS_ADDR_UNSPECIFIED(addr) || !(flags & SA_SET_IF_NONZERO))
+    {
+        struct openvpn_sockaddr si;
+        CLEAR(si);
+        si.addr.in6.sin6_family = AF_INET6;
+        si.addr.in6.sin6_addr = *addr;
+        setenv_sockaddr(es, name_prefix, &si, flags);
+    }
+}
+
+void
+setenv_link_socket_actual(struct env_set *es, const char *name_prefix,
+                          const struct link_socket_actual *act, const unsigned 
int flags)
+{
+    setenv_sockaddr(es, name_prefix, &act->dest, flags);
+}
+
+/*
+ * Convert protocol names between index and ascii form.
+ */
+
+struct proto_names
+{
+    const char *short_form;
+    const char *display_form;
+    sa_family_t proto_af;
+    int proto;
+};
+
+/* Indexed by PROTO_x */
+static const struct proto_names proto_names[] = {
+    { "proto-uninitialized", "proto-NONE", AF_UNSPEC, PROTO_NONE },
+    /* try IPv4 and IPv6 (client), bind dual-stack (server) */
+    { "udp", "UDP", AF_UNSPEC, PROTO_UDP },
+    { "tcp-server", "TCP_SERVER", AF_UNSPEC, PROTO_TCP_SERVER },
+    { "tcp-client", "TCP_CLIENT", AF_UNSPEC, PROTO_TCP_CLIENT },
+    { "tcp", "TCP", AF_UNSPEC, PROTO_TCP },
+    /* force IPv4 */
+    { "udp4", "UDPv4", AF_INET, PROTO_UDP },
+    { "tcp4-server", "TCPv4_SERVER", AF_INET, PROTO_TCP_SERVER },
+    { "tcp4-client", "TCPv4_CLIENT", AF_INET, PROTO_TCP_CLIENT },
+    { "tcp4", "TCPv4", AF_INET, PROTO_TCP },
+    /* force IPv6 */
+    { "udp6", "UDPv6", AF_INET6, PROTO_UDP },
+    { "tcp6-server", "TCPv6_SERVER", AF_INET6, PROTO_TCP_SERVER },
+    { "tcp6-client", "TCPv6_CLIENT", AF_INET6, PROTO_TCP_CLIENT },
+    { "tcp6", "TCPv6", AF_INET6, PROTO_TCP },
+};
+
+int
+ascii2proto(const char *proto_name)
+{
+    for (size_t i = 0; i < SIZE(proto_names); ++i)
+    {
+        if (!strcmp(proto_name, proto_names[i].short_form))
+        {
+            return proto_names[i].proto;
+        }
+    }
+    return -1;
+}
+
+sa_family_t
+ascii2af(const char *proto_name)
+{
+    for (size_t i = 0; i < SIZE(proto_names); ++i)
+    {
+        if (!strcmp(proto_name, proto_names[i].short_form))
+        {
+            return proto_names[i].proto_af;
+        }
+    }
+    return 0;
+}
+
+const char *
+proto2ascii(int proto, sa_family_t af, bool display_form)
+{
+    for (size_t i = 0; i < SIZE(proto_names); ++i)
+    {
+        if (proto_names[i].proto_af == af && proto_names[i].proto == proto)
+        {
+            if (display_form)
+            {
+                return proto_names[i].display_form;
+            }
+            else
+            {
+                return proto_names[i].short_form;
+            }
+        }
+    }
+
+    return "[unknown protocol]";
+}
+
+const char *
+proto2ascii_all(struct gc_arena *gc)
+{
+    struct buffer out = alloc_buf_gc(256, gc);
+
+    for (size_t i = 0; i < SIZE(proto_names); ++i)
+    {
+        if (i)
+        {
+            buf_printf(&out, " ");
+        }
+        buf_printf(&out, "[%s]", proto_names[i].short_form);
+    }
+    return BSTR(&out);
+}
+
+const char *
+addr_family_name(int af)
+{
+    switch (af)
+    {
+        case AF_INET:
+            return "AF_INET";
+
+        case AF_INET6:
+            return "AF_INET6";
+    }
+    return "AF_UNSPEC";
+}
+
+/*
+ * Given a local proto, return local proto
+ * if !remote, or compatible remote proto
+ * if remote.
+ *
+ * This is used for options compatibility
+ * checking.
+ *
+ * IPv6 and IPv4 protocols are comptabile but OpenVPN
+ * has always sent UDPv4, TCPv4 over the wire. Keep these
+ * strings for backward compatibility
+ */
+const char *
+proto_remote(int proto, bool remote)
+{
+    ASSERT(proto >= 0 && proto < PROTO_N);
+    if (proto == PROTO_UDP)
+    {
+        return "UDPv4";
+    }
+
+    if ((remote && proto == PROTO_TCP_CLIENT) || (!remote && proto == 
PROTO_TCP_SERVER))
+    {
+        return "TCPv4_SERVER";
+    }
+    if ((remote && proto == PROTO_TCP_SERVER) || (!remote && proto == 
PROTO_TCP_CLIENT))
+    {
+        return "TCPv4_CLIENT";
+    }
+
+    ASSERT(0);
+    return ""; /* Make the compiler happy */
+}
+
+/**
+ * Small helper function for openvpn_getaddrinfo to print the address
+ * family when resolving fails
+ */
+static const char *
+getaddrinfo_addr_family_name(int af)
+{
+    switch (af)
+    {
+        case AF_INET:
+            return "[AF_INET]";
+
+        case AF_INET6:
+            return "[AF_INET6]";
+    }
+    return "";
+}
+
+/*
+ * Prepend a random string to hostname to prevent DNS caching.
+ * For example, foo.bar.gov would be modified to <random-chars>.foo.bar.gov.
+ * Of course, this requires explicit support in the DNS server (wildcard).
+ */
+static const char *
+hostname_randomize(const char *hostname, struct gc_arena *gc)
+{
+#define n_rnd_bytes 6
+
+    uint8_t rnd_bytes[n_rnd_bytes];
+    const char *rnd_str;
+    struct buffer hname = alloc_buf_gc(strlen(hostname) + sizeof(rnd_bytes) * 
2 + 4, gc);
+
+    prng_bytes(rnd_bytes, sizeof(rnd_bytes));
+    rnd_str = format_hex_ex(rnd_bytes, sizeof(rnd_bytes), 40, 0, NULL, gc);
+    buf_printf(&hname, "%s.%s", rnd_str, hostname);
+    return BSTR(&hname);
+#undef n_rnd_bytes
+}
+
+/*
+ * Translate IPv4/IPv6 addr or hostname into struct addrinfo
+ * If resolve error, try again for resolve_retry_seconds seconds.
+ */
+int
+openvpn_getaddrinfo(unsigned int flags, const char *hostname, const char 
*servname,
+                    int resolve_retry_seconds, struct signal_info *sig_info, 
int ai_family,
+                    struct addrinfo **res)
+{
+    struct addrinfo hints;
+    int status;
+    struct signal_info sigrec = { 0 };
+    int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS;
+    struct gc_arena gc = gc_new();
+    const char *print_hostname;
+    const char *print_servname;
+
+    ASSERT(res);
+
+    ASSERT(hostname || servname);
+    ASSERT(!(flags & GETADDR_HOST_ORDER));
+
+    if (servname)
+    {
+        print_servname = servname;
+    }
+    else
+    {
+        print_servname = "";
+    }
+
+    if (flags & GETADDR_MSG_VIRT_OUT)
+    {
+        msglevel |= M_MSG_VIRT_OUT;
+    }
+
+    if ((flags & (GETADDR_FATAL_ON_SIGNAL | GETADDR_WARN_ON_SIGNAL)) && 
!sig_info)
+    {
+        sig_info = &sigrec;
+    }
+
+    /* try numeric ip addr first */
+    CLEAR(hints);
+    hints.ai_flags = AI_NUMERICHOST;
+
+    if (flags & GETADDR_PASSIVE)
+    {
+        hints.ai_flags |= AI_PASSIVE;
+    }
+
+    if (flags & GETADDR_DATAGRAM)
+    {
+        hints.ai_socktype = SOCK_DGRAM;
+    }
+    else
+    {
+        hints.ai_socktype = SOCK_STREAM;
+    }
+
+    /* if hostname is not set, we want to bind to 'ANY', with
+     * the correct address family - v4-only or v6/v6-dual-stack */
+    if (!hostname)
+    {
+        hints.ai_family = ai_family;
+    }
+
+    status = getaddrinfo(hostname, servname, &hints, res);
+
+    if (status != 0)                      /* parse as numeric address failed? 
*/
+    {
+        const int fail_wait_interval = 5; /* seconds */
+        /* Add +4 to cause integer division rounding up (1 + 4) = 5, (0+4)/5=0 
*/
+        int resolve_retries =
+            (flags & GETADDR_TRY_ONCE) ? 1 : ((resolve_retry_seconds + 4) / 
fail_wait_interval);
+        const char *fmt;
+        int level = 0;
+
+        /* this is not a numeric IP, therefore force resolution using the
+         * provided ai_family */
+        hints.ai_family = ai_family;
+
+        if (hostname && (flags & GETADDR_RANDOMIZE))
+        {
+            hostname = hostname_randomize(hostname, &gc);
+        }
+
+        if (hostname)
+        {
+            print_hostname = hostname;
+        }
+        else
+        {
+            print_hostname = "undefined";
+        }
+
+        fmt = "RESOLVE: Cannot resolve host address: %s:%s%s (%s)";
+        if ((flags & GETADDR_MENTION_RESOLVE_RETRY) && !resolve_retry_seconds)
+        {
+            fmt = "RESOLVE: Cannot resolve host address: %s:%s%s (%s)"
+                  "(I would have retried this name query if you had "
+                  "specified the --resolv-retry option.)";
+        }
+
+        if (!(flags & GETADDR_RESOLVE) || status == EAI_FAIL)
+        {
+            msg(msglevel, "RESOLVE: Cannot parse IP address: %s:%s (%s)", 
print_hostname,
+                print_servname, gai_strerror(status));
+            goto done;
+        }
+
+#ifdef ENABLE_MANAGEMENT
+        if (flags & GETADDR_UPDATE_MANAGEMENT_STATE)
+        {
+            if (management)
+            {
+                management_set_state(management, OPENVPN_STATE_RESOLVE, NULL, 
NULL, NULL, NULL,
+                                     NULL);
+            }
+        }
+#endif
+
+        /*
+         * Resolve hostname
+         */
+        while (true)
+        {
+#ifndef _WIN32
+            /* force resolv.conf reload */
+            res_init();
+#endif
+            /* try hostname lookup */
+            hints.ai_flags &= ~AI_NUMERICHOST;
+            dmsg(D_SOCKET_DEBUG, "GETADDRINFO flags=0x%04x ai_family=%d 
ai_socktype=%d", flags,
+                 hints.ai_family, hints.ai_socktype);
+            status = getaddrinfo(hostname, servname, &hints, res);
+
+            if (sig_info)
+            {
+                get_signal(&sig_info->signal_received);
+                if (sig_info->signal_received) /* were we interrupted by a 
signal? */
+                {
+                    /* why are we overwriting SIGUSR1 ? */
+                    if (signal_reset(sig_info, SIGUSR1) == SIGUSR1) /* ignore 
SIGUSR1 */
+                    {
+                        msg(level, "RESOLVE: Ignored SIGUSR1 signal received 
during "
+                                   "DNS resolution attempt");
+                    }
+                    else
+                    {
+                        /* turn success into failure (interrupted syscall) */
+                        if (0 == status)
+                        {
+                            ASSERT(res);
+                            freeaddrinfo(*res);
+                            *res = NULL;
+                            status = EAI_AGAIN; /* = temporary failure */
+                            errno = EINTR;
+                        }
+                        goto done;
+                    }
+                }
+            }
+
+            /* success? */
+            if (0 == status)
+            {
+                break;
+            }
+
+            /* resolve lookup failed, should we
+             * continue or fail? */
+            level = msglevel;
+            if (resolve_retries > 0)
+            {
+                level = D_RESOLVE_ERRORS;
+            }
+
+            msg(level, fmt, print_hostname, print_servname, 
getaddrinfo_addr_family_name(ai_family),
+                gai_strerror(status));
+
+            if (--resolve_retries <= 0)
+            {
+                goto done;
+            }
+
+            management_sleep(fail_wait_interval);
+        }
+
+        ASSERT(res);
+
+        /* hostname resolve succeeded */
+
+        /*
+         * Do not choose an IP Addresse by random or change the order *
+         * of IP addresses, doing so will break RFC 3484 address selection *
+         */
+    }
+    else
+    {
+        /* IP address parse succeeded */
+        if (flags & GETADDR_RANDOMIZE)
+        {
+            msg(M_WARN, "WARNING: ignoring --remote-random-hostname because 
the "
+                        "hostname is an IP address");
+        }
+    }
+
+done:
+    if (sig_info && sig_info->signal_received)
+    {
+        int level = 0;
+        if (flags & GETADDR_FATAL_ON_SIGNAL)
+        {
+            level = M_FATAL;
+        }
+        else if (flags & GETADDR_WARN_ON_SIGNAL)
+        {
+            level = M_WARN;
+        }
+        msg(level, "RESOLVE: signal received during DNS resolution attempt");
+    }
+
+    gc_free(&gc);
+    return status;
+}
+
+/*
+ * We do our own inet_aton because the glibc function
+ * isn't very good about error checking.
+ */
+int
+openvpn_inet_aton(const char *dotted_quad, struct in_addr *addr)
+{
+    unsigned int a, b, c, d;
+
+    CLEAR(*addr);
+    if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) == 4)
+    {
+        if (a < 256 && b < 256 && c < 256 && d < 256)
+        {
+            addr->s_addr = htonl(a << 24 | b << 16 | c << 8 | d);
+            return OIA_IP; /* good dotted quad */
+        }
+    }
+    if (string_class(dotted_quad, CC_DIGIT | CC_DOT, 0))
+    {
+        return OIA_ERROR; /* probably a badly formatted dotted quad */
+    }
+    else
+    {
+        return OIA_HOSTNAME; /* probably a hostname */
+    }
+}
+
+bool
+ip_addr_dotted_quad_safe(const char *dotted_quad)
+{
+    /* verify non-NULL */
+    if (!dotted_quad)
+    {
+        return false;
+    }
+
+    /* verify length is within limits */
+    if (strlen(dotted_quad) > 15)
+    {
+        return false;
+    }
+
+    /* verify that all chars are either numeric or '.' and that no numeric
+     * substring is greater than 3 chars */
+    {
+        int nnum = 0;
+        const char *p = dotted_quad;
+        int c;
+
+        while ((c = *p++))
+        {
+            if (c >= '0' && c <= '9')
+            {
+                ++nnum;
+                if (nnum > 3)
+                {
+                    return false;
+                }
+            }
+            else if (c == '.')
+            {
+                nnum = 0;
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+
+    /* verify that string will convert to IP address */
+    {
+        struct in_addr a;
+        return openvpn_inet_aton(dotted_quad, &a) == OIA_IP;
+    }
+}
+
+bool
+ipv6_addr_safe(const char *ipv6_text_addr)
+{
+    /* verify non-NULL */
+    if (!ipv6_text_addr)
+    {
+        return false;
+    }
+
+    /* verify length is within limits */
+    if (strlen(ipv6_text_addr) > INET6_ADDRSTRLEN)
+    {
+        return false;
+    }
+
+    /* verify that string will convert to IPv6 address */
+    {
+        struct in6_addr a6;
+        return inet_pton(AF_INET6, ipv6_text_addr, &a6) == 1;
+    }
+}
+
+static bool
+dns_addr_safe(const char *addr)
+{
+    if (addr)
+    {
+        const size_t len = strlen(addr);
+        return len > 0 && len <= 255 && string_class(addr, CC_ALNUM | CC_DASH 
| CC_DOT, 0);
+    }
+    else
+    {
+        return false;
+    }
+}
+
+bool
+ip_or_dns_addr_safe(const char *addr, const bool allow_fqdn)
+{
+    if (ip_addr_dotted_quad_safe(addr))
+    {
+        return true;
+    }
+    else if (allow_fqdn)
+    {
+        return dns_addr_safe(addr);
+    }
+    else
+    {
+        return false;
+    }
+}
+
+bool
+mac_addr_safe(const char *mac_addr)
+{
+    /* verify non-NULL */
+    if (!mac_addr)
+    {
+        return false;
+    }
+
+    /* verify length is within limits */
+    if (strlen(mac_addr) > 17)
+    {
+        return false;
+    }
+
+    /* verify that all chars are either alphanumeric or ':' and that no
+     * alphanumeric substring is greater than 2 chars */
+    {
+        int nnum = 0;
+        const char *p = mac_addr;
+        int c;
+
+        while ((c = *p++))
+        {
+            if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' 
&& c <= 'F'))
+            {
+                ++nnum;
+                if (nnum > 2)
+                {
+                    return false;
+                }
+            }
+            else if (c == ':')
+            {
+                nnum = 0;
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+
+    /* error-checking is left to script invoked in lladdr.c */
+    return true;
+}
diff --git a/src/openvpn/socket_util.h b/src/openvpn/socket_util.h
new file mode 100644
index 0000000..5ea37dd
--- /dev/null
+++ b/src/openvpn/socket_util.h
@@ -0,0 +1,491 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2025 OpenVPN Inc <sa...@openvpn.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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/>.
+ */
+
+#ifndef SOCKET_UTIL_H
+#define SOCKET_UTIL_H
+
+#include "buffer.h"
+#include "env_set.h"
+#include "sig.h"
+
+#define PS_SHOW_PORT_IF_DEFINED (1 << 0)
+#define PS_SHOW_PORT            (1 << 1)
+#define PS_SHOW_PKTINFO         (1 << 2)
+#define PS_DONT_SHOW_ADDR       (1 << 3)
+#define PS_DONT_SHOW_FAMILY     (1 << 4)
+
+/* OpenVPN sockaddr struct */
+struct openvpn_sockaddr
+{
+    /*int dummy;*/ /* add offset to force a bug if sa not explicitly 
dereferenced */
+    union
+    {
+        struct sockaddr sa;
+        struct sockaddr_in in4;
+        struct sockaddr_in6 in6;
+    } addr;
+};
+
+/* actual address of remote, based on source address of received packets */
+struct link_socket_actual
+{
+    /*int dummy;*/ /* add offset to force a bug if dest not explicitly 
dereferenced */
+
+    struct openvpn_sockaddr dest;
+#if ENABLE_IP_PKTINFO
+    union
+    {
+#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
+        struct in_pktinfo in4;
+#elif defined(IP_RECVDSTADDR)
+        struct in_addr in4;
+#endif
+        struct in6_pktinfo in6;
+    } pi;
+#endif
+};
+
+const char *print_sockaddr_ex(const struct sockaddr *addr, const char 
*separator,
+                              const unsigned int flags, struct gc_arena *gc);
+
+static inline const char *
+print_openvpn_sockaddr(const struct openvpn_sockaddr *addr, struct gc_arena 
*gc)
+{
+    return print_sockaddr_ex(&addr->addr.sa, ":", PS_SHOW_PORT, gc);
+}
+
+static inline const char *
+print_sockaddr(const struct sockaddr *addr, struct gc_arena *gc)
+{
+    return print_sockaddr_ex(addr, ":", PS_SHOW_PORT, gc);
+}
+
+
+const char *print_link_socket_actual_ex(const struct link_socket_actual *act, 
const char *separator,
+                                        const unsigned int flags, struct 
gc_arena *gc);
+
+const char *print_link_socket_actual(const struct link_socket_actual *act, 
struct gc_arena *gc);
+
+
+#define IA_EMPTY_IF_UNDEF (1 << 0)
+#define IA_NET_ORDER      (1 << 1)
+const char *print_in_addr_t(in_addr_t addr, unsigned int flags, struct 
gc_arena *gc);
+
+const char *print_in6_addr(struct in6_addr addr6, unsigned int flags, struct 
gc_arena *gc);
+
+const char *print_in_port_t(in_port_t port, struct gc_arena *gc);
+
+struct in6_addr add_in6_addr(struct in6_addr base, uint32_t add);
+
+#define SA_IP_PORT        (1 << 0)
+#define SA_SET_IF_NONZERO (1 << 1)
+void setenv_sockaddr(struct env_set *es, const char *name_prefix,
+                     const struct openvpn_sockaddr *addr, const unsigned int 
flags);
+
+void setenv_in_addr_t(struct env_set *es, const char *name_prefix, in_addr_t 
addr,
+                      const unsigned int flags);
+
+void setenv_in6_addr(struct env_set *es, const char *name_prefix, const struct 
in6_addr *addr,
+                     const unsigned int flags);
+
+void setenv_link_socket_actual(struct env_set *es, const char *name_prefix,
+                               const struct link_socket_actual *act, const 
unsigned int flags);
+
+/*
+ * DNS resolution
+ */
+
+#define GETADDR_RESOLVE                 (1 << 0)
+#define GETADDR_FATAL                   (1 << 1)
+#define GETADDR_HOST_ORDER              (1 << 2)
+#define GETADDR_MENTION_RESOLVE_RETRY   (1 << 3)
+#define GETADDR_FATAL_ON_SIGNAL         (1 << 4)
+#define GETADDR_WARN_ON_SIGNAL          (1 << 5)
+#define GETADDR_MSG_VIRT_OUT            (1 << 6)
+#define GETADDR_TRY_ONCE                (1 << 7)
+#define GETADDR_UPDATE_MANAGEMENT_STATE (1 << 8)
+#define GETADDR_RANDOMIZE               (1 << 9)
+#define GETADDR_PASSIVE                 (1 << 10)
+#define GETADDR_DATAGRAM                (1 << 11)
+
+#define GETADDR_CACHE_MASK (GETADDR_DATAGRAM | GETADDR_PASSIVE)
+
+/**
+ * Translate an IPv4 addr or hostname from string form to in_addr_t
+ *
+ * In case of resolve error, it will try again for
+ * resolve_retry_seconds seconds.
+ */
+in_addr_t getaddr(unsigned int flags, const char *hostname, int 
resolve_retry_seconds,
+                  bool *succeeded, struct signal_info *sig_info);
+
+/**
+ * Translate an IPv6 addr or hostname from string form to in6_addr
+ */
+bool get_ipv6_addr(const char *hostname, struct in6_addr *network, unsigned 
int *netbits,
+                   int msglevel);
+
+int openvpn_getaddrinfo(unsigned int flags, const char *hostname, const char 
*servname,
+                        int resolve_retry_seconds, struct signal_info 
*sig_info, int ai_family,
+                        struct addrinfo **res);
+
+/* return values of openvpn_inet_aton */
+#define OIA_HOSTNAME 0
+#define OIA_IP       1
+#define OIA_ERROR    -1
+int openvpn_inet_aton(const char *dotted_quad, struct in_addr *addr);
+
+/* integrity validation on pulled options */
+bool ip_addr_dotted_quad_safe(const char *dotted_quad);
+
+bool ip_or_dns_addr_safe(const char *addr, const bool allow_fqdn);
+
+bool mac_addr_safe(const char *mac_addr);
+
+bool ipv6_addr_safe(const char *ipv6_text_addr);
+
+/*
+ * Transport protocol naming and other details.
+ */
+
+/*
+ * Use enum's instead of #define to allow for easier
+ * optional proto support
+ */
+enum proto_num
+{
+    PROTO_NONE, /* catch for uninitialized */
+    PROTO_UDP,
+    PROTO_TCP,
+    PROTO_TCP_SERVER,
+    PROTO_TCP_CLIENT,
+    PROTO_N
+};
+
+static inline bool
+proto_is_net(int proto)
+{
+    ASSERT(proto >= 0 && proto < PROTO_N);
+    return proto != PROTO_NONE;
+}
+
+/**
+ * @brief Returns if the protocol being used is UDP
+ */
+static inline bool
+proto_is_udp(int proto)
+{
+    ASSERT(proto >= 0 && proto < PROTO_N);
+    return proto == PROTO_UDP;
+}
+
+/**
+ * @brief Return if the protocol is datagram (UDP)
+ *
+ */
+static inline bool
+proto_is_dgram(int proto)
+{
+    return proto_is_udp(proto);
+}
+
+/**
+ * @brief returns if the proto is a TCP variant (tcp-server, tcp-client or tcp)
+ */
+static inline bool
+proto_is_tcp(int proto)
+{
+    ASSERT(proto >= 0 && proto < PROTO_N);
+    return proto == PROTO_TCP_CLIENT || proto == PROTO_TCP_SERVER;
+}
+
+int ascii2proto(const char *proto_name);
+
+sa_family_t ascii2af(const char *proto_name);
+
+const char *proto2ascii(int proto, sa_family_t af, bool display_form);
+
+const char *proto2ascii_all(struct gc_arena *gc);
+
+const char *proto_remote(int proto, bool remote);
+
+const char *addr_family_name(int af);
+
+static inline bool
+addr_defined(const struct openvpn_sockaddr *addr)
+{
+    if (!addr)
+    {
+        return 0;
+    }
+    switch (addr->addr.sa.sa_family)
+    {
+        case AF_INET:
+            return addr->addr.in4.sin_addr.s_addr != 0;
+
+        case AF_INET6:
+            return !IN6_IS_ADDR_UNSPECIFIED(&addr->addr.in6.sin6_addr);
+
+        default:
+            return 0;
+    }
+}
+
+static inline bool
+addr_local(const struct sockaddr *addr)
+{
+    if (!addr)
+    {
+        return false;
+    }
+    switch (addr->sa_family)
+    {
+        case AF_INET:
+            return ((const struct sockaddr_in *)addr)->sin_addr.s_addr == 
htonl(INADDR_LOOPBACK);
+
+        case AF_INET6:
+            return IN6_IS_ADDR_LOOPBACK(&((const struct sockaddr_in6 
*)addr)->sin6_addr);
+
+        default:
+            return false;
+    }
+}
+
+
+static inline bool
+addr_defined_ipi(const struct link_socket_actual *lsa)
+{
+#if ENABLE_IP_PKTINFO
+    if (!lsa)
+    {
+        return 0;
+    }
+    switch (lsa->dest.addr.sa.sa_family)
+    {
+#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
+        case AF_INET:
+            return lsa->pi.in4.ipi_spec_dst.s_addr != 0;
+
+#elif defined(IP_RECVDSTADDR)
+        case AF_INET:
+            return lsa->pi.in4.s_addr != 0;
+
+#endif
+        case AF_INET6:
+            return !IN6_IS_ADDR_UNSPECIFIED(&lsa->pi.in6.ipi6_addr);
+
+        default:
+            return 0;
+    }
+#else /* if ENABLE_IP_PKTINFO */
+    ASSERT(0);
+#endif
+    return false;
+}
+
+/*
+ * Overhead added to packets by various protocols.
+ */
+static inline int
+datagram_overhead(sa_family_t af, int proto)
+{
+    int overhead = 0;
+    overhead += (proto == PROTO_UDP) ? 8 : 20;
+    overhead += (af == AF_INET) ? 20 : 40;
+    return overhead;
+}
+
+/*
+ * Misc inline functions
+ */
+
+static inline bool
+link_socket_proto_connection_oriented(int proto)
+{
+    return !proto_is_dgram(proto);
+}
+
+static inline bool
+link_socket_actual_defined(const struct link_socket_actual *act)
+{
+    return act && addr_defined(&act->dest);
+}
+
+static inline bool
+addr_match(const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr 
*a2)
+{
+    switch (a1->addr.sa.sa_family)
+    {
+        case AF_INET:
+            return a1->addr.in4.sin_addr.s_addr == 
a2->addr.in4.sin_addr.s_addr;
+
+        case AF_INET6:
+            return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, 
&a2->addr.in6.sin6_addr);
+    }
+    ASSERT(0);
+    return false;
+}
+
+static inline bool
+addrlist_match(const struct openvpn_sockaddr *a1, const struct addrinfo 
*addrlist)
+{
+    const struct addrinfo *curele;
+    for (curele = addrlist; curele; curele = curele->ai_next)
+    {
+        switch (a1->addr.sa.sa_family)
+        {
+            case AF_INET:
+                if (a1->addr.in4.sin_addr.s_addr
+                    == ((struct sockaddr_in 
*)curele->ai_addr)->sin_addr.s_addr)
+                {
+                    return true;
+                }
+                break;
+
+            case AF_INET6:
+                if (IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr,
+                                       &((struct sockaddr_in6 
*)curele->ai_addr)->sin6_addr))
+                {
+                    return true;
+                }
+                break;
+
+            default:
+                ASSERT(0);
+        }
+    }
+    return false;
+}
+
+static inline bool
+addrlist_port_match(const struct openvpn_sockaddr *a1, const struct addrinfo 
*a2)
+{
+    const struct addrinfo *curele;
+    for (curele = a2; curele; curele = curele->ai_next)
+    {
+        switch (a1->addr.sa.sa_family)
+        {
+            case AF_INET:
+                if (curele->ai_family == AF_INET
+                    && a1->addr.in4.sin_addr.s_addr
+                           == ((struct sockaddr_in 
*)curele->ai_addr)->sin_addr.s_addr
+                    && a1->addr.in4.sin_port == ((struct sockaddr_in 
*)curele->ai_addr)->sin_port)
+                {
+                    return true;
+                }
+                break;
+
+            case AF_INET6:
+                if (curele->ai_family == AF_INET6
+                    && IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr,
+                                          &((struct sockaddr_in6 
*)curele->ai_addr)->sin6_addr)
+                    && a1->addr.in6.sin6_port
+                           == ((struct sockaddr_in6 
*)curele->ai_addr)->sin6_port)
+                {
+                    return true;
+                }
+                break;
+
+            default:
+                ASSERT(0);
+        }
+    }
+    return false;
+}
+
+
+static inline bool
+addr_port_match(const struct openvpn_sockaddr *a1, const struct 
openvpn_sockaddr *a2)
+{
+    switch (a1->addr.sa.sa_family)
+    {
+        case AF_INET:
+            return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr
+                   && a1->addr.in4.sin_port == a2->addr.in4.sin_port;
+
+        case AF_INET6:
+            return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, 
&a2->addr.in6.sin6_addr)
+                   && a1->addr.in6.sin6_port == a2->addr.in6.sin6_port;
+    }
+    ASSERT(0);
+    return false;
+}
+
+static inline bool
+addr_match_proto(const struct openvpn_sockaddr *a1, const struct 
openvpn_sockaddr *a2,
+                 const int proto)
+{
+    return link_socket_proto_connection_oriented(proto) ? addr_match(a1, a2)
+                                                        : addr_port_match(a1, 
a2);
+}
+
+
+static inline bool
+addrlist_match_proto(const struct openvpn_sockaddr *a1, struct addrinfo 
*addr_list, const int proto)
+{
+    return link_socket_proto_connection_oriented(proto) ? addrlist_match(a1, 
addr_list)
+                                                        : 
addrlist_port_match(a1, addr_list);
+}
+
+static inline void
+addr_zero_host(struct openvpn_sockaddr *addr)
+{
+    switch (addr->addr.sa.sa_family)
+    {
+        case AF_INET:
+            addr->addr.in4.sin_addr.s_addr = 0;
+            break;
+
+        case AF_INET6:
+            memset(&addr->addr.in6.sin6_addr, 0, sizeof(struct in6_addr));
+            break;
+    }
+}
+
+static inline int
+af_addr_size(sa_family_t af)
+{
+    switch (af)
+    {
+        case AF_INET:
+            return sizeof(struct sockaddr_in);
+
+        case AF_INET6:
+            return sizeof(struct sockaddr_in6);
+
+        default:
+#if 0
+            /* could be called from socket_do_accept() with empty addr */
+            msg(M_ERR, "Bad address family: %d\n", af);
+            ASSERT(0);
+#endif
+            return 0;
+    }
+}
+
+static inline bool
+link_socket_actual_match(const struct link_socket_actual *a1, const struct 
link_socket_actual *a2)
+{
+    return addr_port_match(&a1->dest, &a2->dest);
+}
+
+#endif
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index e8fde86..428bf5a 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -30,7 +30,7 @@
 #define SSL_COMMON_H_
 
 #include "session_id.h"
-#include "socket.h"
+#include "socket_util.h"
 #include "packet_id.h"
 #include "crypto.h"
 #include "options.h"
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index f54b608..6c3096b 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -39,7 +39,7 @@
 #include "fdmisc.h"
 #include "common.h"
 #include "run_command.h"
-#include "socket.h"
+#include "socket_util.h"
 #include "manage.h"
 #include "route.h"
 #include "win32.h"


_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to