root/ext/sockets/sendrecvmsg.c

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

DEFINITIONS

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

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 7                                                        |
   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, E_WARNING, "The value %pd 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 static void ancillary_registery_free_elem(zval *el) {
  87         pefree(Z_PTR_P(el), 1);
  88 }
  89 
  90 #ifdef ZTS
  91 static MUTEX_T ancillary_mutex;
  92 #endif
  93 static void init_ancillary_registry(void)
  94 {
  95         ancillary_reg_entry entry;
  96         anc_reg_key key;
  97         ancillary_registry.initialized = 1;
  98 
  99         zend_hash_init(&ancillary_registry.ht, 32, NULL, ancillary_registery_free_elem, 1);
 100 
 101 #define PUT_ENTRY(sizev, var_size, calc, from, to, level, type) \
 102         entry.size                      = sizev; \
 103         entry.var_el_size       = var_size; \
 104         entry.calc_space        = calc; \
 105         entry.from_array        = from; \
 106         entry.to_array          = to; \
 107         key.cmsg_level          = level; \
 108         key.cmsg_type           = type; \
 109         zend_hash_str_update_mem(&ancillary_registry.ht, (char*)&key, sizeof(key) - 1, (void*)&entry, sizeof(entry))
 110 
 111 #if defined(IPV6_PKTINFO) && HAVE_IPV6
 112         PUT_ENTRY(sizeof(struct in6_pktinfo), 0, 0, from_zval_write_in6_pktinfo,
 113                         to_zval_read_in6_pktinfo, IPPROTO_IPV6, IPV6_PKTINFO);
 114 #endif
 115 
 116 #if defined(IPV6_HOPLIMIT) && HAVE_IPV6
 117         PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
 118                         to_zval_read_int, IPPROTO_IPV6, IPV6_HOPLIMIT);
 119 #endif
 120 
 121 #if defined(IPV6_TCLASS) && HAVE_IPV6
 122         PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
 123                         to_zval_read_int, IPPROTO_IPV6, IPV6_TCLASS);
 124 #endif
 125 
 126 #ifdef SO_PASSCRED
 127         PUT_ENTRY(sizeof(struct ucred), 0, 0, from_zval_write_ucred,
 128                         to_zval_read_ucred, SOL_SOCKET, SCM_CREDENTIALS);
 129 #endif
 130 
 131 #ifdef SCM_RIGHTS
 132         PUT_ENTRY(0, sizeof(int), calculate_scm_rights_space, from_zval_write_fd_array,
 133                         to_zval_read_fd_array, SOL_SOCKET, SCM_RIGHTS);
 134 #endif
 135 
 136 }
 137 static void destroy_ancillary_registry(void)
 138 {
 139         if (ancillary_registry.initialized) {
 140                 zend_hash_destroy(&ancillary_registry.ht);
 141                 ancillary_registry.initialized = 0;
 142         }
 143 }
 144 ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type)
 145 {
 146         anc_reg_key                     key = { cmsg_level, msg_type };
 147         ancillary_reg_entry     *entry;
 148 
 149 #ifdef ZTS
 150         tsrm_mutex_lock(ancillary_mutex);
 151 #endif
 152         if (!ancillary_registry.initialized) {
 153                 init_ancillary_registry();
 154         }
 155 #ifdef ZTS
 156         tsrm_mutex_unlock(ancillary_mutex);
 157 #endif
 158 
 159         if ((entry = zend_hash_str_find_ptr(&ancillary_registry.ht, (char*)&key, sizeof(key) - 1)) != NULL) {
 160                 return entry;
 161         } else {
 162                 return NULL;
 163         }
 164 }
 165 
 166 PHP_FUNCTION(socket_sendmsg)
 167 {
 168         zval                    *zsocket,
 169                                         *zmsg;
 170         zend_long                       flags = 0;
 171         php_socket              *php_sock;
 172         struct msghdr   *msghdr;
 173         zend_llist              *allocations;
 174         struct err_s    err = {0};
 175         ssize_t                 res;
 176 
 177         /* zmsg should be passed by ref */
 178         if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra|l", &zsocket, &zmsg, &flags) == FAILURE) {
 179                 return;
 180         }
 181 
 182         LONG_CHECK_VALID_INT(flags);
 183 
 184         if ((php_sock = (php_socket *)zend_fetch_resource(Z_RES_P(zsocket),
 185                                         php_sockets_le_socket_name, php_sockets_le_socket())) == NULL) {
 186                 RETURN_FALSE;
 187         }
 188 
 189         msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_send,
 190                         sizeof(*msghdr), "msghdr", &allocations, &err);
 191 
 192         if (err.has_error) {
 193                 err_msg_dispose(&err);
 194                 RETURN_FALSE;
 195         }
 196 
 197         res = sendmsg(php_sock->bsd_socket, msghdr, (int)flags);
 198 
 199         if (res != -1) {
 200                 zend_llist_destroy(allocations);
 201                 efree(allocations);
 202 
 203                 RETURN_LONG((zend_long)res);
 204         } else {
 205                 PHP_SOCKET_ERROR(php_sock, "error in sendmsg", errno);
 206                 RETURN_FALSE;
 207         }
 208 }
 209 
 210 PHP_FUNCTION(socket_recvmsg)
 211 {
 212         zval                    *zsocket,
 213                                         *zmsg;
 214         zend_long                       flags = 0;
 215         php_socket              *php_sock;
 216         ssize_t                 res;
 217         struct msghdr   *msghdr;
 218         zend_llist              *allocations;
 219         struct err_s    err = {0};
 220 
 221         //ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
 222         if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra/|l",
 223                         &zsocket, &zmsg, &flags) == FAILURE) {
 224                 return;
 225         }
 226 
 227         LONG_CHECK_VALID_INT(flags);
 228 
 229         if ((php_sock = (php_socket *)zend_fetch_resource(Z_RES_P(zsocket),
 230                                         php_sockets_le_socket_name, php_sockets_le_socket())) == NULL) {
 231                 RETURN_FALSE;
 232         }
 233 
 234         msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_recv,
 235                         sizeof(*msghdr), "msghdr", &allocations, &err);
 236 
 237         if (err.has_error) {
 238                 err_msg_dispose(&err);
 239                 RETURN_FALSE;
 240         }
 241 
 242         res = recvmsg(php_sock->bsd_socket, msghdr, (int)flags);
 243 
 244         if (res != -1) {
 245                 zval *zres, tmp;
 246                 struct key_value kv[] = {
 247                                 {KEY_RECVMSG_RET, sizeof(KEY_RECVMSG_RET), &res},
 248                                 {0}
 249                 };
 250 
 251 
 252                 zres = to_zval_run_conversions((char *)msghdr, to_zval_read_msghdr,
 253                                 "msghdr", kv, &err, &tmp);
 254 
 255                 /* we don;t need msghdr anymore; free it */
 256                 msghdr = NULL;
 257                 allocations_dispose(&allocations);
 258 
 259                 zval_dtor(zmsg);
 260                 if (!err.has_error) {
 261                         ZVAL_COPY_VALUE(zmsg, zres);
 262                 } else {
 263                         err_msg_dispose(&err);
 264                         ZVAL_FALSE(zmsg);
 265                         /* no need to destroy/free zres -- it's NULL in this circumstance */
 266                         assert(zres == NULL);
 267                 }
 268         } else {
 269                 SOCKETS_G(last_error) = errno;
 270                 php_error_docref(NULL, E_WARNING, "error in recvmsg [%d]: %s",
 271                                 errno, sockets_strerror(errno));
 272                 RETURN_FALSE;
 273         }
 274 
 275         RETURN_LONG((zend_long)res);
 276 }
 277 
 278 PHP_FUNCTION(socket_cmsg_space)
 279 {
 280         zend_long                               level,
 281                                                 type,
 282                                                 n = 0;
 283         ancillary_reg_entry     *entry;
 284 
 285         if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|l",
 286                         &level, &type, &n) == FAILURE) {
 287                 return;
 288         }
 289 
 290         LONG_CHECK_VALID_INT(level);
 291         LONG_CHECK_VALID_INT(type);
 292         LONG_CHECK_VALID_INT(n);
 293 
 294         if (n < 0) {
 295                 php_error_docref0(NULL, E_WARNING, "The third argument "
 296                                 "cannot be negative");
 297                 return;
 298         }
 299 
 300         entry = get_ancillary_reg_entry(level, type);
 301         if (entry == NULL) {
 302                 php_error_docref0(NULL, E_WARNING, "The pair level %pd/type %pd is "
 303                                 "not supported by PHP", level, type);
 304                 return;
 305         }
 306 
 307         if (entry->var_el_size > 0 && n > (ZEND_LONG_MAX - (zend_long)entry->size -
 308                         (zend_long)CMSG_SPACE(0) - 15L) / entry->var_el_size) {
 309                 /* the -15 is to account for any padding CMSG_SPACE may add after the data */
 310                 php_error_docref0(NULL, E_WARNING, "The value for the "
 311                                 "third argument (%pd) is too large", n);
 312                 return;
 313         }
 314 
 315         RETURN_LONG((zend_long)CMSG_SPACE(entry->size + n * entry->var_el_size));
 316 }
 317 
 318 #if HAVE_IPV6
 319 int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *arg4)
 320 {
 321         struct err_s    err = {0};
 322         zend_llist              *allocations = NULL;
 323         void                    *opt_ptr;
 324         socklen_t               optlen;
 325         int                             retval;
 326 
 327         assert(level == IPPROTO_IPV6);
 328 
 329         switch (optname) {
 330 #ifdef IPV6_PKTINFO
 331         case IPV6_PKTINFO:
 332 #ifdef PHP_WIN32
 333                 if (Z_TYPE_P(arg4) == IS_ARRAY) {
 334                         php_error_docref0(NULL, E_WARNING, "Windows does not "
 335                                         "support sticky IPV6_PKTINFO");
 336                         return FAILURE;
 337                 } else {
 338                         /* windows has no IPV6_RECVPKTINFO, and uses IPV6_PKTINFO
 339                          * for the same effect. We define IPV6_RECVPKTINFO to be
 340                          * IPV6_PKTINFO, so assume the assume user used IPV6_RECVPKTINFO */
 341                         return 1;
 342                 }
 343 #endif
 344                 opt_ptr = from_zval_run_conversions(arg4, php_sock, from_zval_write_in6_pktinfo,
 345                                 sizeof(struct in6_pktinfo),     "in6_pktinfo", &allocations, &err);
 346                 if (err.has_error) {
 347                         err_msg_dispose(&err);
 348                         return FAILURE;
 349                 }
 350 
 351                 optlen = sizeof(struct in6_pktinfo);
 352                 goto dosockopt;
 353 #endif
 354         }
 355 
 356         /* we also support IPV6_TCLASS, but that can be handled by the default
 357          * integer optval handling in the caller */
 358         return 1;
 359 
 360 dosockopt:
 361         retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
 362         if (retval != 0) {
 363                 PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
 364         }
 365         allocations_dispose(&allocations);
 366 
 367         return retval != 0 ? FAILURE : SUCCESS;
 368 }
 369 
 370 int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result)
 371 {
 372         struct err_s            err = {0};
 373         void                            *buffer;
 374         socklen_t                       size;
 375         int                                     res;
 376         to_zval_read_field      *reader;
 377 
 378         assert(level == IPPROTO_IPV6);
 379 
 380         switch (optname) {
 381 #ifdef IPV6_PKTINFO
 382         case IPV6_PKTINFO:
 383                 size = sizeof(struct in6_pktinfo);
 384                 reader = &to_zval_read_in6_pktinfo;
 385                 break;
 386 #endif
 387         default:
 388                 return 1;
 389         }
 390 
 391         buffer = ecalloc(1, size);
 392         res = getsockopt(php_sock->bsd_socket, level, optname, buffer, &size);
 393         if (res != 0) {
 394                 PHP_SOCKET_ERROR(php_sock, "unable to get socket option", errno);
 395         } else {
 396                 zval tmp;
 397                 zval *zv = to_zval_run_conversions(buffer, reader, "in6_pktinfo",
 398                                 empty_key_value_list, &err, &tmp);
 399                 if (err.has_error) {
 400                         err_msg_dispose(&err);
 401                         res = -1;
 402                 } else {
 403                         ZVAL_COPY_VALUE(result, zv);
 404                 }
 405         }
 406         efree(buffer);
 407 
 408         return res == 0 ? SUCCESS : FAILURE;
 409 }
 410 #endif /* HAVE_IPV6 */
 411 
 412 void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)
 413 {
 414         /* IPv6 ancillary data */
 415 #if defined(IPV6_RECVPKTINFO) && HAVE_IPV6
 416         REGISTER_LONG_CONSTANT("IPV6_RECVPKTINFO",              IPV6_RECVPKTINFO,       CONST_CS | CONST_PERSISTENT);
 417         REGISTER_LONG_CONSTANT("IPV6_PKTINFO",          IPV6_PKTINFO,       CONST_CS | CONST_PERSISTENT);
 418 #endif
 419 #if defined(IPV6_RECVHOPLIMIT) && HAVE_IPV6
 420         REGISTER_LONG_CONSTANT("IPV6_RECVHOPLIMIT",             IPV6_RECVHOPLIMIT,      CONST_CS | CONST_PERSISTENT);
 421         REGISTER_LONG_CONSTANT("IPV6_HOPLIMIT",         IPV6_HOPLIMIT,      CONST_CS | CONST_PERSISTENT);
 422 #endif
 423         /* would require some effort:
 424         REGISTER_LONG_CONSTANT("IPV6_RECVRTHDR",                IPV6_RECVRTHDR,         CONST_CS | CONST_PERSISTENT);
 425         REGISTER_LONG_CONSTANT("IPV6_RECVHOPOPTS",              IPV6_RECVHOPOPTS,       CONST_CS | CONST_PERSISTENT);
 426         REGISTER_LONG_CONSTANT("IPV6_RECVDSTOPTS",              IPV6_RECVDSTOPTS,       CONST_CS | CONST_PERSISTENT);
 427         */
 428 #if defined(IPV6_RECVTCLASS) && HAVE_IPV6
 429         REGISTER_LONG_CONSTANT("IPV6_RECVTCLASS",               IPV6_RECVTCLASS,        CONST_CS | CONST_PERSISTENT);
 430         REGISTER_LONG_CONSTANT("IPV6_TCLASS",                   IPV6_TCLASS,            CONST_CS | CONST_PERSISTENT);
 431 #endif
 432 
 433         /*
 434         REGISTER_LONG_CONSTANT("IPV6_RTHDR",                    IPV6_RTHDR,                     CONST_CS | CONST_PERSISTENT);
 435         REGISTER_LONG_CONSTANT("IPV6_HOPOPTS",                  IPV6_HOPOPTS,           CONST_CS | CONST_PERSISTENT);
 436         REGISTER_LONG_CONSTANT("IPV6_DSTOPTS",                  IPV6_DSTOPTS,           CONST_CS | CONST_PERSISTENT);
 437         */
 438 
 439 #ifdef SCM_RIGHTS
 440         REGISTER_LONG_CONSTANT("SCM_RIGHTS",                    SCM_RIGHTS,                     CONST_CS | CONST_PERSISTENT);
 441 #endif
 442 #ifdef SO_PASSCRED
 443         REGISTER_LONG_CONSTANT("SCM_CREDENTIALS",               SCM_CREDENTIALS,        CONST_CS | CONST_PERSISTENT);
 444         REGISTER_LONG_CONSTANT("SO_PASSCRED",                   SO_PASSCRED,            CONST_CS | CONST_PERSISTENT);
 445 #endif
 446 
 447 #ifdef ZTS
 448         ancillary_mutex = tsrm_mutex_alloc();
 449 #endif
 450 }
 451 
 452 void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)
 453 {
 454 #ifdef ZTS
 455         tsrm_mutex_free(ancillary_mutex);
 456 #endif
 457 
 458         destroy_ancillary_registry();
 459 }

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