PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/sockets - sendrecvmsg.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 99 136 72.8 %
Date: 2014-10-14 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Tue, 14 Oct 2014 07:25:49 +0000 (7 days ago)

Copyright © 2005-2014 The PHP Group
All rights reserved.