From e7dcd6a5407a65664e7fada76fc4bb791080a7b2 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 22 Jan 2026 02:30:56 +0000 Subject: [PATCH 1/2] Fix (attempt) GH-20752: netsnmp_session_init() without system ipv6. - opening up building peername to non ipv6 supported systems. - while at it, inet_ntop should not be called with a query size as large as MAX_NAME_LEN/MAX_OID_LEN but INET6_ADDRSTRLEN max. - checking in case of non ipv6 support (AF_INET6 can be still defined in this case). --- ext/snmp/snmp.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 0654a97dfc861..f54f4fd99ed43 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -842,6 +842,9 @@ static bool netsnmp_session_init(php_snmp_session **session_p, int version, zend struct sockaddr **res; // TODO: Do not strip and re-add the port in peername? unsigned remote_port = SNMP_PORT; +#if defined(HAVE_GETADDRINFO) + char name[INET6_ADDRSTRLEN]; +#endif *session_p = (php_snmp_session *)emalloc(sizeof(php_snmp_session)); session = *session_p; @@ -888,18 +891,17 @@ static bool netsnmp_session_init(php_snmp_session **session_p, int version, zend res = psal; while (n-- > 0) { pptr = session->peername; -#if defined(HAVE_GETADDRINFO) && defined(HAVE_IPV6) +#if defined(HAVE_GETADDRINFO) if (force_ipv6 && (*res)->sa_family != AF_INET6) { res++; continue; } if ((*res)->sa_family == AF_INET6) { - strcpy(session->peername, "udp6:["); - pptr = session->peername + strlen(session->peername); - inet_ntop((*res)->sa_family, &(((struct sockaddr_in6*)(*res))->sin6_addr), pptr, MAX_NAME_LEN); - strcat(pptr, "]"); + if (inet_ntop((*res)->sa_family, &(((struct sockaddr_in6*)(*res))->sin6_addr), name, sizeof(name))) { + snprintf(pptr, MAX_NAME_LEN, "udp6:[%s]", name); + } } else if ((*res)->sa_family == AF_INET) { - inet_ntop((*res)->sa_family, &(((struct sockaddr_in*)(*res))->sin_addr), pptr, MAX_NAME_LEN); + inet_ntop((*res)->sa_family, &(((struct sockaddr_in*)(*res))->sin_addr), pptr, INET_ADDRSTRLEN); } else { res++; continue; From 687ac23e9d5944c28bae5dc4311f8e0072b03f7e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Feb 2026 20:11:08 +0000 Subject: [PATCH 2/2] Prefer AF_INET results on IPv4-only systems per @thomasvincent suggestion. On IPv4-only systems, getaddrinfo() can still return AF_INET6 mapped addresses (e.g. ::ffff:127.0.0.1). The code would format these as udp6:[addr] peername that Net-SNMP cannot connect to. When IPv6 is not explicitly requested (no bracket notation), prefer AF_INET results. If no suitable address is found, fall back to the original hostname and let Net-SNMP resolve it itself, matching the behavior of snmprealwalk and other Net-SNMP CLI tools. --- ext/snmp/snmp.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index f54f4fd99ed43..a2ebb46d2da5b 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -886,6 +886,10 @@ static bool netsnmp_session_init(php_snmp_session **session_p, int version, zend return false; } + /* Save hostname before clearing peername, since host_ptr may point into the same buffer */ + char saved_hostname[MAX_NAME_LEN]; + strlcpy(saved_hostname, host_ptr, MAX_NAME_LEN); + /* we have everything we need in psal, flush peername and fill it properly */ *(session->peername) = '\0'; res = psal; @@ -896,6 +900,13 @@ static bool netsnmp_session_init(php_snmp_session **session_p, int version, zend res++; continue; } + /* When IPv6 is not explicitly requested, prefer IPv4 results to avoid + issues on systems where getaddrinfo() returns AF_INET6 mapped addresses + but IPv6 networking is not actually available */ + if (!force_ipv6 && (*res)->sa_family == AF_INET6) { + res++; + continue; + } if ((*res)->sa_family == AF_INET6) { if (inet_ntop((*res)->sa_family, &(((struct sockaddr_in6*)(*res))->sin6_addr), name, sizeof(name))) { snprintf(pptr, MAX_NAME_LEN, "udp6:[%s]", name); @@ -910,13 +921,11 @@ static bool netsnmp_session_init(php_snmp_session **session_p, int version, zend break; } - if (strlen(session->peername) == 0) { - php_error_docref(NULL, E_WARNING, "Unknown failure while resolving '%s'", ZSTR_VAL(hostname)); - return false; + /* If no suitable address was found, fall back to the original hostname + and let Net-SNMP resolve it (matches behavior of snmprealwalk et al.) */ + if (session->peername[0] == '\0') { + strlcpy(session->peername, saved_hostname, MAX_NAME_LEN); } - /* XXX FIXME - There should be check for non-empty session->peername! - */ /* put back non-standard SNMP port */ if (remote_port != SNMP_PORT) {