123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- /*
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | https://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Gustavo Lopes <cataphract@php.net> |
- +----------------------------------------------------------------------+
- */
- #ifdef __sun
- /* to enable 'new' ancillary data layout instead */
- # define _XPG4_2
- #endif
- #include <php.h>
- #include "php_sockets.h"
- #include "sendrecvmsg.h"
- #include "conversions.h"
- #include <limits.h>
- #include <Zend/zend_llist.h>
- #ifdef ZTS
- #include <TSRM/TSRM.h>
- #endif
- #define MAX_USER_BUFF_SIZE ((size_t)(100*1024*1024))
- #define DEFAULT_BUFF_SIZE 8192
- #define MAX_ARRAY_KEY_SIZE 128
- #ifdef PHP_WIN32
- #include "windows_common.h"
- #include <Mswsock.h>
- #define IPV6_RECVPKTINFO IPV6_PKTINFO
- #define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
- #define msghdr _WSAMSG
- static GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
- static __declspec(thread) LPFN_WSARECVMSG WSARecvMsg = NULL;
- inline ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
- {
- DWORD recvd = 0,
- bytesReturned;
- if (WSARecvMsg == NULL) {
- int res = WSAIoctl((SOCKET) sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
- &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID),
- &WSARecvMsg, sizeof(WSARecvMsg),
- &bytesReturned, NULL, NULL);
- if (res != 0) {
- return -1;
- }
- }
- msg->dwFlags = (DWORD)flags;
- return WSARecvMsg((SOCKET)sockfd, msg, &recvd, NULL, NULL) == 0
- ? (ssize_t)recvd
- : -1;
- }
- inline ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
- {
- DWORD sent = 0;
- return WSASendMsg((SOCKET)sockfd, (struct msghdr*)msg, (DWORD)flags, &sent, NULL, NULL) == 0
- ? (ssize_t)sent
- : -1;
- }
- #endif
- #define LONG_CHECK_VALID_INT(l, arg_pos) \
- do { \
- if ((l) < INT_MIN || (l) > INT_MAX) { \
- zend_argument_value_error((arg_pos), "must be between %d and %d", INT_MIN, INT_MAX); \
- RETURN_THROWS(); \
- } \
- } while (0)
- static struct {
- int initialized;
- HashTable ht;
- } ancillary_registry;
- static void ancillary_registery_free_elem(zval *el) {
- pefree(Z_PTR_P(el), 1);
- }
- #ifdef ZTS
- static MUTEX_T ancillary_mutex;
- #endif
- static void init_ancillary_registry(void)
- {
- ancillary_reg_entry entry;
- anc_reg_key key;
- ancillary_registry.initialized = 1;
- zend_hash_init(&ancillary_registry.ht, 32, NULL, ancillary_registery_free_elem, 1);
- #define PUT_ENTRY(sizev, var_size, calc, from, to, level, type) \
- entry.size = sizev; \
- entry.var_el_size = var_size; \
- entry.calc_space = calc; \
- entry.from_array = from; \
- entry.to_array = to; \
- key.cmsg_level = level; \
- key.cmsg_type = type; \
- zend_hash_str_update_mem(&ancillary_registry.ht, (char*)&key, sizeof(key), (void*)&entry, sizeof(entry))
- #if defined(IPV6_PKTINFO) && HAVE_IPV6
- PUT_ENTRY(sizeof(struct in6_pktinfo), 0, 0, from_zval_write_in6_pktinfo,
- to_zval_read_in6_pktinfo, IPPROTO_IPV6, IPV6_PKTINFO);
- #endif
- #if defined(IPV6_HOPLIMIT) && HAVE_IPV6
- PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
- to_zval_read_int, IPPROTO_IPV6, IPV6_HOPLIMIT);
- #endif
- #if defined(IPV6_TCLASS) && HAVE_IPV6
- PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
- to_zval_read_int, IPPROTO_IPV6, IPV6_TCLASS);
- #endif
- #ifdef SO_PASSCRED
- #ifdef ANC_CREDS_UCRED
- PUT_ENTRY(sizeof(struct ucred), 0, 0, from_zval_write_ucred,
- to_zval_read_ucred, SOL_SOCKET, SCM_CREDENTIALS);
- #else
- PUT_ENTRY(sizeof(struct cmsgcred), 0, 0, from_zval_write_ucred,
- to_zval_read_ucred, SOL_SOCKET, SCM_CREDS);
- #endif
- #endif
- #ifdef SCM_RIGHTS
- PUT_ENTRY(0, sizeof(int), calculate_scm_rights_space, from_zval_write_fd_array,
- to_zval_read_fd_array, SOL_SOCKET, SCM_RIGHTS);
- #endif
- }
- static void destroy_ancillary_registry(void)
- {
- if (ancillary_registry.initialized) {
- zend_hash_destroy(&ancillary_registry.ht);
- ancillary_registry.initialized = 0;
- }
- }
- ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type)
- {
- anc_reg_key key = { cmsg_level, msg_type };
- ancillary_reg_entry *entry;
- #ifdef ZTS
- tsrm_mutex_lock(ancillary_mutex);
- #endif
- if (!ancillary_registry.initialized) {
- init_ancillary_registry();
- }
- #ifdef ZTS
- tsrm_mutex_unlock(ancillary_mutex);
- #endif
- if ((entry = zend_hash_str_find_ptr(&ancillary_registry.ht, (char*)&key, sizeof(key))) != NULL) {
- return entry;
- } else {
- return NULL;
- }
- }
- PHP_FUNCTION(socket_sendmsg)
- {
- zval *zsocket,
- *zmsg;
- zend_long flags = 0;
- php_socket *php_sock;
- struct msghdr *msghdr;
- zend_llist *allocations;
- struct err_s err = {0};
- ssize_t res;
- /* zmsg should be passed by ref */
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oa|l", &zsocket, socket_ce, &zmsg, &flags) == FAILURE) {
- RETURN_THROWS();
- }
- LONG_CHECK_VALID_INT(flags, 3);
- php_sock = Z_SOCKET_P(zsocket);
- ENSURE_SOCKET_VALID(php_sock);
- msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_send,
- sizeof(*msghdr), "msghdr", &allocations, &err);
- if (err.has_error) {
- err_msg_dispose(&err);
- RETURN_FALSE;
- }
- res = sendmsg(php_sock->bsd_socket, msghdr, (int)flags);
- if (res != -1) {
- RETVAL_LONG((zend_long)res);
- } else {
- PHP_SOCKET_ERROR(php_sock, "Error in sendmsg", errno);
- RETVAL_FALSE;
- }
- allocations_dispose(&allocations);
- }
- PHP_FUNCTION(socket_recvmsg)
- {
- zval *zsocket,
- *zmsg;
- zend_long flags = 0;
- php_socket *php_sock;
- ssize_t res;
- struct msghdr *msghdr;
- zend_llist *allocations;
- struct err_s err = {0};
- //ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oa|l", &zsocket, socket_ce, &zmsg, &flags) == FAILURE) {
- RETURN_THROWS();
- }
- LONG_CHECK_VALID_INT(flags, 3);
- php_sock = Z_SOCKET_P(zsocket);
- ENSURE_SOCKET_VALID(php_sock);
- msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_recv,
- sizeof(*msghdr), "msghdr", &allocations, &err);
- if (err.has_error) {
- err_msg_dispose(&err);
- RETURN_FALSE;
- }
- res = recvmsg(php_sock->bsd_socket, msghdr, (int)flags);
- if (res != -1) {
- zval *zres, tmp;
- struct key_value kv[] = {
- {KEY_RECVMSG_RET, sizeof(KEY_RECVMSG_RET), &res},
- {0}
- };
- zres = to_zval_run_conversions((char *)msghdr, to_zval_read_msghdr,
- "msghdr", kv, &err, &tmp);
- /* we don;t need msghdr anymore; free it */
- msghdr = NULL;
- zval_ptr_dtor(zmsg);
- if (!err.has_error) {
- ZVAL_COPY_VALUE(zmsg, zres);
- } else {
- err_msg_dispose(&err);
- ZVAL_FALSE(zmsg);
- /* no need to destroy/free zres -- it's NULL in this circumstance */
- assert(zres == NULL);
- }
- RETVAL_LONG((zend_long)res);
- } else {
- SOCKETS_G(last_error) = errno;
- php_error_docref(NULL, E_WARNING, "Error in recvmsg [%d]: %s",
- errno, sockets_strerror(errno));
- RETVAL_FALSE;
- }
- allocations_dispose(&allocations);
- }
- PHP_FUNCTION(socket_cmsg_space)
- {
- zend_long level,
- type,
- n = 0;
- ancillary_reg_entry *entry;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|l",
- &level, &type, &n) == FAILURE) {
- RETURN_THROWS();
- }
- LONG_CHECK_VALID_INT(level, 1);
- LONG_CHECK_VALID_INT(type, 2);
- LONG_CHECK_VALID_INT(n, 3);
- if (n < 0) {
- zend_argument_value_error(3, "must be greater than or equal to 0");
- RETURN_THROWS();
- }
- entry = get_ancillary_reg_entry(level, type);
- if (entry == NULL) {
- zend_value_error("Pair level " ZEND_LONG_FMT " and/or type " ZEND_LONG_FMT " is not supported",
- level, type);
- RETURN_THROWS();
- }
- if (entry->var_el_size > 0) {
- /* Leading underscore to avoid symbol collision on AIX. */
- size_t _rem_size = ZEND_LONG_MAX - entry->size;
- size_t n_max = _rem_size / entry->var_el_size;
- size_t size = entry->size + n * entry->var_el_size;
- size_t total_size = CMSG_SPACE(size);
- if (n > n_max /* zend_long overflow */
- || total_size > ZEND_LONG_MAX
- || total_size < size /* align overflow */) {
- zend_argument_value_error(3, "is too large");
- RETURN_THROWS();
- }
- }
- RETURN_LONG((zend_long)CMSG_SPACE(entry->size + n * entry->var_el_size));
- }
- #if HAVE_IPV6
- int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *arg4)
- {
- struct err_s err = {0};
- zend_llist *allocations = NULL;
- void *opt_ptr;
- socklen_t optlen;
- int retval;
- assert(level == IPPROTO_IPV6);
- switch (optname) {
- #ifdef IPV6_PKTINFO
- case IPV6_PKTINFO:
- #ifdef PHP_WIN32
- if (Z_TYPE_P(arg4) == IS_ARRAY) {
- php_error_docref(NULL, E_WARNING, "Windows does not "
- "support sticky IPV6_PKTINFO");
- return FAILURE;
- } else {
- /* windows has no IPV6_RECVPKTINFO, and uses IPV6_PKTINFO
- * for the same effect. We define IPV6_RECVPKTINFO to be
- * IPV6_PKTINFO, so assume the assume user used IPV6_RECVPKTINFO */
- return 1;
- }
- #endif
- opt_ptr = from_zval_run_conversions(arg4, php_sock, from_zval_write_in6_pktinfo,
- sizeof(struct in6_pktinfo), "in6_pktinfo", &allocations, &err);
- if (err.has_error) {
- err_msg_dispose(&err);
- return FAILURE;
- }
- optlen = sizeof(struct in6_pktinfo);
- goto dosockopt;
- #endif
- }
- /* we also support IPV6_TCLASS, but that can be handled by the default
- * integer optval handling in the caller */
- return 1;
- dosockopt:
- retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
- if (retval != 0) {
- PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
- }
- allocations_dispose(&allocations);
- return retval != 0 ? FAILURE : SUCCESS;
- }
- int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result)
- {
- struct err_s err = {0};
- void *buffer;
- socklen_t size;
- int res;
- to_zval_read_field *reader;
- assert(level == IPPROTO_IPV6);
- switch (optname) {
- #ifdef IPV6_PKTINFO
- case IPV6_PKTINFO:
- size = sizeof(struct in6_pktinfo);
- reader = &to_zval_read_in6_pktinfo;
- break;
- #endif
- default:
- return 1;
- }
- buffer = ecalloc(1, size);
- res = getsockopt(php_sock->bsd_socket, level, optname, buffer, &size);
- if (res != 0) {
- PHP_SOCKET_ERROR(php_sock, "unable to get socket option", errno);
- } else {
- zval tmp;
- zval *zv = to_zval_run_conversions(buffer, reader, "in6_pktinfo",
- empty_key_value_list, &err, &tmp);
- if (err.has_error) {
- err_msg_dispose(&err);
- res = -1;
- } else {
- ZVAL_COPY_VALUE(result, zv);
- }
- }
- efree(buffer);
- return res == 0 ? SUCCESS : FAILURE;
- }
- #endif /* HAVE_IPV6 */
- void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)
- {
- /* IPv6 ancillary data */
- #if defined(IPV6_RECVPKTINFO) && HAVE_IPV6
- REGISTER_LONG_CONSTANT("IPV6_RECVPKTINFO", IPV6_RECVPKTINFO, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("IPV6_PKTINFO", IPV6_PKTINFO, CONST_CS | CONST_PERSISTENT);
- #endif
- #if defined(IPV6_RECVHOPLIMIT) && HAVE_IPV6
- REGISTER_LONG_CONSTANT("IPV6_RECVHOPLIMIT", IPV6_RECVHOPLIMIT, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("IPV6_HOPLIMIT", IPV6_HOPLIMIT, CONST_CS | CONST_PERSISTENT);
- #endif
- /* would require some effort:
- REGISTER_LONG_CONSTANT("IPV6_RECVRTHDR", IPV6_RECVRTHDR, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("IPV6_RECVHOPOPTS", IPV6_RECVHOPOPTS, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("IPV6_RECVDSTOPTS", IPV6_RECVDSTOPTS, CONST_CS | CONST_PERSISTENT);
- */
- #if defined(IPV6_RECVTCLASS) && HAVE_IPV6
- REGISTER_LONG_CONSTANT("IPV6_RECVTCLASS", IPV6_RECVTCLASS, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("IPV6_TCLASS", IPV6_TCLASS, CONST_CS | CONST_PERSISTENT);
- #endif
- /*
- REGISTER_LONG_CONSTANT("IPV6_RTHDR", IPV6_RTHDR, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("IPV6_HOPOPTS", IPV6_HOPOPTS, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("IPV6_DSTOPTS", IPV6_DSTOPTS, CONST_CS | CONST_PERSISTENT);
- */
- #ifdef SCM_RIGHTS
- REGISTER_LONG_CONSTANT("SCM_RIGHTS", SCM_RIGHTS, CONST_CS | CONST_PERSISTENT);
- #endif
- #ifdef SO_PASSCRED
- #ifdef SCM_CREDENTIALS
- REGISTER_LONG_CONSTANT("SCM_CREDENTIALS", SCM_CREDENTIALS, CONST_CS | CONST_PERSISTENT);
- #else
- REGISTER_LONG_CONSTANT("SCM_CREDS", SCM_CREDS, CONST_CS | CONST_PERSISTENT);
- #endif
- REGISTER_LONG_CONSTANT("SO_PASSCRED", SO_PASSCRED, CONST_CS | CONST_PERSISTENT);
- #endif
- #ifdef ZTS
- ancillary_mutex = tsrm_mutex_alloc();
- #endif
- }
- void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)
- {
- #ifdef ZTS
- tsrm_mutex_free(ancillary_mutex);
- #endif
- destroy_ancillary_registry();
- }
|