1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Add stream socket keepalive context options

This adds so_keepalive, tcp_keepidle, tcp_keepintvl and tcp_keepcnt
stream socket context options that are used to set their upper case
C macro variants by setsockopt function.

The test requires sockets extension and just tests that the values are
being set. This is because a real test would be slow and difficult to
show that those options really work due to how they work internally.

Closes GH-20381
This commit is contained in:
Jakub Zelenka
2025-11-03 22:22:01 +01:00
parent bd8da04def
commit 9582d8e6d7
6 changed files with 378 additions and 28 deletions

2
NEWS
View File

@@ -73,6 +73,8 @@ PHP NEWS
while COW violation flag is still set). (alexandre-daubois)
- Streams:
. Added so_keepalive, tcp_keepidle, tcp_keepintvl and tcp_keepcnt stream
socket context options.
. Added so_reuseaddr streams context socket option that allows disabling
address resuse.
. Fixed bug GH-20370 (User stream filters could violate typed property

View File

@@ -46,6 +46,9 @@ PHP 8.6 UPGRADE NOTES
. Added stream socket context option so_reuseaddr that allows disabling
address reuse (SO_REUSEADDR) and explicitly uses SO_EXCLUSIVEADDRUSE on
Windows.
. Added stream socket context options so_keepalive, tcp_keepidle,
tcp_keepintvl and tcp_keepcnt that allow setting socket keepalive
options.
========================================
3. Changes in SAPI modules

View File

@@ -0,0 +1,151 @@
--TEST--
stream_socket_server() and stream_socket_client() SO_KEEPALIVE context option test
--EXTENSIONS--
sockets
--SKIPIF--
<?php
if (!defined('TCP_KEEPIDLE') && !defined('TCP_KEEPALIVE')) {
die('skip TCP_KEEPIDLE/TCP_KEEPALIVE not available');
}
if (!defined('TCP_KEEPINTVL')) {
die('skip TCP_KEEPINTVL not available');
}
if (!defined('TCP_KEEPCNT')) {
die('skip TCP_KEEPCNT not available');
}
?>
--FILE--
<?php
// Test server with SO_KEEPALIVE enabled
$server_context = stream_context_create([
'socket' => [
'so_keepalive' => true,
'tcp_keepidle' => 60,
'tcp_keepintvl' => 10,
'tcp_keepcnt' => 5,
]
]);
$server = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $server_context);
if (!$server) {
die('Unable to create server');
}
$addr = stream_socket_get_name($server, false);
$port = (int)substr(strrchr($addr, ':'), 1);
// Test client with SO_KEEPALIVE enabled
$client_context = stream_context_create([
'socket' => [
'so_keepalive' => true,
'tcp_keepidle' => 30,
'tcp_keepintvl' => 5,
'tcp_keepcnt' => 3,
]
]);
$client = stream_socket_client("tcp://127.0.0.1:$port", $errno, $errstr, 30,
STREAM_CLIENT_CONNECT, $client_context);
if (!$client) {
die('Unable to create client');
}
$accepted = stream_socket_accept($server, 1);
if (!$accepted) {
die('Unable to accept connection');
}
// Verify server side (accepted connection)
$server_sock = socket_import_stream($accepted);
$server_keepalive = socket_get_option($server_sock, SOL_SOCKET, SO_KEEPALIVE);
echo "Server SO_KEEPALIVE: " . ($server_keepalive ? "enabled" : "disabled") . "\n";
if (defined('TCP_KEEPIDLE')) {
$server_idle = socket_get_option($server_sock, SOL_TCP, TCP_KEEPIDLE);
echo "Server TCP_KEEPIDLE: $server_idle\n";
} else {
$server_idle = socket_get_option($server_sock, SOL_TCP, TCP_KEEPALIVE);
echo "Server TCP_KEEPIDLE: $server_idle\n";
}
$server_intvl = socket_get_option($server_sock, SOL_TCP, TCP_KEEPINTVL);
echo "Server TCP_KEEPINTVL: $server_intvl\n";
$server_cnt = socket_get_option($server_sock, SOL_TCP, TCP_KEEPCNT);
echo "Server TCP_KEEPCNT: $server_cnt\n";
// Verify client side
$client_sock = socket_import_stream($client);
$client_keepalive = socket_get_option($client_sock, SOL_SOCKET, SO_KEEPALIVE);
echo "Client SO_KEEPALIVE: " . ($client_keepalive ? "enabled" : "disabled") . "\n";
if (defined('TCP_KEEPIDLE')) {
$client_idle = socket_get_option($client_sock, SOL_TCP, TCP_KEEPIDLE);
echo "Client TCP_KEEPIDLE: $client_idle\n";
} else {
$client_idle = socket_get_option($client_sock, SOL_TCP, TCP_KEEPALIVE);
echo "Client TCP_KEEPIDLE: $client_idle\n";
}
$client_intvl = socket_get_option($client_sock, SOL_TCP, TCP_KEEPINTVL);
echo "Client TCP_KEEPINTVL: $client_intvl\n";
$client_cnt = socket_get_option($client_sock, SOL_TCP, TCP_KEEPCNT);
echo "Client TCP_KEEPCNT: $client_cnt\n";
fclose($accepted);
fclose($client);
fclose($server);
// Test server with SO_KEEPALIVE disabled
$server2_context = stream_context_create(['socket' => ['so_keepalive' => false]]);
$server2 = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $server2_context);
$addr2 = stream_socket_get_name($server2, false);
$port2 = (int)substr(strrchr($addr2, ':'), 1);
$client2 = stream_socket_client("tcp://127.0.0.1:$port2");
$accepted2 = stream_socket_accept($server2, 1);
$server2_sock = socket_import_stream($accepted2);
$server2_keepalive = socket_get_option($server2_sock, SOL_SOCKET, SO_KEEPALIVE);
echo "Server disabled SO_KEEPALIVE: " . ($server2_keepalive ? "enabled" : "disabled") . "\n";
fclose($accepted2);
fclose($client2);
fclose($server2);
// Test client with SO_KEEPALIVE disabled
$server3 = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);
$addr3 = stream_socket_get_name($server3, false);
$port3 = (int)substr(strrchr($addr3, ':'), 1);
$client3_context = stream_context_create(['socket' => ['so_keepalive' => false]]);
$client3 = stream_socket_client("tcp://127.0.0.1:$port3", $errno, $errstr, 30,
STREAM_CLIENT_CONNECT, $client3_context);
$client3_sock = socket_import_stream($client3);
$client3_keepalive = socket_get_option($client3_sock, SOL_SOCKET, SO_KEEPALIVE);
echo "Client disabled SO_KEEPALIVE: " . ($client3_keepalive ? "enabled" : "disabled") . "\n";
fclose($client3);
fclose($server3);
?>
--EXPECT--
Server SO_KEEPALIVE: enabled
Server TCP_KEEPIDLE: 60
Server TCP_KEEPINTVL: 10
Server TCP_KEEPCNT: 5
Client SO_KEEPALIVE: enabled
Client TCP_KEEPIDLE: 30
Client TCP_KEEPINTVL: 5
Client TCP_KEEPCNT: 3
Server disabled SO_KEEPALIVE: disabled
Client disabled SO_KEEPALIVE: disabled

