File fix-upsd-buffer-overflow.patch of Package nut

From 16d7f45dea569efb6dc93c83a54de521683d5c74 Mon Sep 17 00:00:00 2001
From: Jeremy Cooper <jeremy.gthb@baymoo.org>
Date: Wed, 23 Apr 2025 15:20:31 -0700
Subject: [PATCH 1/8] Fix issue #2914: upsd misprint/buffer overflow.

---
 server/upsd.c | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/server/upsd.c b/server/upsd.c
index a40b74d6b5..58f87a4fd2 100644
--- a/server/upsd.c
+++ b/server/upsd.c
@@ -507,17 +507,13 @@ static void setuptcp(stype_t *server)
 		}
 
 		if (ai->ai_next) {
-			char ipaddrbuf[SMALLBUF];
-			const char *ipaddr;
-			snprintf(ipaddrbuf, sizeof(ipaddrbuf), " as ");
-			ipaddr = inet_ntop(ai->ai_family, ai->ai_addr,
-				ipaddrbuf + strlen(ipaddrbuf),
-				sizeof(ipaddrbuf));
+			const char *ipaddr = inet_ntopW((struct sockaddr_storage *)ai->ai_addr);
 			upslogx(LOG_WARNING,
-				"setuptcp: bound to %s%s but there seem to be "
+				"setuptcp: bound to %s%s%s but there seem to be "
 				"further (ignored) addresses resolved for this name",
 				server->addr,
-				ipaddr == NULL ? "" : ipaddrbuf);
+				ipaddr == NULL ? "" : " as ",
+				ipaddr == NULL ? "" : ipaddr);
 		}
 
 		server->sock_fd = sock_fd;

From 49a12adf54b826d75e8b984c3ea310bb6d896a2f Mon Sep 17 00:00:00 2001
From: Jim Klimov <jimklimov+nut@gmail.com>
Date: Thu, 24 Apr 2025 09:55:58 +0200
Subject: [PATCH 3/8] server/upsd.c: introduce inet_ntopAI() specifically for
 struct addrinfo conversion [#2915]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
---
 server/upsd.c | 42 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)

diff --git a/server/upsd.c b/server/upsd.c
index 58f87a4fd2..6a70e6b1a1 100644
--- a/server/upsd.c
+++ b/server/upsd.c
@@ -186,6 +186,46 @@ static const char *inet_ntopW (struct sockaddr_storage *s)
 	}
 }
 
