Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -488,11 +488,11 @@ PHP 8.6 UPGRADE NOTES
linger time is limited to an unsigned short on some platforms.
. Added stream socket context options so_rcvbuf and so_sndbuf that set the
socket receive and send buffer sizes in bytes (SO_RCVBUF and SO_SNDBUF) on
TCP and UDP sockets. The value must be an integer between 1 and 2147483647,
any other value makes the stream creation fail. The operating system may
round, cap or otherwise adjust the requested size, and may stop sizing that
buffer automatically, so the size read back can differ from the one
requested.
TCP, UDP, unix and unix datagram sockets. The value must be an integer
between 1 and 2147483647, any other value makes the stream creation fail.
The operating system may round, cap or otherwise adjust the requested size,
and may stop sizing that buffer automatically, so the size read back can
differ from the one requested.
. Allowed casting filtered streams as file descriptors for select.
. Added the "write_seek_mode" filter parameter for the bz2, iconv,
zlib, and string stream filters. This parameter must be set via an
Expand Down
58 changes: 58 additions & 0 deletions ext/standard/tests/network/so_rcvbuf_sndbuf_udg.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
--TEST--
stream_socket_server() SO_RCVBUF and SO_SNDBUF context options test with unix datagram sockets
--EXTENSIONS--
sockets
--SKIPIF--
<?php
if (array_search("udg", stream_get_transports()) === false) {
die('skip No support for UNIX domain sockets.');
}
?>
--FILE--
<?php
function buffers($stream): array {
$sock = socket_import_stream($stream);
return [
socket_get_option($sock, SOL_SOCKET, SO_RCVBUF),
socket_get_option($sock, SOL_SOCKET, SO_SNDBUF),
];
}

$control_path = sys_get_temp_dir() . '/' . uniqid('so_buf_control_') . '.sock';
$server_path = sys_get_temp_dir() . '/' . uniqid('so_buf_server_') . '.sock';

$control = stream_socket_server("udg://$control_path", $errno, $errstr, STREAM_SERVER_BIND);

if (!$control) {
die('Unable to create server');
}

[$rcvbuf, $sndbuf] = buffers($control);

// Shrinking is always honoured, while growing may be capped by the system maximum.
$context = stream_context_create(['socket' => [
'so_rcvbuf' => intdiv($rcvbuf, 4),
'so_sndbuf' => intdiv($sndbuf, 4),
]]);

$server = stream_socket_server("udg://$server_path", $errno, $errstr, STREAM_SERVER_BIND, $context);

if (!$server) {
die('Unable to create server');
}

[$server_rcvbuf, $server_sndbuf] = buffers($server);
echo "Server buffers", PHP_EOL;
var_dump($server_rcvbuf < $rcvbuf);
var_dump($server_sndbuf < $sndbuf);

fclose($server);
fclose($control);
unlink($server_path);
unlink($control_path);

?>
--EXPECT--
Server buffers
bool(true)
bool(true)
108 changes: 108 additions & 0 deletions ext/standard/tests/network/so_rcvbuf_sndbuf_unix.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
--TEST--
stream_socket_server() and stream_socket_client() SO_RCVBUF and SO_SNDBUF context options test with unix sockets
--EXTENSIONS--
sockets
--SKIPIF--
<?php
if (array_search("unix", stream_get_transports()) === false) {
die('skip No support for UNIX domain sockets.');
}
?>
--FILE--
<?php
function buffers($stream): array {
$sock = socket_import_stream($stream);
return [
socket_get_option($sock, SOL_SOCKET, SO_RCVBUF),
socket_get_option($sock, SOL_SOCKET, SO_SNDBUF),
];
}

$control_path = sys_get_temp_dir() . '/' . uniqid('so_buf_control_') . '.sock';
$server_path = sys_get_temp_dir() . '/' . uniqid('so_buf_server_') . '.sock';

$control = stream_socket_server("unix://$control_path", $errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);

if (!$control) {
die('Unable to create server');
}

$control_client = stream_socket_client("unix://$control_path", $errno, $errstr, 30);

if (!$control_client) {
die('Unable to create client');
}

