diff --git a/ext/sockets/config.m4 b/ext/sockets/config.m4 index f637c32b3b66d..0c37d88773028 100644 --- a/ext/sockets/config.m4 +++ b/ext/sockets/config.m4 @@ -23,6 +23,8 @@ if test "$PHP_SOCKETS" != "no"; then AC_CHECK_TYPES([struct cmsgcred],,, [#include ]) + AC_CHECK_FUNCS(m4_normalize([connectx])) + PHP_SOCKETS_CFLAGS=-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 AS_CASE([$host_alias], [*darwin*], diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 82634cc8f3e54..0b729dd1435c4 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -3135,3 +3135,93 @@ PHP_FUNCTION(socket_wsaprotocol_info_release) } /* }}} */ #endif + +#ifdef HAVE_CONNECTX +PHP_FUNCTION(socket_connectx) +{ + zval *arg1, *buffers = NULL; + php_socket *php_sock; + php_addrinfo *ai; + zend_long flags = 0; + struct iovec *raw_buffers = NULL; + uint32_t raw_buffers_len = 0; + size_t buffers_len = 0; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY(buffers) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + php_sock = Z_SOCKET_P(arg1); + ENSURE_SOCKET_VALID(php_sock); + + if (buffers != NULL) { + raw_buffers_len = zend_hash_num_elements(Z_ARRVAL_P(buffers)); + if (raw_buffers_len == 0) { + zend_argument_value_error(3, "must contain at least one entry"); + RETURN_THROWS(); + } + raw_buffers = emalloc(raw_buffers_len * sizeof(struct iovec)); + zval *buffer; + raw_buffers_len = 0; + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(buffers), buffer) { + if (Z_TYPE_P(buffer) != IS_STRING) { + efree(buffers); + zend_argument_type_error(2, "must only have elements of type string, %s given", zend_zval_value_name(buffer)); + RETURN_THROWS(); + } + const struct iovec vec = { + .iov_base = (void *)Z_STRVAL_P(buffer), + .iov_len = Z_STRLEN_P(buffer) + }; + raw_buffers[raw_buffers_len] = vec; + raw_buffers_len ++; + } ZEND_HASH_FOREACH_END(); + } + + if (flags > 0 && flags & ~(CONNECT_DATA_IDEMPOTENT|CONNECT_RESUME_ON_READ_WRITE)) { + efree(buffers); + zend_argument_value_error(4, "must be CONNECT_DATA_IDEMPOTENT and/or CONNECT_RESUME_ON_READ_WRITE"); + RETURN_THROWS(); + } + + ai = Z_ADDRESS_INFO_P(arg1); + // TODO: multiple interfaces support ? + struct sa_endpoints epoints = {0}; + epoints.sae_dstaddr = ai->addrinfo.ai_addr; + epoints.sae_dstaddrlen = ai->addrinfo.ai_addrlen; + + // TODO: using final length result ? + if (connectx(php_sock->bsd_socket, &epoints, SAE_ASSOCID_ANY, (unsigned int)flags, raw_buffers, raw_buffers_len, &buffers_len, NULL) < 0) { + efree(raw_buffers); + PHP_SOCKET_ERROR(php_sock, "Unable to connect", errno); + RETURN_FALSE; + } + + efree(raw_buffers); + RETURN_TRUE; +} + +PHP_FUNCTION(socket_disconnectx) +{ + zval *arg1; + php_socket *php_sock; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce) + ZEND_PARSE_PARAMETERS_END(); + + php_sock = Z_SOCKET_P(arg1); + ENSURE_SOCKET_VALID(php_sock); + + if (disconnectx(php_sock->bsd_socket, SAE_ASSOCID_ANY, SAE_CONNID_ANY) < 0) { + PHP_SOCKET_ERROR(php_sock, "Unable to disconnect", errno); + RETURN_FALSE; + } + + RETURN_TRUE; +} +#endif diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index 6050a89414565..352bf01a097be 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -33,6 +33,20 @@ */ const AF_PACKET = UNKNOWN; #endif +#ifdef CONNECT_DATA_IDEMPOTENT +/** + * @var int + * @cvalue CONNECT_DATA_IDEMPOTENT + */ +const CONNECT_DATA_IDEMPOTENT = UNKNOWN; +#endif +#ifdef CONNECT_RESUME_ON_READ_WRITE +/** + * @var int + * @cvalue CONNECT_RESUME_ON_READ_WRITE + */ +const CONNECT_RESUME_ON_READ_WRITE = UNKNOWN; +#endif /** * @var int * @cvalue SOCK_STREAM @@ -2190,3 +2204,8 @@ function socket_wsaprotocol_info_import(string $info_id): Socket|false {} function socket_wsaprotocol_info_release(string $info_id): bool {} #endif + +#ifdef HAVE_CONNECTX +function socket_connectx(Socket $socket, ?array $buffers = null, ?int $flags = 0): bool {} +function socket_disconnectx(Socket $socket): bool {} +#endif diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index b7af6eb6b89ac..ef3fcf0da727e 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7b1baf47dce2fb08faa5616068238ea078d1609b */ + * Stub hash: 7a6612448ec7e208abe657a7408e00b8d80ccfd7 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1) @@ -211,6 +211,18 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_socket_wsaprotocol_info_release, ZEND_END_ARG_INFO() #endif +#if defined(HAVE_CONNECTX) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_socket_connectx, 0, 1, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, socket, Socket, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, buffers, IS_ARRAY, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 1, "0") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_socket_disconnectx, 0, 1, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, socket, Socket, 0) +ZEND_END_ARG_INFO() +#endif + ZEND_FUNCTION(socket_select); ZEND_FUNCTION(socket_create_listen); ZEND_FUNCTION(socket_accept); @@ -257,6 +269,10 @@ ZEND_FUNCTION(socket_wsaprotocol_info_export); ZEND_FUNCTION(socket_wsaprotocol_info_import); ZEND_FUNCTION(socket_wsaprotocol_info_release); #endif +#if defined(HAVE_CONNECTX) +ZEND_FUNCTION(socket_connectx); +ZEND_FUNCTION(socket_disconnectx); +#endif static const zend_function_entry ext_functions[] = { ZEND_FE(socket_select, arginfo_socket_select) @@ -306,6 +322,10 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(socket_wsaprotocol_info_export, arginfo_socket_wsaprotocol_info_export) ZEND_FE(socket_wsaprotocol_info_import, arginfo_socket_wsaprotocol_info_import) ZEND_FE(socket_wsaprotocol_info_release, arginfo_socket_wsaprotocol_info_release) +#endif +#if defined(HAVE_CONNECTX) + ZEND_FE(socket_connectx, arginfo_socket_connectx) + ZEND_FE(socket_disconnectx, arginfo_socket_disconnectx) #endif ZEND_FE_END }; @@ -322,6 +342,12 @@ static void register_sockets_symbols(int module_number) #endif #if defined(AF_PACKET) REGISTER_LONG_CONSTANT("AF_PACKET", AF_PACKET, CONST_PERSISTENT); +#endif +#if defined(CONNECT_DATA_IDEMPOTENT) + REGISTER_LONG_CONSTANT("CONNECT_DATA_IDEMPOTENT", CONNECT_DATA_IDEMPOTENT, CONST_PERSISTENT); +#endif +#if defined(CONNECT_RESUME_ON_READ_WRITE) + REGISTER_LONG_CONSTANT("CONNECT_RESUME_ON_READ_WRITE", CONNECT_RESUME_ON_READ_WRITE, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("SOCK_STREAM", SOCK_STREAM, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SOCK_DGRAM", SOCK_DGRAM, CONST_PERSISTENT);