From patchwork Sun Aug 31 15:59:32 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gert Doering X-Patchwork-Id: 4383 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:2a1c:b0:671:5a2c:6455 with SMTP id k28csp698870maz; Sun, 31 Aug 2025 08:59:53 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCXV3KZ10EyDdZKNtiIxG6/spexzN/uVDm4tAooolJ6QYckKfi6Yfxp35NnjxZ2WZv8loCD5mk8Miwk=@openvpn.net X-Google-Smtp-Source: AGHT+IFuXUmIDGq1eumOd6F7F6qrAz457exkP/Wrbi7ySKU6WRiTlteoq0VD50Sovc+I5x+mzQRU X-Received: by 2002:a05:6830:67ef:b0:745:1905:af2d with SMTP id 46e09a7af769-74569dca2bdmr3627385a34.10.1756655993648; Sun, 31 Aug 2025 08:59:53 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1756655993; cv=none; d=google.com; s=arc-20240605; b=aYpg3Swe/5t2ou1CEoLYzI45PodjEmEPFe844aObEr2LKHOdgWHX+ZuoE+g8K92Kfi HAneC7I/aa8bIHeRmuB/bNOsSVfER1U/qsjRJdEdPdqnSng62rqIEUpMGboLnLpUajvJ MHxOnfguqfyfaUOqmNPvSoih1ej57JjbSvfYTqPSGlBN7DQ2zLm4llLSZuy29mwRINfo Kw6KFCMWx4ApG4TudkupW+u1qMKUKMNpyUwT+kKqxyFgNEbfaTrzC4eHHUcQ8LphAVpx Uir9Zn7uccFb8MPEZXRoZFV5vnLj922w547l8S3Uj9IMTICtK1yP9yOLgLfjFPRuIrFu TuAQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=errors-to:content-transfer-encoding:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:references:in-reply-to:message-id:date:to:from :dkim-signature:dkim-signature:dkim-signature; bh=BX9UwyhGw9c6lnKH3PAlVRpFJx/LeMTlShX6RBRw2L8=; fh=4NbAC/LsuMLI0S0hprUlLSLCiHwg6SCAifhH718Jh0Q=; b=BOlEL3ndBj2Pw/lDgmMf+hahAJF4fjPJR1Hktnbv1GHflNC1SVwUXRCGicv4kJ6rOW GRV+6vjRwuZrvQRVnHnM8PdUK17rOM4yi43ONUIoXT9aSJ9ZYrj2gXu+usYwjbuupn2h 5qZbbn3IqIi2xBz2HaVZWvjW53+tcEI9ZZWDs8xssEC7fpEC0EwIJkkIbq7LJpTlXsSy o6KM5oAGMn4htXM2kYdnujUE7Yf7bm7OXQGOBzbEkFlY2nlv7WQXvHD7Q+3xiVNSwOM2 0pni/u+9zxdxpt5XLDeltkZZVTY2EYSaL72VplWMUi9C8BpE6cp1FoexlSCieWOz+7k3 dWpQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=Lf2S2m4e; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=ACDgjmE8; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=MPNeDBcx; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id 46e09a7af769-745744a7b0csi419077a34.437.2025.08.31.08.59.53 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 31 Aug 2025 08:59:53 -0700 (PDT) Received-SPF: pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) client-ip=216.105.38.7; Authentication-Results: mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=Lf2S2m4e; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=ACDgjmE8; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=MPNeDBcx; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.sourceforge.net; s=beta; h=Content-Transfer-Encoding:Content-Type: List-Subscribe:List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id: Subject:MIME-Version:References:In-Reply-To:Message-ID:Date:To:From:Sender: Reply-To:Cc:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=BX9UwyhGw9c6lnKH3PAlVRpFJx/LeMTlShX6RBRw2L8=; b=Lf2S2m4erUswQmvkRVbSKaZRmE tS0qExXi1vAbwV72hj2Fk2M22rTp9QzHaLMnYAewJMdQ32bGjX66K8oG1MK5TjHjNYi+tuU8pS3jb /2Ver09t+0EreEpWr0xxvlvM0aaTupmrUVI5Mq6gtI2fv2PbqJNofFBFh3yrUGIX3wMs=; Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1uskT9-0001gc-Dr; Sun, 31 Aug 2025 15:59:51 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1uskT7-0001gS-FC for openvpn-devel@lists.sourceforge.net; Sun, 31 Aug 2025 15:59:49 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=v4RwlsPvEGSL5ChBe4DKQNNmrvJPmMdLbFgeiwbQvVQ=; b=ACDgjmE8xl+1q1z4fSrVFW943A RT/3Opn67FG3WjIaV3898Y6f582G5L9ViQvaS7ON44Zuw7VRVfGfXiwzndBa6AdNx9cpgsthjB3zC BW2o+GDk00WhmY4JydiPjUanJe/DfRSdAcpOsc6j+JEnyZ4d4SyScUuz9PJAQlFlgq5c=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID: Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=v4RwlsPvEGSL5ChBe4DKQNNmrvJPmMdLbFgeiwbQvVQ=; b=MPNeDBcxLh+p99+VZTy2h0kJlW YyhaSXA+FVOFU1cvZ/IOWFUF1wBTnQA2WejhEkUrgvx9Pd6y2Aexjxo02ekFHigDQTe/2FdGmtvM8 dOiPIN6MfEmGPZQKuCeP1nEEf839lr7rVn5qQtf44xnRgh+V47mdn4/OyYBtGmRoJ+Ws=; Received: from [193.149.48.143] (helo=blue.greenie.muc.de) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1uskT4-00077V-EJ for openvpn-devel@lists.sourceforge.net; Sun, 31 Aug 2025 15:59:49 +0000 Received: from blue.greenie.muc.de (localhost [127.0.0.1]) by blue.greenie.muc.de (8.18.1/8.18.1) with ESMTP id 57VFxd4n029265 for ; Sun, 31 Aug 2025 17:59:39 +0200 Received: (from gert@localhost) by blue.greenie.muc.de (8.18.1/8.18.1/Submit) id 57VFxdjO029264 for openvpn-devel@lists.sourceforge.net; Sun, 31 Aug 2025 17:59:39 +0200 From: Gert Doering To: openvpn-devel@lists.sourceforge.net Date: Sun, 31 Aug 2025 17:59:32 +0200 Message-ID: <20250831155939.29240-1-gert@greenie.muc.de> X-Mailer: git-send-email 2.49.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Score: 1.3 (+) X-Spam-Report: Spam detection software, running on the system "sfi-spamd-1.hosts.colo.sdot.me", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Frank Lichtenheld This extracts a lot of the helper functions that do not actually work on sockets, but instead on addresses or similar. This includes Content analysis details: (1.3 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 1.3 RDNS_NONE Delivered to internal network by a host with no rDNS X-Headers-End: 1uskT4-00077V-EJ Subject: [Openvpn-devel] [PATCH v1] socket: Create socket_util with non-socket functions X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: =?utf-8?q?1841987314993580423?= X-GMAIL-MSGID: =?utf-8?q?1841987314993580423?= From: Frank Lichtenheld 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 Acked-by: Gert Doering --- 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 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 .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 #include 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 + * + * 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 . + */ + +#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 .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 + * + * 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 . + */ + +#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"