[$rcvbuf, $sndbuf] = buffers($control);
[$client_rcvbuf, $client_sndbuf] = buffers($control_client);

// Shrinking is always honoured, while growing may be capped by the system maximum.
function context(int $rcvbuf, int $sndbuf) {
return stream_context_create(['socket' => [
'so_rcvbuf' => intdiv($rcvbuf, 4),
'so_sndbuf' => intdiv($sndbuf, 4),
]]);
}

$server = stream_socket_server("unix://$server_path", $errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, context($rcvbuf, $sndbuf));

if (!$server) {
die('Unable to create server');
}

echo "Listen buffers", PHP_EOL;
[$listen_rcvbuf, $listen_sndbuf] = buffers($server);
var_dump($listen_rcvbuf < $rcvbuf);
var_dump($listen_sndbuf < $sndbuf);

$client = stream_socket_client("unix://$server_path", $errno, $errstr, 30,
STREAM_CLIENT_CONNECT, context($client_rcvbuf, $client_sndbuf));

if (!$client) {
die('Unable to create client');
}

$accepted = stream_socket_accept($server, 1);

if (!$accepted) {
die('Unable to accept connection');
}

echo "Client buffers", PHP_EOL;
[$rcvbuf2, $sndbuf2] = buffers($client);
var_dump($rcvbuf2 < $client_rcvbuf);
var_dump($sndbuf2 < $client_sndbuf);

$invalid = stream_context_create(['socket' => ['so_rcvbuf' => 0]]);

echo "Invalid buffer size", PHP_EOL;
var_dump(@stream_socket_server("unix://$server_path.invalid", $errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $invalid));
echo $errstr, PHP_EOL;
var_dump(@stream_socket_client("unix://$server_path", $errno, $errstr, 30,
STREAM_CLIENT_CONNECT, $invalid));
echo $errstr, PHP_EOL;

fclose($accepted);
fclose($client);
fclose($server);
fclose($control_client);
fclose($control);
unlink($server_path);
unlink($control_path);

?>
--EXPECT--
Listen buffers
bool(true)
bool(true)
Client buffers
bool(true)
bool(true)
Invalid buffer size
bool(false)
so_rcvbuf context option must be between 1 and 2147483647
bool(false)
so_rcvbuf context option must be between 1 and 2147483647
118 changes: 37 additions & 81 deletions main/network.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,8 +443,42 @@ PHPAPI int php_network_connect_socket(php_socket_t sockfd,
}
/* }}} */

static void php_network_set_socket_buffers(php_socket_t sock, const php_sockvals *sockvals)
PHPAPI void php_network_apply_sockvals(php_socket_t sock, const php_sockvals *sockvals)
{
#ifdef SO_LINGER
if (sockvals->mask & PHP_SOCKVAL_SO_LINGER) {
unsigned short secs = sockvals->linger > USHRT_MAX
? USHRT_MAX : (unsigned short)sockvals->linger;
struct linger linger_val = {
.l_onoff = (sockvals->linger > 0),
.l_linger = sockvals->linger > 0 ? secs : 0
};
#ifdef SO_LINGER_SEC
setsockopt(sock, SOL_SOCKET, SO_LINGER_SEC, (char*)&linger_val, sizeof(linger_val));
#else
setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&linger_val, sizeof(linger_val));
#endif
}
#endif
#if defined(TCP_KEEPIDLE)
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
}
#elif defined(TCP_KEEPALIVE)
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
}
#endif
#ifdef TCP_KEEPINTVL
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPINTVL) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&sockvals->keepalive.keepintvl, sizeof(sockvals->keepalive.keepintvl));
}
#endif
#ifdef TCP_KEEPCNT
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPCNT) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (char*)&sockvals->keepalive.keepcnt, sizeof(sockvals->keepalive.keepcnt));
}
#endif
#ifdef SO_RCVBUF
if (sockvals->mask & PHP_SOCKVAL_SO_RCVBUF) {
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&sockvals->rcvbuf, sizeof(sockvals->rcvbuf));
Expand Down Expand Up @@ -547,47 +581,8 @@ php_socket_t php_network_bind_socket_to_local_addr_ex(const char *host, unsigned
}
#endif

