diff --git a/ext/standard/tests/network/so_keepalive.phpt b/ext/standard/tests/network/so_keepalive.phpt new file mode 100644 index 000000000000..a437d16694b3 --- /dev/null +++ b/ext/standard/tests/network/so_keepalive.phpt @@ -0,0 +1,151 @@ +--TEST-- +stream_socket_server() and stream_socket_client() SO_KEEPALIVE context option test +--EXTENSIONS-- +sockets +--SKIPIF-- + +--FILE-- + [ + '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 diff --git a/main/network.c b/main/network.c index 6c43321cf2e8..583d56f08401 100644 --- a/main/network.c +++ b/main/network.c @@ -450,9 +450,9 @@ PHPAPI int php_network_connect_socket(php_socket_t sockfd, /* 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; @@ -531,6 +531,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); @@ -558,7 +587,13 @@ php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned po 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) { @@ -822,11 +857,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; @@ -950,6 +983,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); @@ -996,7 +1063,15 @@ php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short 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 diff --git a/main/php_network.h b/main/php_network.h index 45e1e1902631..08d6bbc140ce 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -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 ); diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index db35a9b7952c..7992143854fc 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -48,8 +48,14 @@ 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(_stream) \ + (_stream->ops == &php_stream_unix_socket_ops || _stream->ops == &php_stream_unixdg_socket_ops) +#else +#define PHP_STREAM_XPORT_IS_UNIX(_stream) false +#endif +#define PHP_STREAM_XPORT_IS_UDP(_stream) (_stream->ops == &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,9 +675,10 @@ 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); @@ -729,7 +736,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 +745,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, +#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, stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM, sockopts, + sockvals.mask ? &sockvals : NULL, xparam->want_errortext ? &xparam->outputs.error_text : NULL, &err ); @@ -761,9 +812,10 @@ 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); @@ -807,7 +859,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 +868,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,11 +876,54 @@ 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, + sock->socket = php_network_connect_socket_to_host_ex(host, portno, stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM, xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout, @@ -840,7 +931,8 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_ &err, bindto, bindport, - sockopts + sockopts, + sockvals.mask ? &sockvals : NULL ); ret = sock->socket == -1 ? -1 : 0;