From 6db12e7cd89fa4643f42a884e8323a3aef1b52cc Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 7 Sep 2025 21:08:10 +0200 Subject: [PATCH] Fix bug #67563: mysqli compiled with mysqlnd does not take ipv6 adress as parameter In the past, when libmysqlclient could be used, it accepted ipv6 addresses as hostname without enclosing it first in brackets. However, in mysqlnd this never worked. In the past this caused a discrepancy between the two implementations. Nowadays, mysqli only works with mysqlnd so we don't even have to cater to libmysqlclient. However, a plain ipv6 address should still work as a hostname. Also for people migrating to newer PHP versions it's nice if this keeps working. The solution is to check if we're dealing with an ipv6 address not yet enclosed in brackets. In that case we add the brackets automatically. Closes GH-19750. --- NEWS | 4 ++++ ext/mysqli/tests/bug67563.phpt | 40 ++++++++++++++++++++++++++++++++ ext/mysqlnd/mysqlnd_connection.c | 18 +++++++++++++- 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 ext/mysqli/tests/bug67563.phpt diff --git a/NEWS b/NEWS index 58d9ebc69ad..067cceab3be 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,10 @@ PHP NEWS - GD: . FIxed GH-19955 (imagefttext() memory leak). (David Carlier) +- MySQLnd: + . Fixed bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress + as parameter). (nielsdos) + - SimpleXML: . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). (nielsdos) diff --git a/ext/mysqli/tests/bug67563.phpt b/ext/mysqli/tests/bug67563.phpt new file mode 100644 index 00000000000..72a51ecdccd --- /dev/null +++ b/ext/mysqli/tests/bug67563.phpt @@ -0,0 +1,40 @@ +--TEST-- +Fix bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress as parameter) +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--INI-- +max_execution_time=240 +--FILE-- +close(); + } +} + +print "done!"; +?> +--EXPECT-- +done! diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index d46029889fa..a97f2820a31 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -513,6 +513,16 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn, } /* }}} */ +/* ipv6 addresses have at least two colons, which is how we can differentiate between domain names and addresses */ +static bool mysqlnd_fast_is_ipv6_address(const char *s) +{ + const char *first_colon = strchr(s, ':'); + if (!first_colon) { + return false; + } + return strchr(first_colon + 1, ':') != NULL; +} + /* {{{ mysqlnd_conn_data::get_scheme */ static MYSQLND_STRING MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING *socket_or_pipe, unsigned int port, bool * unix_socket, bool * named_pipe) @@ -542,7 +552,13 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_ if (!port) { port = 3306; } - transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port); + + /* ipv6 addresses are in the format [address]:port */ + if (hostname.s[0] != '[' && mysqlnd_fast_is_ipv6_address(hostname.s)) { + transport.l = mnd_sprintf(&transport.s, 0, "tcp://[%s]:%u", hostname.s, port); + } else { + transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port); + } } DBG_INF_FMT("transport=%s", transport.s? transport.s:"OOM"); DBG_RETURN(transport);