Commit 44d6417bd7c for php.net

commit 44d6417bd7c12ae2e7fadd7cf950529743e5bb42
Author: David Carlier <devnexen@gmail.com>
Date:   Wed Nov 19 21:19:33 2025 +0000

    ext/sockets: GH-20532 socket_addrinfo_lookup() sets EAI error code on resolution failures with a new optional argument.

    close GH-20534

diff --git a/NEWS b/NEWS
index 74816c94a29..86a2174a986 100644
--- a/NEWS
+++ b/NEWS
@@ -86,6 +86,8 @@ PHP                                                                        NEWS
     transmitted data can remain unacknowledged. (James Lucas)
   . Added AF_UNSPEC support for sock_addrinfo_lookup() as a sole umbrella for
     AF_INET* family only. (David Carlier)
+  . Fixed GH-20532 (socket_addrinfo_lookup gives the error code with a new optional
+    parameter). (David Carlier)

 - SPL:
   . DirectoryIterator key can now work better with filesystem supporting larger
diff --git a/UPGRADING b/UPGRADING
index 3d0a56756cd..b72a6379664 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -77,6 +77,11 @@ PHP 8.6 UPGRADE NOTES
 - Phar:
   . Phar::mungServer() now supports reference values.

+- Sockets:
+  . socket_addrinfo_lookup() now has an additional optional argument $error
+    when not null, and on failure, gives the error code (one of the EAI_*
+    constants).
+
 - Zip:
   . ZipArchive::extractTo now raises a TypeError for the
     files argument if one or more of the entries is not
@@ -112,6 +117,23 @@ PHP 8.6 UPGRADE NOTES
 - Sockets:
   . TCP_USER_TIMEOUT (Linux only).
   . AF_UNSPEC.
+  . EAI_BADFLAGS.
+  . EAI_NONAME.
+  . EAI_AGAIN.
+  . EAI_FAIL.
+  . EAI_NODATA.
+  . EAI_FAMILY.
+  . EAI_SOCKTYPE.
+  . EAI_SERVICE.
+  . EAI_ADDRFAMILY.
+  . EAI_SYSTEM.
+  . EAI_OVERFLOW
+  . EAI_INPROGRESS.
+  . EAI_CANCELED.
+  . EAI_NOTCANCELED.
+  . EAI_ALLDONE.
+  . EAI_INTR.
+  . EAI_IDN_ENCODE.

 ========================================
 11. Changes to INI File Handling
diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c
index 54f862d5536..accaf4bbbcf 100644
--- a/ext/sockets/sockets.c
+++ b/ext/sockets/sockets.c
@@ -2751,16 +2751,18 @@ PHP_FUNCTION(socket_addrinfo_lookup)
 {
 	zend_string *service = NULL;
 	zend_string *hostname, *key;
-	zval *hint, *zhints = NULL;
+	zval *hint, *zhints = NULL, *error_code = NULL;
+	int ret = 0;

 	struct addrinfo hints, *result, *rp;
 	php_addrinfo *res;

-	ZEND_PARSE_PARAMETERS_START(1, 3)
+	ZEND_PARSE_PARAMETERS_START(1, 4)
 		Z_PARAM_STR(hostname)
 		Z_PARAM_OPTIONAL
 		Z_PARAM_STR_OR_NULL(service)
 		Z_PARAM_ARRAY(zhints)
+		Z_PARAM_ZVAL_OR_NULL(error_code)
 	ZEND_PARSE_PARAMETERS_END();

 	memset(&hints, 0, sizeof(hints));
@@ -2848,7 +2850,10 @@ PHP_FUNCTION(socket_addrinfo_lookup)
 		} ZEND_HASH_FOREACH_END();
 	}