View File

@@ -452,9 +452,9 @@ ok:
/* Bind to a local IP address.
* Returns the bound socket, or -1 on failure.
* */
/* {{{ php_network_bind_socket_to_local_addr */
php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port,
int socktype, long sockopts, zend_string **error_string, int *error_code
php_socket_t php_network_bind_socket_to_local_addr_ex(const char *host, unsigned port,
int socktype, long sockopts, php_sockvals *sockvals, zend_string **error_string,
int *error_code
)
{
int num_addrs, n, err = 0;
@@ -533,6 +533,35 @@ php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned po
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&sockoptval, sizeof(sockoptval));
}
#endif
#ifdef SO_KEEPALIVE
if (sockopts & STREAM_SOCKOP_SO_KEEPALIVE) {
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&sockoptval, sizeof(sockoptval));
}
#endif
/* Set socket values if provided */
if (sockvals != NULL) {
#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
}
n = bind(sock, sa, socklen);
@@ -560,7 +589,13 @@ bound:
return sock;
}
/* }}} */
php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port,
int socktype, long sockopts, zend_string **error_string, int *error_code
)
{
return php_network_bind_socket_to_local_addr_ex(host, port, socktype, sockopts, NULL, error_string, error_code);
}
PHPAPI zend_result php_network_parse_network_address_with_port(const char *addr, size_t addrlen, struct sockaddr *sa, socklen_t *sl)
{
@@ -824,11 +859,9 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
* enable non-blocking mode on the socket.
* Returns the connected (or connecting) socket, or -1 on failure.
* */
/* {{{ php_network_connect_socket_to_host */
php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short port,
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
int *error_code, const char *bindto, unsigned short bindport, long sockopts, php_sockvals *sockvals
)
{
int num_addrs, n, fatal = 0;
@@ -952,6 +985,40 @@ php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short
}
}
#endif
#ifdef SO_KEEPALIVE
{
int val = 1;
if (sockopts & STREAM_SOCKOP_SO_KEEPALIVE) {
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&val, sizeof(val));
}
}
#endif
/* Set socket values if provided */
if (sockvals != NULL) {
#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
}
n = php_network_connect_socket(sock, sa, socklen, asynchronous,
timeout ? &working_timeout : NULL,
error_string, error_code);
@@ -998,7 +1065,15 @@ connected:
return sock;
}
/* }}} */
php_socket_t php_network_connect_socket_to_host(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
)
{
return php_network_connect_socket_to_host_ex(host, port, socktype, asynchronous, timeout,
error_string, error_code, bindto, bindport, sockopts, NULL);
}
/* {{{ php_any_addr
* Fills any (wildcard) address into php_sockaddr_storage

View File

@@ -124,6 +124,7 @@ typedef int php_socket_t;
#define STREAM_SOCKOP_IPV6_V6ONLY_ENABLED (1 << 4)
#define STREAM_SOCKOP_TCP_NODELAY (1 << 5)
#define STREAM_SOCKOP_SO_REUSEADDR (1 << 6)
#define STREAM_SOCKOP_SO_KEEPALIVE (1 << 7)
/* uncomment this to debug poll(2) emulation on systems that have poll(2) */
/* #define PHP_USE_POLL_2_EMULATION 1 */
@@ -266,10 +267,28 @@ typedef struct {
} php_sockaddr_storage;
#endif
#define PHP_SOCKVAL_TCP_KEEPIDLE (1 << 0)
#define PHP_SOCKVAL_TCP_KEEPCNT (1 << 1)
#define PHP_SOCKVAL_TCP_KEEPINTVL (1 << 2)
typedef struct {
unsigned int mask;
struct {
int keepidle;
int keepcnt;
int keepintvl;
} keepalive;
} php_sockvals;
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 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
);
PHPAPI php_socket_t php_network_connect_socket_to_host(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
@@ -286,6 +305,10 @@ PHPAPI int php_network_connect_socket(php_socket_t sockfd,
#define php_connect_nonb(sock, addr, addrlen, timeout) \
php_network_connect_socket((sock), (addr), (addrlen), 0, (timeout), NULL, NULL)
PHPAPI php_socket_t php_network_bind_socket_to_local_addr_ex(const char *host, unsigned port,
int socktype, long sockopts, php_sockvals *sockvals, zend_string **error_string, int *error_code
);
PHPAPI php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port,
int socktype, long sockopts, zend_string **error_string, int *error_code
);

View File

@@ -16,7 +16,7 @@
#include "php.h"
#include "ext/standard/file.h"
#include "streams/php_streams_int.h"
#include "php_streams.h"
#include "php_network.h"
#if defined(PHP_WIN32) || defined(__riscos__)
@@ -48,8 +48,18 @@ static const php_stream_ops php_stream_udp_socket_ops;
#ifdef AF_UNIX
static const php_stream_ops php_stream_unix_socket_ops;
static const php_stream_ops php_stream_unixdg_socket_ops;
#endif
#define PHP_STREAM_XPORT_IS_UNIX_DG(stream) php_stream_is(stream, &php_stream_unixdg_socket_ops)
#define PHP_STREAM_XPORT_IS_UNIX_ST(stream) php_stream_is(stream, &php_stream_unix_socket_ops)
#define PHP_STREAM_XPORT_IS_UNIX(stream) \
(PHP_STREAM_XPORT_IS_UNIX_DG(stream) || PHP_STREAM_XPORT_IS_UNIX_ST(stream))
#else
#define PHP_STREAM_XPORT_IS_UNIX_DG(stream) false
#define PHP_STREAM_XPORT_IS_UNIX_STD(stream) false
#define PHP_STREAM_XPORT_IS_UNIX(stream) false
#endif
#define PHP_STREAM_XPORT_IS_UDP(stream) (php_stream_is(stream, &php_stream_udp_socket_ops))
#define PHP_STREAM_XPORT_IS_TCP(stream) (!PHP_STREAM_XPORT_IS_UNIX(stream) && !PHP_STREAM_XPORT_IS_UDP(stream))
static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam);
@@ -669,18 +679,19 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *
int portno, err;
long sockopts = STREAM_SOCKOP_NONE;
zval *tmpzval = NULL;
php_sockvals sockvals = {0};
#ifdef AF_UNIX
if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
if (PHP_STREAM_XPORT_IS_UNIX(stream)) {
struct sockaddr_un unix_addr;
sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
sock->socket = socket(PF_UNIX, PHP_STREAM_XPORT_IS_UNIX_ST(stream) ? SOCK_STREAM : SOCK_DGRAM, 0);
if (sock->socket == SOCK_ERR) {
if (xparam->want_errortext) {
char errstr[256];
xparam->outputs.error_text = strpprintf(0, "Failed to create unix%s socket %s",
stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
PHP_STREAM_XPORT_IS_UNIX_ST(stream) ? "" : " datagram",
php_socket_strerror_s(errno, errstr, sizeof(errstr)));
}
return -1;
@@ -729,7 +740,7 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *
#endif
#ifdef SO_BROADCAST
if (stream->ops == &php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
if (PHP_STREAM_XPORT_IS_UDP(stream) /* SO_BROADCAST is only applicable for UDP */
&& PHP_STREAM_CONTEXT(stream)
&& (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
&& zend_is_true(tmpzval)
@@ -738,9 +749,53 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *
}
#endif
sock->socket = php_network_bind_socket_to_local_addr(host, portno,
stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
#ifdef SO_KEEPALIVE
if (PHP_STREAM_XPORT_IS_TCP(stream) /* SO_KEEPALIVE is only applicable for TCP */
&& PHP_STREAM_CONTEXT(stream)
&& (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_keepalive")) != NULL
&& zend_is_true(tmpzval)
) {
sockopts |= STREAM_SOCKOP_SO_KEEPALIVE;
}
#endif
/* Parse TCP keepalive parameters - only for TCP streams */
if (PHP_STREAM_XPORT_IS_TCP(stream)) {
#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE)
if (PHP_STREAM_CONTEXT(stream)
&& (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepidle")) != NULL
&& Z_TYPE_P(tmpzval) == IS_LONG
) {
sockvals.mask |= PHP_SOCKVAL_TCP_KEEPIDLE;
sockvals.keepalive.keepidle = (int)Z_LVAL_P(tmpzval);
}
#endif
#ifdef TCP_KEEPINTVL
if (PHP_STREAM_CONTEXT(stream)
&& (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepintvl")) != NULL
&& Z_TYPE_P(tmpzval) == IS_LONG
) {
sockvals.mask |= PHP_SOCKVAL_TCP_KEEPINTVL;
sockvals.keepalive.keepintvl = (int)Z_LVAL_P(tmpzval);
}
#endif
#ifdef TCP_KEEPCNT
if (PHP_STREAM_CONTEXT(stream)
&& (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepcnt")) != NULL
&& Z_TYPE_P(tmpzval) == IS_LONG
) {
sockvals.mask |= PHP_SOCKVAL_TCP_KEEPCNT;
sockvals.keepalive.keepcnt = (int)Z_LVAL_P(tmpzval);
}
#endif
}
sock->socket = php_network_bind_socket_to_local_addr_ex(host, portno,
PHP_STREAM_XPORT_IS_UDP(stream) ? SOCK_DGRAM : SOCK_STREAM,
sockopts,
sockvals.mask ? &sockvals : NULL,
xparam->want_errortext ? &xparam->outputs.error_text : NULL,
&err
);
@@ -761,12 +816,13 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
int ret;
zval *tmpzval = NULL;
long sockopts = STREAM_SOCKOP_NONE;
php_sockvals sockvals = {0};
#ifdef AF_UNIX
if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
if (PHP_STREAM_XPORT_IS_UNIX(stream)) {
struct sockaddr_un unix_addr;
sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
sock->socket = socket(PF_UNIX, PHP_STREAM_XPORT_IS_UNIX_ST(stream) ? SOCK_STREAM : SOCK_DGRAM, 0);
if (sock->socket == SOCK_ERR) {
if (xparam->want_errortext) {
@@ -807,7 +863,7 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
}
#ifdef SO_BROADCAST
if (stream->ops == &php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
if (PHP_STREAM_XPORT_IS_UDP(stream) /* SO_BROADCAST is only applicable for UDP */
&& PHP_STREAM_CONTEXT(stream)
&& (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
&& zend_is_true(tmpzval)
@@ -816,11 +872,7 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
}
#endif
if (stream->ops != &php_stream_udp_socket_ops /* TCP_NODELAY is only applicable for TCP */
#ifdef AF_UNIX
&& stream->ops != &php_stream_unix_socket_ops
&& stream->ops != &php_stream_unixdg_socket_ops
#endif
if (PHP_STREAM_XPORT_IS_TCP(stream) /* TCP_NODELAY is only applicable for TCP */
&& PHP_STREAM_CONTEXT(stream)
&& (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL
&& zend_is_true(tmpzval)
@@ -828,19 +880,63 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
sockopts |= STREAM_SOCKOP_TCP_NODELAY;
}
#ifdef SO_KEEPALIVE
if (PHP_STREAM_XPORT_IS_TCP(stream) /* SO_KEEPALIVE is only applicable for TCP */
&& PHP_STREAM_CONTEXT(stream)
&& (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_keepalive")) != NULL
&& zend_is_true(tmpzval)
) {
sockopts |= STREAM_SOCKOP_SO_KEEPALIVE;
}
#endif
/* Parse TCP keepalive parameters - only for TCP streams */
if (PHP_STREAM_XPORT_IS_TCP(stream)) {
#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE)
if (PHP_STREAM_CONTEXT(stream)
&& (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepidle")) != NULL
&& Z_TYPE_P(tmpzval) == IS_LONG
) {
sockvals.mask |= PHP_SOCKVAL_TCP_KEEPIDLE;
sockvals.keepalive.keepidle = (int)Z_LVAL_P(tmpzval);
}
#endif
#ifdef TCP_KEEPINTVL
if (PHP_STREAM_CONTEXT(stream)
&& (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepintvl")) != NULL
&& Z_TYPE_P(tmpzval) == IS_LONG
) {
sockvals.mask |= PHP_SOCKVAL_TCP_KEEPINTVL;
sockvals.keepalive.keepintvl = (int)Z_LVAL_P(tmpzval);
}
#endif
#ifdef TCP_KEEPCNT
if (PHP_STREAM_CONTEXT(stream)
&& (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepcnt")) != NULL
&& Z_TYPE_P(tmpzval) == IS_LONG
) {
sockvals.mask |= PHP_SOCKVAL_TCP_KEEPCNT;
sockvals.keepalive.keepcnt = (int)Z_LVAL_P(tmpzval);
}
#endif
}
/* Note: the test here for php_stream_udp_socket_ops is important, because we
* want the default to be TCP sockets so that the openssl extension can
* re-use this code. */
sock->socket = php_network_connect_socket_to_host(host, portno,
stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
sock->socket = php_network_connect_socket_to_host_ex(host, portno,
PHP_STREAM_XPORT_IS_UDP(stream) ? SOCK_DGRAM : SOCK_STREAM,
xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
xparam->inputs.timeout,
xparam->want_errortext ? &xparam->outputs.error_text : NULL,
&err,
bindto,
bindport,
sockopts
sockopts,
sockvals.mask ? &sockvals : NULL
);
ret = sock->socket == -1 ? -1 : 0;