+static const char *inet_ntopAI(struct addrinfo *ai)
+{
+	/* Note: below we manipulate copies of ai - cannot cast into
+	 * specific structure type pointers right away because:
+	 *   error: cast from 'struct sockaddr *' to 'struct sockaddr_storage *'
+	 *          increases required alignment from 2 to 8
+	 */
+	/* https://stackoverflow.com/a/29147085/4715872
+	 * obviously INET6_ADDRSTRLEN is expected to be larger
+	 * than INET_ADDRSTRLEN, but this may be required in case
+	 * if for some unexpected reason IPv6 is not supported, and
+	 * INET6_ADDRSTRLEN is defined as 0
+	 * but this is not very likely and I am aware of no cases of
+	 * this in practice (editor)
+	 */
+	static char	addrstr[(INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN) + 1];
+
+	if (!ai || !ai->ai_addr) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	addrstr[0] = '\0';
+	switch (ai->ai_family) {
+		case AF_INET: {
+			struct sockaddr_in	addr_in;
+			memcpy(&addr_in, ai->ai_addr, sizeof(addr_in));
+			return inet_ntop(AF_INET, &(addr_in.sin_addr), addrstr, INET_ADDRSTRLEN);
+		}
+		case AF_INET6: {
+			struct sockaddr_in6	addr_in6;
+			memcpy(&addr_in6, ai->ai_addr, sizeof(addr_in6));
+			return inet_ntop(AF_INET6, &(addr_in6.sin6_addr), addrstr, INET6_ADDRSTRLEN);
+		}
+		default:
+			errno = EAFNOSUPPORT;
+			return NULL;
+	}
+}
+
 /* return a pointer to the named ups if possible */
 upstype_t *get_ups_ptr(const char *name)
 {
@@ -507,7 +547,7 @@ static void setuptcp(stype_t *server)
 		}
 
 		if (ai->ai_next) {
-			const char *ipaddr = inet_ntopW((struct sockaddr_storage *)ai->ai_addr);
+			const char *ipaddr = inet_ntopAI(ai);
 			upslogx(LOG_WARNING,
 				"setuptcp: bound to %s%s%s but there seem to be "
 				"further (ignored) addresses resolved for this name",

From db2cc95ddb884aa4c98875a5b9a034edb7f64849 Mon Sep 17 00:00:00 2001
From: Jim Klimov <jimklimov+nut@gmail.com>
Date: Thu, 24 Apr 2025 09:56:32 +0200
Subject: [PATCH 4/8] server/upsd.c: add a sanity check into inet_ntopW()
 [#2915]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
---
 server/upsd.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/server/upsd.c b/server/upsd.c
index 6a70e6b1a1..61539326d0 100644
--- a/server/upsd.c
+++ b/server/upsd.c
@@ -170,10 +170,15 @@ static int	reload_flag = 0, exit_flag = 0;
 # define SERVICE_UNIT_NAME "nut-server.service"
 #endif
 
-static const char *inet_ntopW (struct sockaddr_storage *s)
+static const char *inet_ntopW(struct sockaddr_storage *s)
 {
 	static char str[40];
 
+	if (!s) {
+		errno = EINVAL;
+		return NULL;
+	}
+
 	switch (s->ss_family)
 	{
 	case AF_INET:

From 4d4e84c7073a49f9787a660132988acd28ef399d Mon Sep 17 00:00:00 2001
From: Jim Klimov <jimklimov+nut@gmail.com>
Date: Thu, 24 Apr 2025 10:13:14 +0200
Subject: [PATCH 5/8] Refactor inet_ntopSS() and inet_ntopAI() into common
 code, simplify upsclient and upsd

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
---
 clients/upsclient.c | 29 ++------------------
 common/common.c     | 64 +++++++++++++++++++++++++++++++++++++++++++++
 docs/nut.dict       |  6 ++++-
 include/common.h    | 17 +++++++++---
 server/upsd.c       | 63 +-------------------------------------------
 6 files changed, 90 insertions(+), 94 deletions(-)

diff --git a/clients/upsclient.c b/clients/upsclient.c
index f2773b41d8..67efcf1339 100644
--- a/clients/upsclient.c
+++ b/clients/upsclient.c
@@ -1172,34 +1172,9 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, uint16_t port, int flags
 			    ups->upserror == UPSCLI_ERR_CONNFAILURE &&
 			    ups->syserrno == ETIMEDOUT
 			) {
-				/* https://stackoverflow.com/a/29147085/4715872
-				 * obviously INET6_ADDRSTRLEN is expected to be larger
-				 * than INET_ADDRSTRLEN, but this may be required in case
-				 * if for some unexpected reason IPv6 is not supported, and
-				 * INET6_ADDRSTRLEN is defined as 0
-				 * but this is not very likely and I am aware of no cases of
-				 * this in practice (editor)
-				 */
-				char	addrstr[(INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN) + 1];
-				addrstr[0] = '\0';
-				switch (ai->ai_family) {
-					case AF_INET: {
-						struct sockaddr_in addr_in;
-						memcpy(&addr_in, ai->ai_addr, sizeof(addr_in));
-						inet_ntop(AF_INET, &(addr_in.sin_addr), addrstr, INET_ADDRSTRLEN);
-						break;
-					}
-					case AF_INET6: {
-						struct sockaddr_in6 addr_in6;
-						memcpy(&addr_in6, ai->ai_addr, sizeof(addr_in6));
-						inet_ntop(AF_INET6, &(addr_in6.sin6_addr), addrstr, INET6_ADDRSTRLEN);
-						break;
-					}
-					default:
-						break;
-				}
+				const char	*addrstr = inet_ntopAI(ai);
 				upslogx(LOG_WARNING, "%s: Connection to host timed out: '%s'",
-					__func__, *addrstr ? addrstr : NUT_STRARG(host));
+					__func__, (addrstr && *addrstr) ? addrstr : NUT_STRARG(host));
 				break;
 			}
 			continue;
diff --git a/common/common.c b/common/common.c
index c1ea0d0243..cd0012d3fd 100644
--- a/common/common.c
+++ b/common/common.c
@@ -4485,3 +4485,67 @@ int match_regex_hex(const regex_t *preg, const int n)
 	return match_regex(preg, buf);
 }
 #endif	/* HAVE_LIBREGEX */
+
+/* NOT THREAD SAFE!
+ * Helpers to convert one IP address to string from different structure types
+ * Return pointer to internal buffer, or NULL and errno upon errors */
+const char *inet_ntopSS(struct sockaddr_storage *s)
+{
+	static char str[40];
+
+	if (!s) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	switch (s->ss_family)
+	{
+	case AF_INET:
+		return inet_ntop (AF_INET, &(((struct sockaddr_in *)s)->sin_addr), str, 16);
+	case AF_INET6:
+		return inet_ntop (AF_INET6, &(((struct sockaddr_in6 *)s)->sin6_addr), str, 40);
+	default:
+		errno = EAFNOSUPPORT;
+		return NULL;
+	}
+}
+
+const char *inet_ntopAI(struct addrinfo *ai)
+{
+	/* Note: below we manipulate copies of ai - cannot cast into
+	 * specific structure type pointers right away because:
+	 *   error: cast from 'struct sockaddr *' to 'struct sockaddr_storage *'
+	 *          increases required alignment from 2 to 8
+	 */
+	/* https://stackoverflow.com/a/29147085/4715872
+	 * obviously INET6_ADDRSTRLEN is expected to be larger
+	 * than INET_ADDRSTRLEN, but this may be required in case
+	 * if for some unexpected reason IPv6 is not supported, and
+	 * INET6_ADDRSTRLEN is defined as 0
+	 * but this is not very likely and I am aware of no cases of
+	 * this in practice (editor)
+	 */
+	static char	addrstr[(INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN) + 1];
+
+	if (!ai || !ai->ai_addr) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	addrstr[0] = '\0';
+	switch (ai->ai_family) {
+		case AF_INET: {
+			struct sockaddr_in	addr_in;
+			memcpy(&addr_in, ai->ai_addr, sizeof(addr_in));
+			return inet_ntop(AF_INET, &(addr_in.sin_addr), addrstr, INET_ADDRSTRLEN);
+		}
+		case AF_INET6: {
+			struct sockaddr_in6	addr_in6;
+			memcpy(&addr_in6, ai->ai_addr, sizeof(addr_in6));
+			return inet_ntop(AF_INET6, &(addr_in6.sin6_addr), addrstr, INET6_ADDRSTRLEN);
+		}
+		default:
+			errno = EAFNOSUPPORT;
+			return NULL;
+	}
+}
diff --git a/docs/nut.dict b/docs/nut.dict
index 6286067839..7eaacafbe4 100644
--- a/docs/nut.dict
+++ b/docs/nut.dict
@@ -1,4 +1,4 @@
-personal_ws-1.1 en 3458 utf-8
+personal_ws-1.1 en 3462 utf-8
 AAC
 AAS
 ABI
@@ -2206,6 +2206,7 @@ ina
 includePath
 includedir
 inductor
+inet
 infos
 infoval
 inh
@@ -2623,6 +2624,9 @@ nowarn
 np
 nspr
 nss
+ntopAI
+ntopSS
+ntopW
 nuget
 num
 numOfBytesFromUPS
diff --git a/include/common.h b/include/common.h
index 210f7e542e..b7e6fe27ee 100644
--- a/include/common.h
+++ b/include/common.h
@@ -65,11 +65,14 @@
 #endif
 
 #ifndef WIN32
-#include <syslog.h>
+# include <netdb.h>
+# include <sys/socket.h>
+# include <arpa/inet.h>
+# include <syslog.h>
 #else	/* WIN32 */
-#include <winsock2.h>
-#include <windows.h>
-#include <ws2tcpip.h>
+# include <winsock2.h>
+# include <windows.h>
+# include <ws2tcpip.h>
 #endif	/* WIN32 */
 
 #include <unistd.h>	/* useconds_t */
@@ -402,6 +405,12 @@ const char * rootpidpath(void);
 /* Die with a standard message if socket filename is too long */
 void check_unix_socket_filename(const char *fn);
 
+/* NOT THREAD SAFE!
+ * Helpers to convert one IP address to string from different structure types
+ * Return pointer to internal buffer, or NULL and errno upon errors */
+const char *inet_ntopSS(struct sockaddr_storage *s);
+const char *inet_ntopAI(struct addrinfo *ai);
+
 /* Provide integration for systemd inhibitor interface (where available,
  * dummy code otherwise) implementing the pseudo-code example from
  * https://systemd.io/INHIBITOR_LOCKS/
diff --git a/server/upsd.c b/server/upsd.c
index 61539326d0..924a0708c3 100644
--- a/server/upsd.c
+++ b/server/upsd.c
@@ -170,67 +170,6 @@ static int	reload_flag = 0, exit_flag = 0;
 # define SERVICE_UNIT_NAME "nut-server.service"
 #endif
 
-static const char *inet_ntopW(struct sockaddr_storage *s)
-{
-	static char str[40];
-
-	if (!s) {
-		errno = EINVAL;
-		return NULL;
-	}
-
-	switch (s->ss_family)
-	{
-	case AF_INET:
-		return inet_ntop (AF_INET, &(((struct sockaddr_in *)s)->sin_addr), str, 16);
-	case AF_INET6:
-		return inet_ntop (AF_INET6, &(((struct sockaddr_in6 *)s)->sin6_addr), str, 40);
-	default:
-		errno = EAFNOSUPPORT;
-		return NULL;
-	}
-}
-
-static const char *inet_ntopAI(struct addrinfo *ai)
-{
-	/* Note: below we manipulate copies of ai - cannot cast into
-	 * specific structure type pointers right away because:
-	 *   error: cast from 'struct sockaddr *' to 'struct sockaddr_storage *'
-	 *          increases required alignment from 2 to 8
-	 */
-	/* https://stackoverflow.com/a/29147085/4715872
-	 * obviously INET6_ADDRSTRLEN is expected to be larger
-	 * than INET_ADDRSTRLEN, but this may be required in case
-	 * if for some unexpected reason IPv6 is not supported, and
-	 * INET6_ADDRSTRLEN is defined as 0
-	 * but this is not very likely and I am aware of no cases of
-	 * this in practice (editor)
-	 */
-	static char	addrstr[(INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN) + 1];
-
-	if (!ai || !ai->ai_addr) {
-		errno = EINVAL;
-		return NULL;
-	}
-
-	addrstr[0] = '\0';
-	switch (ai->ai_family) {
-		case AF_INET: {
-			struct sockaddr_in	addr_in;
-			memcpy(&addr_in, ai->ai_addr, sizeof(addr_in));
-			return inet_ntop(AF_INET, &(addr_in.sin_addr), addrstr, INET_ADDRSTRLEN);
-		}
-		case AF_INET6: {
-			struct sockaddr_in6	addr_in6;
-			memcpy(&addr_in6, ai->ai_addr, sizeof(addr_in6));
-			return inet_ntop(AF_INET6, &(addr_in6.sin6_addr), addrstr, INET6_ADDRSTRLEN);
-		}
-		default:
-			errno = EAFNOSUPPORT;
-			return NULL;
-	}
-}
-
 /* return a pointer to the named ups if possible */
 upstype_t *get_ups_ptr(const char *name)
 {
@@ -853,7 +792,7 @@ static void client_connect(stype_t *server)
 
 	time(&client->last_heard);
 
-	client->addr = xstrdup(inet_ntopW(&csock));
+	client->addr = xstrdup(inet_ntopSS(&csock));
 
 	client->tracking = 0;
 

From e3e9093518fac446dc7558ce36a83ad46e581f14 Mon Sep 17 00:00:00 2001
From: Jim Klimov <jimklimov+nut@gmail.com>
Date: Thu, 24 Apr 2025 10:36:10 +0000
Subject: [PATCH 7/8] include/common.h: fix includes for inet_ntopXX [#2916]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
---
 include/common.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/common.h b/include/common.h
index b7e6fe27ee..e9eb9a5def 100644
--- a/include/common.h
+++ b/include/common.h
@@ -68,6 +68,7 @@
 # include <netdb.h>
 # include <sys/socket.h>
 # include <arpa/inet.h>
+# include <netinet/in.h>
 # include <syslog.h>
 #else	/* WIN32 */
 # include <winsock2.h>

From 862eb7c975dc4da99bd23873d2dc92d16f519981 Mon Sep 17 00:00:00 2001
From: Jim Klimov <jimklimov+nut@gmail.com>
Date: Thu, 24 Apr 2025 11:11:33 +0000
Subject: [PATCH 8/8] Constrain pre-processor visibility and impact of
 inet_ntopXX() methods with NUT_WANT_INET_NTOP_XX macro [#2916]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
---
 clients/upsclient.c |  2 ++
 common/common.c     |  2 ++
 include/common.h    | 19 +++++++++++++++----
 server/upsd.c       |  4 +++-
 4 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/clients/upsclient.c b/clients/upsclient.c
index 67efcf1339..3bf2b84546 100644
--- a/clients/upsclient.c
+++ b/clients/upsclient.c
@@ -20,6 +20,8 @@
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
 
+#define NUT_WANT_INET_NTOP_XX	1
+
 #include "config.h"	/* safe because it doesn't contain prototypes */
 #include "nut_platform.h"
 
diff --git a/common/common.c b/common/common.c
index cd0012d3fd..8d37881efc 100644
--- a/common/common.c
+++ b/common/common.c
@@ -18,6 +18,8 @@
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
 
+#define NUT_WANT_INET_NTOP_XX	1
+
 #include "common.h"
 #include "timehead.h"
 
diff --git a/include/common.h b/include/common.h
index e9eb9a5def..dce48bc888 100644
--- a/include/common.h
+++ b/include/common.h
@@ -65,10 +65,6 @@
 #endif
 
 #ifndef WIN32
-# include <netdb.h>
-# include <sys/socket.h>
-# include <arpa/inet.h>
-# include <netinet/in.h>
 # include <syslog.h>
 #else	/* WIN32 */
 # include <winsock2.h>
@@ -76,6 +72,19 @@
 # include <ws2tcpip.h>
 #endif	/* WIN32 */
 
+#ifdef NUT_WANT_INET_NTOP_XX
+/* We currently have a few consumers who should define this macro before
+ * including common.h, while we do not want to encumber preprocessing the
+ * majority of codebase with these headers and our (thread-unsafe) methods.
+ */
+# ifndef WIN32
+#  include <netdb.h>
+#  include <sys/socket.h>
+#  include <arpa/inet.h>
+#  include <netinet/in.h>
+# endif	/* WIN32 */
+#endif	/* NUT_WANT_INET_NTOP_XX */
+
 #include <unistd.h>	/* useconds_t */
 #ifndef HAVE_USECONDS_T
 # define useconds_t	unsigned long int
@@ -406,11 +415,13 @@ const char * rootpidpath(void);
 /* Die with a standard message if socket filename is too long */
 void check_unix_socket_filename(const char *fn);
 
+#ifdef NUT_WANT_INET_NTOP_XX
 /* NOT THREAD SAFE!
  * Helpers to convert one IP address to string from different structure types
  * Return pointer to internal buffer, or NULL and errno upon errors */
 const char *inet_ntopSS(struct sockaddr_storage *s);
 const char *inet_ntopAI(struct addrinfo *ai);
+#endif	/* NUT_WANT_INET_NTOP_XX */
 
 /* Provide integration for systemd inhibitor interface (where available,
  * dummy code otherwise) implementing the pseudo-code example from
diff --git a/server/upsd.c b/server/upsd.c
index 924a0708c3..abc5a28e47 100644
--- a/server/upsd.c
+++ b/server/upsd.c
@@ -5,7 +5,7 @@
 	2008		Arjen de Korte <adkorte-guest@alioth.debian.org>
 	2011 - 2012	Arnaud Quette <arnaud.quette.free.fr>
 	2019 		Eaton (author: Arnaud Quette <ArnaudQuette@eaton.com>)
-	2020 - 2024	Jim Klimov <jimklimov+nut@gmail.com>
+	2020 - 2025	Jim Klimov <jimklimov+nut@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -22,6 +22,8 @@
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
 
+#define NUT_WANT_INET_NTOP_XX	1
+
 #include "config.h"	/* must be the first header */
 
 #include "upsd.h"

openSUSE Build Service is sponsored by