-	if (getaddrinfo(ZSTR_VAL(hostname), service ? ZSTR_VAL(service) : NULL, &hints, &result) != 0) {
+	if ((ret = getaddrinfo(ZSTR_VAL(hostname), service ? ZSTR_VAL(service) : NULL, &hints, &result)) != 0) {
+		if (error_code) {
+			ZEND_TRY_ASSIGN_REF_LONG(error_code, ret);
+		}
 		RETURN_FALSE;
 	}

diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php
index 0f645da25ce..56b2ac07e86 100644
--- a/ext/sockets/sockets.stub.php
+++ b/ext/sockets/sockets.stub.php
@@ -2067,6 +2067,136 @@
 const SHUT_RDWR = UNKNOWN;
 #endif

+
+#ifdef EAI_BADFLAGS
+/**
+ * @var int
+ * @cvalue EAI_BADFLAGS
+ */
+const EAI_BADFLAGS = UNKNOWN;
+#endif
+#ifdef EAI_NONAME
+/**
+ * @var int
+ * @cvalue EAI_NONAME
+ */
+const EAI_NONAME = UNKNOWN;
+#endif
+#ifdef EAI_AGAIN
+/**
+ * @var int
+ * @cvalue EAI_AGAIN
+ */
+const EAI_AGAIN = UNKNOWN;
+#endif
+#ifdef EAI_FAIL
+/**
+ * @var int
+ * @cvalue EAI_FAIL
+ */
+const EAI_FAIL = UNKNOWN;
+#endif
+#ifdef EAI_NODATA
+/**
+ * @var int
+ * @cvalue EAI_NODATA
+ */
+const EAI_NODATA = UNKNOWN;
+#endif
+#ifdef EAI_FAMILY
+/**
+ * @var int
+ * @cvalue EAI_FAMILY
+ */
+const EAI_FAMILY = UNKNOWN;
+#endif
+#ifdef EAI_SOCKTYPE
+/**
+ * @var int
+ * @cvalue EAI_SOCKTYPE
+ */
+const EAI_SOCKTYPE = UNKNOWN;
+#endif
+#ifdef EAI_SERVICE
+/**
+ * @var int
+ * @cvalue EAI_SERVICE
+ */
+const EAI_SERVICE = UNKNOWN;
+#endif
+#ifdef EAI_ADDRFAMILY
+/**
+ * @var int
+ * @cvalue EAI_ADDRFAMILY
+ */
+const EAI_ADDRFAMILY = UNKNOWN;
+#else
+#ifdef EAI_FAMILY
+/**
+ * @var int
+ * @cvalue EAI_FAMILY
+ */
+const EAI_ADDRFAMILY = UNKNOWN;
+#else
+#endif
+#endif
+#ifdef EAI_SYSTEM
+/**
+ * @var int
+ * @cvalue EAI_SYSTEM
+ */
+const EAI_SYSTEM = UNKNOWN;
+#endif
+#ifdef EAI_OVERFLOW
+/**
+ * @var int
+ * @cvalue EAI_OVERFLOW
+ */
+const EAI_OVERFLOW = UNKNOWN;
+#endif
+#ifdef EAI_INPROGRESS
+/**
+ * @var int
+ * @cvalue EAI_INPROGRESS
+ */
+const EAI_INPROGRESS = UNKNOWN;
+#endif
+#ifdef EAI_CANCELED
+/**
+ * @var int
+ * @cvalue EAI_CANCELED
+ */
+const EAI_CANCELED = UNKNOWN;
+#endif
+#ifdef EAI_NOTCANCELED
+/**
+ * @var int
+ * @cvalue EAI_NOTCANCELED
+ */
+const EAI_NOTCANCELED = UNKNOWN;
+#endif
+#ifdef EAI_ALLDONE
+/**
+ * @var int
+ * @cvalue EAI_ALLDONE
+ */
+const EAI_ALLDONE = UNKNOWN;
+#endif
+#ifdef EAI_INTR
+/**
+ * @var int
+ * @cvalue EAI_INTR
+ */
+const EAI_INTR = UNKNOWN;
+#endif
+#ifdef EAI_IDN_ENCODE
+/**
+ * @var int
+ * @cvalue EAI_IDN_ENCODE
+ */
+const EAI_IDN_ENCODE = UNKNOWN;
+#endif
+
 /**
  * @strict-properties
  * @not-serializable
@@ -2187,9 +2317,10 @@ function socket_cmsg_space(int $level, int $type, int $num = 0): ?int {}

 /**
  * @return array<int, AddressInfo>|false
+ * @param int $error_code
  * @refcount 1
  */
-function socket_addrinfo_lookup(string $host, ?string $service = null, array $hints = []): array|false {}
+function socket_addrinfo_lookup(string $host, ?string $service = null, array $hints = [], &$error_code = null): array|false {}

 function socket_addrinfo_connect(AddressInfo $address): Socket|false {}

diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h
index 50a81c5ee3a..522f356f853 100644
Binary files a/ext/sockets/sockets_arginfo.h and b/ext/sockets/sockets_arginfo.h differ
diff --git a/ext/sockets/tests/gh20532.phpt b/ext/sockets/tests/gh20532.phpt
new file mode 100644
index 00000000000..f3368c83036
--- /dev/null
+++ b/ext/sockets/tests/gh20532.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-20562 - socket_addrinfo_lookup() returns error codes on resolution failures.
+--EXTENSIONS--
+sockets
+--FILE--
+<?php
+$error_code = 0;
+var_dump(socket_addrinfo_lookup(".whynot", null, [], $error_code) === false && $error_code === EAI_NONAME);
+var_dump(socket_addrinfo_lookup("2001:db8::1", null, ['ai_family' => AF_INET], $error_code) === false && in_array($error_code, [EAI_FAMILY, EAI_ADDRFAMILY, EAI_NONAME, EAI_NODATA]));
+var_dump(socket_addrinfo_lookup("example.com", "http", ['ai_socktype' => SOCK_RAW, 'ai_flags' => 2147483647], $error_code) === false && in_array($error_code, [EAI_SOCKTYPE, EAI_SERVICE, EAI_BADFLAGS, EAI_NONAME]));
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+