/* Set socket values if provided */
if (sockvals != NULL) {
#ifdef SO_LINGER
if (sockvals->mask & PHP_SOCKVAL_SO_LINGER) {
/* l_linger is an unsigned short on Windows, so clamp rather than
* truncate: a truncated value may still be in range and would then
* be applied silently (e.g. 65536 becoming 0, an abortive close). */
unsigned short secs = sockvals->linger > USHRT_MAX
? USHRT_MAX : (unsigned short)sockvals->linger;
struct linger linger_val = {
.l_onoff = (sockvals->linger > 0),
.l_linger = sockvals->linger > 0 ? secs : 0
};
#ifdef SO_LINGER_SEC
setsockopt(sock, SOL_SOCKET, SO_LINGER_SEC, (char*)&linger_val, sizeof(linger_val));
#else
setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&linger_val, sizeof(linger_val));
#endif
}
#endif
#if defined(TCP_KEEPIDLE)
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
}
#elif defined(TCP_KEEPALIVE)
/* macOS uses TCP_KEEPALIVE instead of TCP_KEEPIDLE */
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
}
#endif
#ifdef TCP_KEEPINTVL
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPINTVL) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&sockvals->keepalive.keepintvl, sizeof(sockvals->keepalive.keepintvl));
}
#endif
#ifdef TCP_KEEPCNT
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPCNT) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (char*)&sockvals->keepalive.keepcnt, sizeof(sockvals->keepalive.keepcnt));
}
#endif
php_network_set_socket_buffers(sock, sockvals);
php_network_apply_sockvals(sock, sockvals);
}

n = bind(sock, sa, socklen);
Expand Down Expand Up @@ -1052,47 +1047,8 @@ php_socket_t php_network_connect_socket_to_host_ex(const char *host, unsigned sh
}
#endif

/* Set socket values if provided */
if (sockvals != NULL) {
#ifdef SO_LINGER
if (sockvals->mask & PHP_SOCKVAL_SO_LINGER) {
/* l_linger is an unsigned short on Windows, so clamp rather than
* truncate: a truncated value may still be in range and would then
* be applied silently (e.g. 65536 becoming 0, an abortive close). */
unsigned short secs = sockvals->linger > USHRT_MAX
? USHRT_MAX : (unsigned short)sockvals->linger;
struct linger linger_val = {
.l_onoff = (sockvals->linger > 0),
.l_linger = sockvals->linger > 0 ? secs : 0
};
#ifdef SO_LINGER_SEC
setsockopt(sock, SOL_SOCKET, SO_LINGER_SEC, (char*)&linger_val, sizeof(linger_val));
#else
setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&linger_val, sizeof(linger_val));
#endif
}
#endif
#if defined(TCP_KEEPIDLE)
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
}
#elif defined(TCP_KEEPALIVE)
/* macOS uses TCP_KEEPALIVE instead of TCP_KEEPIDLE */
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
}
#endif
#ifdef TCP_KEEPINTVL
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPINTVL) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&sockvals->keepalive.keepintvl, sizeof(sockvals->keepalive.keepintvl));
}
#endif
#ifdef TCP_KEEPCNT
if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPCNT) {
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (char*)&sockvals->keepalive.keepcnt, sizeof(sockvals->keepalive.keepcnt));
}
#endif
php_network_set_socket_buffers(sock, sockvals);
php_network_apply_sockvals(sock, sockvals);
}

n = php_network_connect_socket(sock, sa, socklen, asynchronous,
Expand Down
2 changes: 2 additions & 0 deletions main/php_network.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ BEGIN_EXTERN_C()
PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string);
PHPAPI void php_network_freeaddresses(struct sockaddr **sal);

PHPAPI void php_network_apply_sockvals(php_socket_t sock, const php_sockvals *sockvals);

PHPAPI php_socket_t php_network_connect_socket_to_host_ex(const char *host, unsigned short port,
int socktype, int asynchronous, struct timeval *timeout, zend_string **error_string,
int *error_code, const char *bindto, unsigned short bindport, long sockopts, php_sockvals *sockvals
Expand Down
Loading
Loading