mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Add so_reuseaddr stream socket context option
This is to allow disabling of SO_REUSEADDR that is enabled by default. To achieve better compatibility on Windows SO_EXCLUSIVEADDRUSE is set if so_reuseaddr is false. Closes GH-19967
This commit is contained in:
4
NEWS
4
NEWS
@@ -33,6 +33,10 @@ PHP NEWS
|
||||
. Fixed bug GH-19926 (reset internal pointer earlier while splicing array
|
||||
while COW violation flag is still set). (alexandre-daubois)
|
||||
|
||||
- Streams:
|
||||
. Added so_reuseaddr streams context socket option that allows disabling
|
||||
address resuse.
|
||||
|
||||
- Zip:
|
||||
. Fixed ZipArchive callback being called after executor has shut down.
|
||||
(ilutov)
|
||||
|
||||
@@ -36,6 +36,12 @@ PHP 8.6 UPGRADE NOTES
|
||||
IntlNumberRangeFormatter::IDENTITY_FALLBACK_SINGLE_VALUE, IntlNumberRangeFormatter::IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE, IntlNumberRangeFormatter::IDENTITY_FALLBACK_APPROXIMATELY and
|
||||
IntlNumberRangeFormatter::IDENTITY_FALLBACK_RANGE identity fallbacks.
|
||||
It is supported from icu 63.
|
||||
|
||||
- Streams:
|
||||
. Added stream socket context option so_reuseaddr that allows disabling
|
||||
address reuse (SO_REUSEADDR) and explicitly uses SO_EXCLUSIVEADDRUSE on
|
||||
Windows.
|
||||
|
||||
========================================
|
||||
3. Changes in SAPI modules
|
||||
========================================
|
||||
|
||||
73
ext/standard/tests/network/so_reuseaddr.phpt
Normal file
73
ext/standard/tests/network/so_reuseaddr.phpt
Normal file
@@ -0,0 +1,73 @@
|
||||
--TEST--
|
||||
stream_socket_server() SO_REUSEADDR context option test
|
||||
--FILE--
|
||||
<?php
|
||||
$is_win = substr(PHP_OS, 0, 3) == "WIN";
|
||||
// Test default behavior (SO_REUSEADDR enabled)
|
||||
$server1 = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);
|
||||
if (!$server1) {
|
||||
die('Unable to create server3');
|
||||
}
|
||||
|
||||
$addr = stream_socket_get_name($server1, false);
|
||||
$port = (int)substr(strrchr($addr, ':'), 1);
|
||||
|
||||
$client1 = stream_socket_client("tcp://127.0.0.1:$port");
|
||||
$accepted = stream_socket_accept($server1, 1);
|
||||
|
||||
// Force real TCP connection with data
|
||||
fwrite($client1, "test");
|
||||
fread($accepted, 4);
|
||||
fwrite($accepted, "response");
|
||||
fread($client1, 8);
|
||||
|
||||
fclose($client1);
|
||||
if (!$is_win) { // Windows would always succeed if server is closed
|
||||
fclose($server1);
|
||||
}
|
||||
|
||||
$server2 = @stream_socket_server("tcp://127.0.0.1:$port", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);
|
||||
if ($server2) {
|
||||
echo "Default: Server restart succeeded\n";
|
||||
fclose($server2);
|
||||
} else {
|
||||
echo "Default: Server restart failed\n";
|
||||
}
|
||||
|
||||
// Test with SO_REUSEADDR explicitly disabled
|
||||
$context = stream_context_create(['socket' => ['so_reuseaddr' => false]]);
|
||||
$server3 = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context);
|
||||
if (!$server3) {
|
||||
die('Unable to create server3');
|
||||
}
|
||||
|
||||
$addr = stream_socket_get_name($server3, false);
|
||||
$port = (int)substr(strrchr($addr, ':'), 1);
|
||||
|
||||
$client3 = stream_socket_client("tcp://127.0.0.1:$port");
|
||||
$accepted = stream_socket_accept($server3, 1);
|
||||
|
||||
// Force real TCP connection with data
|
||||
fwrite($client3, "test");
|
||||
fread($accepted, 4);
|
||||
fwrite($accepted, "response");
|
||||
fread($client3, 8);
|
||||
|
||||
// Client closes first (becomes active closer)
|
||||
fclose($client3); // This enters TIME_WAIT
|
||||
if (!$is_win) { // Windows would always succeed if server is closed
|
||||
fclose($server3);
|
||||
}
|
||||
|
||||
// Try immediate bind with SO_REUSEADDR disabled (should fail)
|
||||
$server4 = @stream_socket_server("tcp://127.0.0.1:$port", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context);
|
||||
if ($server4) {
|
||||
echo "Disabled: Server restart succeeded\n";
|
||||
fclose($server4);
|
||||
} else {
|
||||
echo "Disabled: Server restart failed\n";
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
Default: Server restart succeeded
|
||||
Disabled: Server restart failed
|
||||
@@ -496,8 +496,13 @@ php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned po
|
||||
|
||||
/* attempt to bind */
|
||||
|
||||
#ifdef SO_REUSEADDR
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&sockoptval, sizeof(sockoptval));
|
||||
if (sockopts & STREAM_SOCKOP_SO_REUSEADDR) {
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&sockoptval, sizeof(sockoptval));
|
||||
}
|
||||
#ifdef PHP_WIN32
|
||||
else {
|
||||
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&sockoptval, sizeof(sockoptval));
|
||||
}
|
||||
#endif
|
||||
#ifdef IPV6_V6ONLY
|
||||
if (sockopts & STREAM_SOCKOP_IPV6_V6ONLY) {
|
||||
|
||||
@@ -123,7 +123,7 @@ typedef int php_socket_t;
|
||||
#define STREAM_SOCKOP_IPV6_V6ONLY (1 << 3)
|
||||
#define STREAM_SOCKOP_IPV6_V6ONLY_ENABLED (1 << 4)
|
||||
#define STREAM_SOCKOP_TCP_NODELAY (1 << 5)
|
||||
|
||||
#define STREAM_SOCKOP_SO_REUSEADDR (1 << 6)
|
||||
|
||||
/* uncomment this to debug poll(2) emulation on systems that have poll(2) */
|
||||
/* #define PHP_USE_POLL_2_EMULATION 1 */
|
||||
|
||||
@@ -718,6 +718,16 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SO_REUSEADDR
|
||||
/* SO_REUSEADDR is enabled by default so this option is just to disable it if set to false. */
|
||||
if (!PHP_STREAM_CONTEXT(stream)
|
||||
|| (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_reuseaddr")) == NULL
|
||||
|| zend_is_true(tmpzval)
|
||||
) {
|
||||
sockopts |= STREAM_SOCKOP_SO_REUSEADDR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SO_BROADCAST
|
||||
if (stream->ops == &php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
|
||||
&& PHP_STREAM_CONTEXT(stream)
|
||||
|
||||
Reference in New Issue
Block a user