Commit 0fd8aae6e85 for php.net
commit 0fd8aae6e851b94123288ea67726ea68622c0c17
Author: Jakub Zelenka <bukka@php.net>
Date: Tue Dec 30 16:53:22 2025 +0100
Fix TCP_KEEPALIVE no inheriting for accepted sockets on MacOS
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index 62929246a07..482715c7455 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -2219,25 +2219,32 @@ static int php_openssl_sockop_stat(php_stream *stream, php_stream_statbuf *ssb)
static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock,
php_stream_xport_param *xparam STREAMS_DC) /* {{{ */
{
- bool nodelay = false;
+ php_sockvals sockvals = {0};
zval *tmpzval = NULL;
xparam->outputs.client = NULL;
- if (PHP_STREAM_CONTEXT(stream) &&
- (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
- zend_is_true(tmpzval)) {
- nodelay = true;
+ if (PHP_STREAM_CONTEXT(stream)) {
+ tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay");
+ if (tmpzval != NULL && zend_is_true(tmpzval)) {
+ sockvals.mask |= PHP_SOCKVAL_TCP_NODELAY;
+ sockvals.tcp_nodelay = 1;
+ }
+ tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepidle");
+ if (tmpzval != NULL && Z_TYPE_P(tmpzval) == IS_LONG) {
+ sockvals.mask |= PHP_SOCKVAL_TCP_KEEPIDLE;
+ sockvals.keepalive.keepidle = Z_LVAL_P(tmpzval);
+ }
}
- php_socket_t clisock = php_network_accept_incoming(sock->s.socket,
+ php_socket_t clisock = php_network_accept_incoming_ex(sock->s.socket,
xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
xparam->want_addr ? &xparam->outputs.addr : NULL,
xparam->want_addr ? &xparam->outputs.addrlen : NULL,
xparam->inputs.timeout,
xparam->want_errortext ? &xparam->outputs.error_text : NULL,
&xparam->outputs.error_code,
- nodelay);
+ &sockvals);
if (clisock != SOCK_ERR) {
php_openssl_netstream_data_t *clisockdata = (php_openssl_netstream_data_t*) emalloc(sizeof(*clisockdata));
diff --git a/main/network.c b/main/network.c
index 58f688a4fea..c4e2ab2e67d 100644
--- a/main/network.c
+++ b/main/network.c
@@ -801,15 +801,22 @@ PHPAPI int php_network_get_sock_name(php_socket_t sock,
* version of the address will be emalloc'd and returned.
* */
-/* {{{ php_network_accept_incoming */
-PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
+ /* Accept a client connection from a server socket,
+ * using an optional timeout.
+ * Returns the peer address in addr/addrlen (it will emalloc
+ * these, so be sure to efree the result).
+ * If you specify textaddr, a text-printable
+ * version of the address will be emalloc'd and returned.
+ * */
+
+PHPAPI php_socket_t php_network_accept_incoming_ex(php_socket_t srvsock,
zend_string **textaddr,
struct sockaddr **addr,
socklen_t *addrlen,
struct timeval *timeout,
zend_string **error_string,
int *error_code,
- int tcp_nodelay
+ php_sockvals *sockvals
)
{
php_socket_t clisock = -1;
@@ -833,11 +840,19 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
textaddr,
addr, addrlen
);
- if (tcp_nodelay) {
#ifdef TCP_NODELAY
+ if (PHP_SOCKVAL_IS_SET(sockvals, PHP_SOCKVAL_TCP_NODELAY)) {
+ int tcp_nodelay = 1;
setsockopt(clisock, IPPROTO_TCP, TCP_NODELAY, (char*)&tcp_nodelay, sizeof(tcp_nodelay));
+ }
#endif
+#ifdef TCP_KEEPALIVE
+ /* MacOS does not inherit TCP_KEEPALIVE so it needs to be set */
+ if (PHP_SOCKVAL_IS_SET(sockvals, PHP_SOCKVAL_TCP_KEEPIDLE)) {
+ setsockopt(clisock, IPPROTO_TCP, TCP_KEEPALIVE,
+ (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
}
+#endif
} else {
error = php_socket_errno();
}
@@ -852,7 +867,22 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
return clisock;
}
-/* }}} */
+
+PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
+ zend_string **textaddr,
+ struct sockaddr **addr,
+ socklen_t *addrlen,
+ struct timeval *timeout,
+ zend_string **error_string,
+ int *error_code,
+ int tcp_nodelay
+ )
+{
+ php_sockvals sockvals = { .mask = tcp_nodelay ? PHP_SOCKVAL_TCP_NODELAY : 0 };
+
+ return php_network_accept_incoming_ex(srvsock, textaddr, addr, addrlen, timeout, error_string,
+ error_code, &sockvals);
+}
/* Connect to a remote host using an interruptible connect with optional timeout.
* Optionally, the connect can be made asynchronously, which will implicitly
diff --git a/main/php_network.h b/main/php_network.h
index 08d6bbc140c..db449ae35ff 100644
--- a/main/php_network.h
+++ b/main/php_network.h
@@ -267,12 +267,16 @@ 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)
+#define PHP_SOCKVAL_TCP_NODELAY (1 << 0)
+#define PHP_SOCKVAL_TCP_KEEPIDLE (1 << 1)
+#define PHP_SOCKVAL_TCP_KEEPCNT (1 << 2)
+#define PHP_SOCKVAL_TCP_KEEPINTVL (1 << 3)
+
+#define PHP_SOCKVAL_IS_SET(sockvals, opt) (sockvals->mask & opt)
typedef struct {
unsigned int mask;
+ int tcp_nodelay;
struct {
int keepidle;
int keepcnt;
@@ -313,6 +317,16 @@ PHPAPI php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsi
int socktype, long sockopts, zend_string **error_string, int *error_code
);
+PHPAPI php_socket_t php_network_accept_incoming_ex(php_socket_t srvsock,
+ zend_string **textaddr,
+ struct sockaddr **addr,
+ socklen_t *addrlen,
+ struct timeval *timeout,
+ zend_string **error_string,
+ int *error_code,
+ php_sockvals *sockvals
+ );
+
PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
zend_string **textaddr,
struct sockaddr **addr,
diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c
index 969d364ffe1..7a7f007f918 100644
--- a/main/streams/xp_socket.c
+++ b/main/streams/xp_socket.c
@@ -964,25 +964,32 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
php_stream_xport_param *xparam STREAMS_DC)
{
- bool nodelay = 0;
+ php_sockvals sockvals = {0};
zval *tmpzval = NULL;
xparam->outputs.client = NULL;
- if ((NULL != PHP_STREAM_CONTEXT(stream)) &&
- (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
- zend_is_true(tmpzval)) {
- nodelay = 1;
+ if (PHP_STREAM_CONTEXT(stream)) {
+ tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay");
+ if (tmpzval != NULL && zend_is_true(tmpzval)) {
+ sockvals.mask |= PHP_SOCKVAL_TCP_NODELAY;
+ sockvals.tcp_nodelay = 1;
+ }
+ tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepidle");
+ if (tmpzval != NULL && Z_TYPE_P(tmpzval) == IS_LONG) {
+ sockvals.mask |= PHP_SOCKVAL_TCP_KEEPIDLE;
+ sockvals.keepalive.keepidle = Z_LVAL_P(tmpzval);
+ }
}
- php_socket_t clisock = php_network_accept_incoming(sock->socket,
+ php_socket_t clisock = php_network_accept_incoming_ex(sock->socket,
xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
xparam->want_addr ? &xparam->outputs.addr : NULL,
xparam->want_addr ? &xparam->outputs.addrlen : NULL,
xparam->inputs.timeout,
xparam->want_errortext ? &xparam->outputs.error_text : NULL,
&xparam->outputs.error_code,
- nodelay);
+ &sockvals);
if (clisock != SOCK_ERR) {
php_netstream_data_t *clisockdata = (php_netstream_data_t*) emalloc(sizeof(*clisockdata));