root/ext/sockets/sendrecvmsg.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. recvmsg
  2. sendmsg
  3. init_ancillary_registry
  4. destroy_ancillary_registry
  5. get_ancillary_reg_entry
  6. PHP_FUNCTION
  7. PHP_FUNCTION
  8. PHP_FUNCTION
  9. php_do_setsockopt_ipv6_rfc3542
  10. php_do_getsockopt_ipv6_rfc3542
  11. php_socket_sendrecvmsg_init
  12. php_socket_sendrecvmsg_shutdown

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 5                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 of the PHP license,      |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.php.net/license/3_01.txt                                  |
  11    | If you did not receive a copy of the PHP license and are unable to   |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@php.net so we can mail you a copy immediately.               |
  14    +----------------------------------------------------------------------+
  15    | Authors: Gustavo Lopes    <cataphract@php.net>                       |
  16    +----------------------------------------------------------------------+
  17  */
  18 
  19 #include <php.h>
  20 #include "php_sockets.h"
  21 #include "sendrecvmsg.h"
  22 #include "conversions.h"
  23 #include <limits.h>
  24 #include <Zend/zend_llist.h>
  25 #ifdef ZTS
  26 #include <TSRM/TSRM.h>
  27 #endif
  28 
  29 #define MAX_USER_BUFF_SIZE ((size_t)(100*1024*1024))
  30 #define DEFAULT_BUFF_SIZE 8192
  31 #define MAX_ARRAY_KEY_SIZE 128
  32 
  33 #ifdef PHP_WIN32
  34 #include "windows_common.h"
  35 #include <Mswsock.h>
  36 #define IPV6_RECVPKTINFO        IPV6_PKTINFO
  37 #define IPV6_RECVHOPLIMIT       IPV6_HOPLIMIT
  38 #define msghdr _WSAMSG
  39 
  40 static GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
  41 static __declspec(thread) LPFN_WSARECVMSG WSARecvMsg = NULL;
  42 inline ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
  43 {
  44         DWORD   recvd = 0,
  45                         bytesReturned;
  46 
  47         if (WSARecvMsg == NULL) {
  48                 int res = WSAIoctl((SOCKET) sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
  49                         &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID),
  50                         &WSARecvMsg, sizeof(WSARecvMsg),
  51                         &bytesReturned, NULL, NULL);
  52                 if (res != 0) {
  53                         return -1;
  54                 }
  55         }
  56 
  57         msg->dwFlags = (DWORD)flags;
  58         return WSARecvMsg((SOCKET)sockfd, msg, &recvd, NULL, NULL) == 0
  59                 ? (ssize_t)recvd
  60                 : -1;
  61 }
  62 inline ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
  63 {
  64         DWORD sent = 0;
  65         return WSASendMsg((SOCKET)sockfd, (struct msghdr*)msg, (DWORD)flags, &sent, NULL, NULL) == 0
  66                 ? (ssize_t)sent
  67                 : -1;
  68 }
  69 #endif
  70 
  71 #define LONG_CHECK_VALID_INT(l) \
  72         do { \
  73                 if ((l) < INT_MIN && (l) > INT_MAX) { \
  74                         php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The value %ld does not fit inside " \
  75                                         "the boundaries of a native integer", (l)); \
  76                         return; \
  77                 } \
  78         } while (0)
  79 
  80 static struct {
  81         int                     initialized;
  82         HashTable       ht;
  83 } ancillary_registry;
  84 
  85 
  86 #ifdef ZTS
  87 static MUTEX_T ancillary_mutex;
  88 #endif
  89 static void init_ancillary_registry(void)
  90 {
  91         ancillary_reg_entry entry;
  92         anc_reg_key key;
  93         ancillary_registry.initialized = 1;
  94 
  95         zend_hash_init(&ancillary_registry.ht, 32, NULL, NULL, 1);
  96 
  97 #define PUT_ENTRY(sizev, var_size, calc, from, to, level, type) \
  98         entry.size                      = sizev; \
  99         entry.var_el_size       = var_size; \
 100         entry.calc_space        = calc; \
 101         entry.from_array        = from; \
 102         entry.to_array          = to; \
 103         key.cmsg_level          = level; \
 104         key.cmsg_type           = type; \
 105         zend_hash_update(&ancillary_registry.ht, (char*)&key, sizeof(key), \
 106                         (void*)&entry, sizeof(entry), NULL)
 107 
 108 #if defined(IPV6_PKTINFO) && HAVE_IPV6
 109         PUT_ENTRY(sizeof(struct in6_pktinfo), 0, 0, from_zval_write_in6_pktinfo,
 110                         to_zval_read_in6_pktinfo, IPPROTO_IPV6, IPV6_PKTINFO);
 111 #endif
 112 
 113 #if defined(IPV6_HOPLIMIT) && HAVE_IPV6
 114         PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
 115                         to_zval_read_int, IPPROTO_IPV6, IPV6_HOPLIMIT);
 116 #endif
 117 
 118 #if defined(IPV6_TCLASS) && HAVE_IPV6
 119         PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
 120                         to_zval_read_int, IPPROTO_IPV6, IPV6_TCLASS);
 121 #endif
 122 
 123 #ifdef SO_PASSCRED
 124         PUT_ENTRY(sizeof(struct ucred), 0, 0, from_zval_write_ucred,
 125                         to_zval_read_ucred, SOL_SOCKET, SCM_CREDENTIALS);
 126 #endif
 127 
 128 #ifdef SCM_RIGHTS
 129         PUT_ENTRY(0, sizeof(int), calculate_scm_rights_space, from_zval_write_fd_array,
 130                         to_zval_read_fd_array, SOL_SOCKET, SCM_RIGHTS);
 131 #endif
 132 
 133 }
 134 static void destroy_ancillary_registry(void)
 135 {
 136         if (ancillary_registry.initialized) {
 137                 zend_hash_destroy(&ancillary_registry.ht);
 138                 ancillary_registry.initialized = 0;
 139         }
 140 }
 141 ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type)
 142 {
 143         anc_reg_key                     key = { cmsg_level, msg_type };
 144         ancillary_reg_entry     *entry;
 145 
 146 #ifdef ZTS
 147         tsrm_mutex_lock(ancillary_mutex);
 148 #endif
 149         if (!ancillary_registry.initialized) {
 150                 init_ancillary_registry();
 151         }
 152 #ifdef ZTS
 153         tsrm_mutex_unlock(ancillary_mutex);
 154 #endif
 155 
 156         if (zend_hash_find(&ancillary_registry.ht, (char*)&key, sizeof(key),
 157                         (void**)&entry) == SUCCESS) {
 158                 return entry;
 159         } else {
 160                 return NULL;
 161         }
 162 }
 163 
 164 PHP_FUNCTION(socket_sendmsg)
 165 {
 166         zval                    *zsocket,
 167                                         *zmsg;
 168         long                    flags = 0;
 169         php_socket              *php_sock;
 170         struct msghdr   *msghdr;
 171         zend_llist              *allocations;
 172         struct err_s    err = {0};
 173         ssize_t                 res;
 174 
 175         /* zmsg should be passed by ref */
 176         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|l", &zsocket, &zmsg, &flags) == FAILURE) {
 177                 return;
 178         }
 179 
 180         LONG_CHECK_VALID_INT(flags);
 181 
 182         ZEND_FETCH_RESOURCE(php_sock, php_socket *, &zsocket, -1,
 183                         php_sockets_le_socket_name, php_sockets_le_socket());
 184 
 185         msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_send,
 186                         sizeof(*msghdr), "msghdr", &allocations, &err);
 187 
 188         if (err.has_error) {
 189                 err_msg_dispose(&err TSRMLS_CC);
 190                 RETURN_FALSE;
 191         }
 192 
 193         res = sendmsg(php_sock->bsd_socket, msghdr, (int)flags);
 194 
 195         if (res != -1) {
 196                 zend_llist_destroy(allocations);
 197                 efree(allocations);
 198 
 199                 RETURN_LONG((long)res);
 200         } else {
 201                 PHP_SOCKET_ERROR(php_sock, "error in sendmsg", errno);
 202                 RETURN_FALSE;
 203         }
 204 }
 205 
 206 PHP_FUNCTION(socket_recvmsg)
 207 {
 208         zval                    *zsocket,
 209                                         *zmsg;
 210         long                    flags = 0;
 211         php_socket              *php_sock;
 212         ssize_t                 res;
 213         struct msghdr   *msghdr;
 214         zend_llist              *allocations;
 215         struct err_s    err = {0};
 216 
 217         //ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
 218         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|l",
 219                         &zsocket, &zmsg, &flags) == FAILURE) {
 220                 return;
 221         }
 222 
 223         LONG_CHECK_VALID_INT(flags);
 224 
 225         ZEND_FETCH_RESOURCE(php_sock, php_socket *, &zsocket, -1,
 226                         php_sockets_le_socket_name, php_sockets_le_socket());
 227 
 228         msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_recv,
 229                         sizeof(*msghdr), "msghdr", &allocations, &err);
 230 
 231         if (err.has_error) {
 232                 err_msg_dispose(&err TSRMLS_CC);
 233                 RETURN_FALSE;
 234         }
 235 
 236         res = recvmsg(php_sock->bsd_socket, msghdr, (int)flags);
 237 
 238         if (res != -1) {
 239                 zval *zres;
 240                 struct key_value kv[] = {
 241                                 {KEY_RECVMSG_RET, sizeof(KEY_RECVMSG_RET), &res},
 242                                 {0}
 243                 };
 244 
 245 
 246                 zres = to_zval_run_conversions((char *)msghdr, to_zval_read_msghdr,
 247                                 "msghdr", kv, &err);
 248 
 249                 /* we don;t need msghdr anymore; free it */
 250                 msghdr = NULL;
 251                 allocations_dispose(&allocations);
 252 
 253                 zval_dtor(zmsg);
 254                 if (!err.has_error) {
 255                         ZVAL_COPY_VALUE(zmsg, zres);
 256                         efree(zres); /* only shallow destruction */
 257                 } else {
 258                         err_msg_dispose(&err TSRMLS_CC);
 259                         ZVAL_FALSE(zmsg);
 260                         /* no need to destroy/free zres -- it's NULL in this circumstance */
 261                         assert(zres == NULL);
 262                 }
 263         } else {
 264                 SOCKETS_G(last_error) = errno;
 265                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error in recvmsg [%d]: %s",
 266                                 errno, sockets_strerror(errno TSRMLS_CC));
 267                 RETURN_FALSE;
 268         }
 269 
 270         RETURN_LONG((long)res);
 271 }
 272 
 273 PHP_FUNCTION(socket_cmsg_space)
 274 {
 275         long                            level,
 276                                                 type,
 277                                                 n = 0;
 278         ancillary_reg_entry     *entry;
 279 
 280         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|l",
 281                         &level, &type, &n) == FAILURE) {
 282                 return;
 283         }
 284 
 285         LONG_CHECK_VALID_INT(level);
 286         LONG_CHECK_VALID_INT(type);
 287         LONG_CHECK_VALID_INT(n);
 288 
 289         if (n < 0) {
 290                 php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The third argument "
 291                                 "cannot be negative");
 292                 return;
 293         }
 294 
 295         entry = get_ancillary_reg_entry(level, type);
 296         if (entry == NULL) {
 297                 php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The pair level %ld/type %ld is "
 298                                 "not supported by PHP", level, type);
 299                 return;
 300         }
 301 
 302         if (entry->var_el_size > 0 && n > (LONG_MAX - (long)entry->size -
 303                         (long)CMSG_SPACE(0) - 15L) / entry->var_el_size) {
 304                 /* the -15 is to account for any padding CMSG_SPACE may add after the data */
 305                 php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The value for the "
 306                                 "third argument (%ld) is too large", n);
 307                 return;
 308         }
 309 
 310         RETURN_LONG((long)CMSG_SPACE(entry->size + n * entry->var_el_size));
 311 }
 312 
 313 #if HAVE_IPV6
 314 int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval **arg4 TSRMLS_DC)
 315 {
 316         struct err_s    err = {0};
 317         zend_llist              *allocations = NULL;
 318         void                    *opt_ptr;
 319         socklen_t               optlen;
 320         int                             retval;
 321 
 322         assert(level == IPPROTO_IPV6);
 323 
 324         switch (optname) {
 325 #ifdef IPV6_PKTINFO
 326         case IPV6_PKTINFO:
 327 #ifdef PHP_WIN32
 328                 if (Z_TYPE_PP(arg4) == IS_ARRAY) {
 329                         php_error_docref0(NULL TSRMLS_CC, E_WARNING, "Windows does not "
 330                                         "support sticky IPV6_PKTINFO");
 331                         return FAILURE;
 332                 } else {
 333                         /* windows has no IPV6_RECVPKTINFO, and uses IPV6_PKTINFO
 334                          * for the same effect. We define IPV6_RECVPKTINFO to be
 335                          * IPV6_PKTINFO, so assume the assume user used IPV6_RECVPKTINFO */
 336                         return 1;
 337                 }
 338 #endif
 339                 opt_ptr = from_zval_run_conversions(*arg4, php_sock, from_zval_write_in6_pktinfo,
 340                                 sizeof(struct in6_pktinfo),     "in6_pktinfo", &allocations, &err);
 341                 if (err.has_error) {
 342                         err_msg_dispose(&err TSRMLS_CC);
 343                         return FAILURE;
 344                 }
 345 
 346                 optlen = sizeof(struct in6_pktinfo);
 347                 goto dosockopt;
 348 #endif
 349         }
 350 
 351         /* we also support IPV6_TCLASS, but that can be handled by the default
 352          * integer optval handling in the caller */
 353         return 1;
 354 
 355 dosockopt:
 356         retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
 357         if (retval != 0) {
 358                 PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
 359         }
 360         allocations_dispose(&allocations);
 361 
 362         return retval != 0 ? FAILURE : SUCCESS;
 363 }
 364 
 365 int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result TSRMLS_DC)
 366 {
 367         struct err_s            err = {0};
 368         void                            *buffer;
 369         socklen_t                       size;
 370         int                                     res;
 371         to_zval_read_field      *reader;
 372 
 373         assert(level == IPPROTO_IPV6);
 374 
 375         switch (optname) {
 376 #ifdef IPV6_PKTINFO
 377         case IPV6_PKTINFO:
 378                 size = sizeof(struct in6_pktinfo);
 379                 reader = &to_zval_read_in6_pktinfo;
 380                 break;
 381 #endif
 382         default:
 383                 return 1;
 384         }
 385 
 386         buffer = ecalloc(1, size);
 387         res = getsockopt(php_sock->bsd_socket, level, optname, buffer, &size);
 388         if (res != 0) {
 389                 PHP_SOCKET_ERROR(php_sock, "unable to get socket option", errno);
 390         } else {
 391                 zval *zv = to_zval_run_conversions(buffer, reader, "in6_pktinfo",
 392                                 empty_key_value_list, &err);
 393                 if (err.has_error) {
 394                         err_msg_dispose(&err TSRMLS_CC);
 395                         res = -1;
 396                 } else {
 397                         ZVAL_COPY_VALUE(result, zv);
 398                         efree(zv);
 399                 }
 400         }
 401         efree(buffer);
 402 
 403         return res == 0 ? SUCCESS : FAILURE;
 404 }
 405 #endif /* HAVE_IPV6 */
 406 
 407 void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)
 408 {
 409         /* IPv6 ancillary data */
 410 #if defined(IPV6_RECVPKTINFO) && HAVE_IPV6
 411         REGISTER_LONG_CONSTANT("IPV6_RECVPKTINFO",              IPV6_RECVPKTINFO,       CONST_CS | CONST_PERSISTENT);
 412         REGISTER_LONG_CONSTANT("IPV6_PKTINFO",          IPV6_PKTINFO,       CONST_CS | CONST_PERSISTENT);
 413 #endif
 414 #if defined(IPV6_RECVHOPLIMIT) && HAVE_IPV6
 415         REGISTER_LONG_CONSTANT("IPV6_RECVHOPLIMIT",             IPV6_RECVHOPLIMIT,      CONST_CS | CONST_PERSISTENT);
 416         REGISTER_LONG_CONSTANT("IPV6_HOPLIMIT",         IPV6_HOPLIMIT,      CONST_CS | CONST_PERSISTENT);
 417 #endif
 418         /* would require some effort:
 419         REGISTER_LONG_CONSTANT("IPV6_RECVRTHDR",                IPV6_RECVRTHDR,         CONST_CS | CONST_PERSISTENT);
 420         REGISTER_LONG_CONSTANT("IPV6_RECVHOPOPTS",              IPV6_RECVHOPOPTS,       CONST_CS | CONST_PERSISTENT);
 421         REGISTER_LONG_CONSTANT("IPV6_RECVDSTOPTS",              IPV6_RECVDSTOPTS,       CONST_CS | CONST_PERSISTENT);
 422         */
 423 #if defined(IPV6_RECVTCLASS) && HAVE_IPV6
 424         REGISTER_LONG_CONSTANT("IPV6_RECVTCLASS",               IPV6_RECVTCLASS,        CONST_CS | CONST_PERSISTENT);
 425         REGISTER_LONG_CONSTANT("IPV6_TCLASS",                   IPV6_TCLASS,            CONST_CS | CONST_PERSISTENT);
 426 #endif
 427 
 428         /*
 429         REGISTER_LONG_CONSTANT("IPV6_RTHDR",                    IPV6_RTHDR,                     CONST_CS | CONST_PERSISTENT);
 430         REGISTER_LONG_CONSTANT("IPV6_HOPOPTS",                  IPV6_HOPOPTS,           CONST_CS | CONST_PERSISTENT);
 431         REGISTER_LONG_CONSTANT("IPV6_DSTOPTS",                  IPV6_DSTOPTS,           CONST_CS | CONST_PERSISTENT);
 432         */
 433 
 434 #ifdef SCM_RIGHTS
 435         REGISTER_LONG_CONSTANT("SCM_RIGHTS",                    SCM_RIGHTS,                     CONST_CS | CONST_PERSISTENT);
 436 #endif
 437 #ifdef SO_PASSCRED
 438         REGISTER_LONG_CONSTANT("SCM_CREDENTIALS",               SCM_CREDENTIALS,        CONST_CS | CONST_PERSISTENT);
 439         REGISTER_LONG_CONSTANT("SO_PASSCRED",                   SO_PASSCRED,            CONST_CS | CONST_PERSISTENT);
 440 #endif
 441 
 442 #ifdef ZTS
 443         ancillary_mutex = tsrm_mutex_alloc();
 444 #endif
 445 }
 446 
 447 void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)
 448 {
 449 #ifdef ZTS
 450         tsrm_mutex_free(ancillary_mutex);
 451 #endif
 452 
 453         destroy_ancillary_registry();
 454 }

/* [<][>][^][v][top][bottom][index][help] */