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 - main/streams - xp_socket.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 264 357 73.9 %
Date: 2016-08-24 Functions: 17 18 94.4 %
Legend: Lines: hit not hit

          Line data    Source code
       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             :   | Author: Wez Furlong <wez@thebrainroom.com>                           |
      16             :   +----------------------------------------------------------------------+
      17             : */
      18             : 
      19             : /* $Id$ */
      20             : 
      21             : #include "php.h"
      22             : #include "ext/standard/file.h"
      23             : #include "streams/php_streams_int.h"
      24             : #include "php_network.h"
      25             : 
      26             : #if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
      27             : # undef AF_UNIX
      28             : #endif
      29             : 
      30             : #if defined(AF_UNIX)
      31             : #include <sys/un.h>
      32             : #endif
      33             : 
      34             : #ifndef MSG_DONTWAIT
      35             : # define MSG_DONTWAIT 0
      36             : #endif
      37             : 
      38             : #ifndef MSG_PEEK
      39             : # define MSG_PEEK 0
      40             : #endif
      41             : 
      42             : #ifdef PHP_WIN32
      43             : /* send/recv family on windows expects int */
      44             : # define XP_SOCK_BUF_SIZE(sz) (((sz) > INT_MAX) ? INT_MAX : (int)(sz))
      45             : #else
      46             : # define XP_SOCK_BUF_SIZE(sz) (sz)
      47             : #endif
      48             : 
      49             : php_stream_ops php_stream_generic_socket_ops;
      50             : PHPAPI php_stream_ops php_stream_socket_ops;
      51             : php_stream_ops php_stream_udp_socket_ops;
      52             : #ifdef AF_UNIX
      53             : php_stream_ops php_stream_unix_socket_ops;
      54             : php_stream_ops php_stream_unixdg_socket_ops;
      55             : #endif
      56             : 
      57             : 
      58             : static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam);
      59             : 
      60             : /* {{{ Generic socket stream operations */
      61       40470 : static size_t php_sockop_write(php_stream *stream, const char *buf, size_t count)
      62             : {
      63       40470 :         php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
      64             :         int didwrite;
      65             :         struct timeval *ptimeout;
      66             : 
      67       40470 :         if (!sock || sock->socket == -1) {
      68           0 :                 return 0;
      69             :         }
      70             : 
      71       40470 :         if (sock->timeout.tv_sec == -1)
      72           0 :                 ptimeout = NULL;
      73             :         else
      74       40470 :                 ptimeout = &sock->timeout;
      75             : 
      76             : retry:
      77       40470 :         didwrite = send(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && ptimeout) ? MSG_DONTWAIT : 0);
      78             : 
      79       40470 :         if (didwrite <= 0) {
      80          11 :                 int err = php_socket_errno();
      81             :                 char *estr;
      82             : 
      83          11 :                 if (sock->is_blocked && (err == EWOULDBLOCK || err == EAGAIN)) {
      84             :                         int retval;
      85             : 
      86           0 :                         sock->timeout_event = 0;
      87             : 
      88             :                         do {
      89           0 :                                 retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout);
      90             : 
      91           0 :                                 if (retval == 0) {
      92           0 :                                         sock->timeout_event = 1;
      93           0 :                                         break;
      94             :                                 }
      95             : 
      96           0 :                                 if (retval > 0) {
      97             :                                         /* writable now; retry */
      98           0 :                                         goto retry;
      99             :                                 }
     100             : 
     101           0 :                                 err = php_socket_errno();
     102           0 :                         } while (err == EINTR);
     103             :                 }
     104          11 :                 estr = php_socket_strerror(err, NULL, 0);
     105          11 :                 php_error_docref(NULL, E_NOTICE, "send of " ZEND_LONG_FMT " bytes failed with errno=%d %s",
     106             :                                 (zend_long)count, err, estr);
     107          11 :                 efree(estr);
     108             :         }
     109             : 
     110       40470 :         if (didwrite > 0) {
     111       40459 :                 php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), didwrite, 0);
     112             :         }
     113             : 
     114       40470 :         if (didwrite < 0) {
     115          11 :                 didwrite = 0;
     116             :         }
     117             : 
     118       40470 :         return didwrite;
     119             : }
     120             : 
     121      119098 : static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock)
     122             : {
     123             :         int retval;
     124             :         struct timeval *ptimeout;
     125             : 
     126      119098 :         if (!sock || sock->socket == -1) {
     127           0 :                 return;
     128             :         }
     129             : 
     130      119098 :         sock->timeout_event = 0;
     131             : 
     132      119098 :         if (sock->timeout.tv_sec == -1)
     133           0 :                 ptimeout = NULL;
     134             :         else
     135      119098 :                 ptimeout = &sock->timeout;
     136             : 
     137             :         while(1) {
     138      119098 :                 retval = php_pollfd_for(sock->socket, PHP_POLLREADABLE, ptimeout);
     139             : 
     140      119098 :                 if (retval == 0)
     141           3 :                         sock->timeout_event = 1;
     142             : 
     143      119098 :                 if (retval >= 0)
     144      119098 :                         break;
     145             : 
     146           0 :                 if (php_socket_errno() != EINTR)
     147           0 :                         break;
     148           0 :         }
     149             : }
     150             : 
     151      119112 : static size_t php_sockop_read(php_stream *stream, char *buf, size_t count)
     152             : {
     153      119112 :         php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
     154      119112 :         ssize_t nr_bytes = 0;
     155             :         int err;
     156             : 
     157      119112 :         if (!sock || sock->socket == -1) {
     158           0 :                 return 0;
     159             :         }
     160             : 
     161      119112 :         if (sock->is_blocked) {
     162      119098 :                 php_sock_stream_wait_for_data(stream, sock);
     163      119098 :                 if (sock->timeout_event)
     164           3 :                         return 0;
     165             :         }
     166             : 
     167      119109 :         nr_bytes = recv(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && sock->timeout.tv_sec != -1) ? MSG_DONTWAIT : 0);
     168      119109 :         err = php_socket_errno();
     169             : 
     170      119109 :         stream->eof = (nr_bytes == 0 || (nr_bytes == -1 && err != EWOULDBLOCK && err != EAGAIN));
     171             : 
     172      119109 :         if (nr_bytes > 0) {
     173      119039 :                 php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0);
     174             :         }
     175             : 
     176      119109 :         if (nr_bytes < 0) {
     177          12 :                 nr_bytes = 0;
     178             :         }
     179             : 
     180      119109 :         return nr_bytes;
     181             : }
     182             : 
     183             : 
     184        2362 : static int php_sockop_close(php_stream *stream, int close_handle)
     185             : {
     186        2362 :         php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
     187             : #ifdef PHP_WIN32
     188             :         int n;
     189             : #endif
     190             : 
     191        2362 :         if (!sock) {
     192           0 :                 return 0;
     193             :         }
     194             : 
     195        2362 :         if (close_handle) {
     196             : 
     197             : #ifdef PHP_WIN32
     198             :                 if (sock->socket == -1)
     199             :                         sock->socket = SOCK_ERR;
     200             : #endif
     201        2362 :                 if (sock->socket != SOCK_ERR) {
     202             : #ifdef PHP_WIN32
     203             :                         /* prevent more data from coming in */
     204             :                         shutdown(sock->socket, SHUT_RD);
     205             : 
     206             :                         /* try to make sure that the OS sends all data before we close the connection.
     207             :                          * Essentially, we are waiting for the socket to become writeable, which means
     208             :                          * that all pending data has been sent.
     209             :                          * We use a small timeout which should encourage the OS to send the data,
     210             :                          * but at the same time avoid hanging indefinitely.
     211             :                          * */
     212             :                         do {
     213             :                                 n = php_pollfd_for_ms(sock->socket, POLLOUT, 500);
     214             :                         } while (n == -1 && php_socket_errno() == EINTR);
     215             : #endif
     216        2362 :                         closesocket(sock->socket);
     217        2362 :                         sock->socket = SOCK_ERR;
     218             :                 }
     219             : 
     220             :         }
     221             : 
     222        2362 :         pefree(sock, php_stream_is_persistent(stream));
     223             : 
     224        2362 :         return 0;
     225             : }
     226             : 
     227        1868 : static int php_sockop_flush(php_stream *stream)
     228             : {
     229             : #if 0
     230             :         php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
     231             :         return fsync(sock->socket);
     232             : #endif
     233        1868 :         return 0;
     234             : }
     235             : 
     236           7 : static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb)
     237             : {
     238             : #if ZEND_WIN32
     239             :         return 0;
     240             : #else
     241           7 :         php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
     242             : 
     243           7 :         return zend_fstat(sock->socket, &ssb->sb);
     244             : #endif
     245             : }
     246             : 
     247           0 : static inline int sock_sendto(php_netstream_data_t *sock, const char *buf, size_t buflen, int flags,
     248             :                 struct sockaddr *addr, socklen_t addrlen
     249             :                 )
     250             : {
     251             :         int ret;
     252           0 :         if (addr) {
     253           0 :                 ret = sendto(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, addr, XP_SOCK_BUF_SIZE(addrlen));
     254             : 
     255           0 :                 return (ret == SOCK_CONN_ERR) ? -1 : ret;
     256             :         }
     257             : #ifdef PHP_WIN32
     258             :         return ((ret = send(sock->socket, buf, buflen > INT_MAX ? INT_MAX : (int)buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
     259             : #else
     260           0 :         return ((ret = send(sock->socket, buf, buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
     261             : #endif
     262             : }
     263             : 
     264           3 : static inline int sock_recvfrom(php_netstream_data_t *sock, char *buf, size_t buflen, int flags,
     265             :                 zend_string **textaddr,
     266             :                 struct sockaddr **addr, socklen_t *addrlen
     267             :                 )
     268             : {
     269             :         int ret;
     270           3 :         int want_addr = textaddr || addr;
     271             : 
     272           3 :         if (want_addr) {
     273             :                 php_sockaddr_storage sa;
     274           0 :                 socklen_t sl = sizeof(sa);
     275           0 :                 ret = recvfrom(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, (struct sockaddr*)&sa, &sl);
     276           0 :                 ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
     277           0 :                 if (sl) {
     278           0 :                         php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
     279             :                                         textaddr, addr, addrlen);
     280             :                 } else {
     281           0 :                         if (textaddr) {
     282           0 :                                 *textaddr = ZSTR_EMPTY_ALLOC();
     283             :                         }
     284           0 :                         if (addr) {
     285           0 :                                 *addr = NULL;
     286           0 :                                 *addrlen = 0;
     287             :                         }
     288             :                 }
     289             :         } else {
     290           3 :                 ret = recv(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags);
     291           3 :                 ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
     292             :         }
     293             : 
     294           3 :         return ret;
     295             : }
     296             : 
     297        1950 : static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam)
     298             : {
     299             :         int oldmode, flags;
     300        1950 :         php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
     301             :         php_stream_xport_param *xparam;
     302             : 
     303        1950 :         if (!sock) {
     304           0 :                 return PHP_STREAM_OPTION_RETURN_NOTIMPL;
     305             :         }
     306             : 
     307        1950 :         switch(option) {
     308             :                 case PHP_STREAM_OPTION_CHECK_LIVENESS:
     309             :                         {
     310             :                                 struct timeval tv;
     311             :                                 char buf;
     312           3 :                                 int alive = 1;
     313             : 
     314           3 :                                 if (value == -1) {
     315           0 :                                         if (sock->timeout.tv_sec == -1) {
     316           0 :                                                 tv.tv_sec = FG(default_socket_timeout);
     317           0 :                                                 tv.tv_usec = 0;
     318             :                                         } else {
     319           0 :                                                 tv = sock->timeout;
     320             :                                         }
     321             :                                 } else {
     322           3 :                                         tv.tv_sec = value;
     323           3 :                                         tv.tv_usec = 0;
     324             :                                 }
     325             : 
     326           3 :                                 if (sock->socket == -1) {
     327           0 :                                         alive = 0;
     328           3 :                                 } else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
     329             : #ifdef PHP_WIN32
     330             :                                         int ret;
     331             : #else
     332             :                                         ssize_t ret;
     333             : #endif
     334             :                                         int err;
     335             : 
     336           3 :                                         ret = recv(sock->socket, &buf, sizeof(buf), MSG_PEEK);
     337           3 :                                         err = php_socket_errno();
     338           3 :                                         if (0 == ret || /* the counterpart did properly shutdown*/
     339             :                                                 (0 > ret && err != EWOULDBLOCK && err != EAGAIN)) { /* there was an unrecoverable error */
     340           1 :                                                 alive = 0;
     341             :                                         }
     342             :                                 }
     343           3 :                                 return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
     344             :                         }
     345             : 
     346             :                 case PHP_STREAM_OPTION_BLOCKING:
     347          13 :                         oldmode = sock->is_blocked;
     348          13 :                         if (SUCCESS == php_set_sock_blocking(sock->socket, value)) {
     349          13 :                                 sock->is_blocked = value;
     350          13 :                                 return oldmode;
     351             :                         }
     352           0 :                         return PHP_STREAM_OPTION_RETURN_ERR;
     353             : 
     354             :                 case PHP_STREAM_OPTION_READ_TIMEOUT:
     355        1765 :                         sock->timeout = *(struct timeval*)ptrparam;
     356        1765 :                         sock->timeout_event = 0;
     357        1765 :                         return PHP_STREAM_OPTION_RETURN_OK;
     358             : 
     359             :                 case PHP_STREAM_OPTION_META_DATA_API:
     360           0 :                         add_assoc_bool((zval *)ptrparam, "timed_out", sock->timeout_event);
     361           0 :                         add_assoc_bool((zval *)ptrparam, "blocked", sock->is_blocked);
     362           0 :                         add_assoc_bool((zval *)ptrparam, "eof", stream->eof);
     363           0 :                         return PHP_STREAM_OPTION_RETURN_OK;
     364             : 
     365             :                 case PHP_STREAM_OPTION_XPORT_API:
     366         152 :                         xparam = (php_stream_xport_param *)ptrparam;
     367             : 
     368         152 :                         switch (xparam->op) {
     369             :                                 case STREAM_XPORT_OP_LISTEN:
     370         142 :                                         xparam->outputs.returncode = (listen(sock->socket, xparam->inputs.backlog) == 0) ?  0: -1;
     371         142 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     372             : 
     373             :                                 case STREAM_XPORT_OP_GET_NAME:
     374           3 :                                         xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
     375           1 :                                                         xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
     376           1 :                                                         xparam->want_addr ? &xparam->outputs.addr : NULL,
     377           1 :                                                         xparam->want_addr ? &xparam->outputs.addrlen : NULL
     378             :                                                         );
     379           1 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     380             : 
     381             :                                 case STREAM_XPORT_OP_GET_PEER_NAME:
     382           3 :                                         xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
     383           1 :                                                         xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
     384           1 :                                                         xparam->want_addr ? &xparam->outputs.addr : NULL,
     385           1 :                                                         xparam->want_addr ? &xparam->outputs.addrlen : NULL
     386             :                                                         );
     387           1 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     388             : 
     389             :                                 case STREAM_XPORT_OP_SEND:
     390           0 :                                         flags = 0;
     391           0 :                                         if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
     392           0 :                                                 flags |= MSG_OOB;
     393             :                                         }
     394           0 :                                         xparam->outputs.returncode = sock_sendto(sock,
     395           0 :                                                         xparam->inputs.buf, xparam->inputs.buflen,
     396             :                                                         flags,
     397             :                                                         xparam->inputs.addr,
     398             :                                                         xparam->inputs.addrlen);
     399           0 :                                         if (xparam->outputs.returncode == -1) {
     400           0 :                                                 char *err = php_socket_strerror(php_socket_errno(), NULL, 0);
     401           0 :                                                 php_error_docref(NULL, E_WARNING,
     402             :                                                         "%s\n", err);
     403           0 :                                                 efree(err);
     404             :                                         }
     405           0 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     406             : 
     407             :                                 case STREAM_XPORT_OP_RECV:
     408           3 :                                         flags = 0;
     409           3 :                                         if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
     410           1 :                                                 flags |= MSG_OOB;
     411             :                                         }
     412           3 :                                         if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) {
     413           1 :                                                 flags |= MSG_PEEK;
     414             :                                         }
     415           9 :                                         xparam->outputs.returncode = sock_recvfrom(sock,
     416             :                                                         xparam->inputs.buf, xparam->inputs.buflen,
     417             :                                                         flags,
     418           3 :                                                         xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
     419           3 :                                                         xparam->want_addr ? &xparam->outputs.addr : NULL,
     420           3 :                                                         xparam->want_addr ? &xparam->outputs.addrlen : NULL
     421             :                                                         );
     422           3 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     423             : 
     424             : 
     425             : #ifdef HAVE_SHUTDOWN
     426             : # ifndef SHUT_RD
     427             : #  define SHUT_RD 0
     428             : # endif
     429             : # ifndef SHUT_WR
     430             : #  define SHUT_WR 1
     431             : # endif
     432             : # ifndef SHUT_RDWR
     433             : #  define SHUT_RDWR 2
     434             : # endif
     435             :                                 case STREAM_XPORT_OP_SHUTDOWN: {
     436             :                                         static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR};
     437             : 
     438           5 :                                         xparam->outputs.returncode = shutdown(sock->socket, shutdown_how[xparam->how]);
     439           5 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     440             :                                 }
     441             : #endif
     442             : 
     443             :                                 default:
     444           0 :                                         return PHP_STREAM_OPTION_RETURN_NOTIMPL;
     445             :                         }
     446             : 
     447             :                 default:
     448          17 :                         return PHP_STREAM_OPTION_RETURN_NOTIMPL;
     449             :         }
     450             : }
     451             : 
     452         222 : static int php_sockop_cast(php_stream *stream, int castas, void **ret)
     453             : {
     454         222 :         php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
     455             : 
     456         222 :         if (!sock) {
     457           0 :                 return FAILURE;
     458             :         }
     459             : 
     460         222 :         switch(castas)  {
     461             :                 case PHP_STREAM_AS_STDIO:
     462           0 :                         if (ret)        {
     463           0 :                                 *(FILE**)ret = fdopen(sock->socket, stream->mode);
     464           0 :                                 if (*ret)
     465           0 :                                         return SUCCESS;
     466           0 :                                 return FAILURE;
     467             :                         }
     468           0 :                         return SUCCESS;
     469             :                 case PHP_STREAM_AS_FD_FOR_SELECT:
     470             :                 case PHP_STREAM_AS_FD:
     471             :                 case PHP_STREAM_AS_SOCKETD:
     472         222 :                         if (ret)
     473         222 :                                 *(php_socket_t *)ret = sock->socket;
     474         222 :                         return SUCCESS;
     475             :                 default:
     476           0 :                         return FAILURE;
     477             :         }
     478             : }
     479             : /* }}} */
     480             : 
     481             : /* These may look identical, but we need them this way so that
     482             :  * we can determine which type of socket we are dealing with
     483             :  * by inspecting stream->ops.
     484             :  * A "useful" side-effect is that the user's scripts can then
     485             :  * make similar decisions using stream_get_meta_data.
     486             :  * */
     487             : php_stream_ops php_stream_generic_socket_ops = {
     488             :         php_sockop_write, php_sockop_read,
     489             :         php_sockop_close, php_sockop_flush,
     490             :         "generic_socket",
     491             :         NULL, /* seek */
     492             :         php_sockop_cast,
     493             :         php_sockop_stat,
     494             :         php_sockop_set_option,
     495             : };
     496             : 
     497             : 
     498             : php_stream_ops php_stream_socket_ops = {
     499             :         php_sockop_write, php_sockop_read,
     500             :         php_sockop_close, php_sockop_flush,
     501             :         "tcp_socket",
     502             :         NULL, /* seek */
     503             :         php_sockop_cast,
     504             :         php_sockop_stat,
     505             :         php_tcp_sockop_set_option,
     506             : };
     507             : 
     508             : php_stream_ops php_stream_udp_socket_ops = {
     509             :         php_sockop_write, php_sockop_read,
     510             :         php_sockop_close, php_sockop_flush,
     511             :         "udp_socket",
     512             :         NULL, /* seek */
     513             :         php_sockop_cast,
     514             :         php_sockop_stat,
     515             :         php_tcp_sockop_set_option,
     516             : };
     517             : 
     518             : #ifdef AF_UNIX
     519             : php_stream_ops php_stream_unix_socket_ops = {
     520             :         php_sockop_write, php_sockop_read,
     521             :         php_sockop_close, php_sockop_flush,
     522             :         "unix_socket",
     523             :         NULL, /* seek */
     524             :         php_sockop_cast,
     525             :         php_sockop_stat,
     526             :         php_tcp_sockop_set_option,
     527             : };
     528             : php_stream_ops php_stream_unixdg_socket_ops = {
     529             :         php_sockop_write, php_sockop_read,
     530             :         php_sockop_close, php_sockop_flush,
     531             :         "udg_socket",
     532             :         NULL, /* seek */
     533             :         php_sockop_cast,
     534             :         php_sockop_stat,
     535             :         php_tcp_sockop_set_option,
     536             : };
     537             : #endif
     538             : 
     539             : 
     540             : /* network socket operations */
     541             : 
     542             : #ifdef AF_UNIX
     543        1714 : static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr)
     544             : {
     545        1714 :         memset(unix_addr, 0, sizeof(*unix_addr));
     546        1714 :         unix_addr->sun_family = AF_UNIX;
     547             : 
     548             :         /* we need to be binary safe on systems that support an abstract
     549             :          * namespace */
     550        1714 :         if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
     551             :                 /* On linux, when the path begins with a NUL byte we are
     552             :                  * referring to an abstract namespace.  In theory we should
     553             :                  * allow an extra byte below, since we don't need the NULL.
     554             :                  * BUT, to get into this branch of code, the name is too long,
     555             :                  * so we don't care. */
     556           1 :                 xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
     557           1 :                 php_error_docref(NULL, E_NOTICE,
     558             :                         "socket path exceeded the maximum allowed length of %lu bytes "
     559             :                         "and was truncated", (unsigned long)sizeof(unix_addr->sun_path));
     560             :         }
     561             : 
     562        1714 :         memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
     563             : 
     564        1714 :         return 1;
     565             : }
     566             : #endif
     567             : 
     568        2235 : static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err)
     569             : {
     570             :         char *colon;
     571        2235 :         char *host = NULL;
     572             : 
     573             : #ifdef HAVE_IPV6
     574             :         char *p;
     575             : 
     576        2235 :         if (*(str) == '[' && str_len > 1) {
     577             :                 /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
     578          13 :                 p = memchr(str + 1, ']', str_len - 2);
     579          13 :                 if (!p || *(p + 1) != ':') {
     580           1 :                         if (get_err) {
     581           1 :                                 *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str);
     582             :                         }
     583           1 :                         return NULL;
     584             :                 }
     585          12 :                 *portno = atoi(p + 2);
     586          12 :                 return estrndup(str + 1, p - str - 1);
     587             :         }
     588             : #endif
     589        2222 :         if (str_len) {
     590        2221 :                 colon = memchr(str, ':', str_len - 1);
     591             :         } else {
     592           1 :                 colon = NULL;
     593             :         }
     594        2222 :         if (colon) {
     595        2218 :                 *portno = atoi(colon + 1);
     596        2218 :                 host = estrndup(str, colon - str);
     597             :         } else {
     598           4 :                 if (get_err) {
     599           4 :                         *err = strpprintf(0, "Failed to parse address \"%s\"", str);
     600             :                 }
     601           4 :                 return NULL;
     602             :         }
     603             : 
     604        2218 :         return host;
     605             : }
     606             : 
     607        2235 : static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno)
     608             : {
     609        2235 :         return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text);
     610             : }
     611             : 
     612         154 : static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
     613             :                 php_stream_xport_param *xparam)
     614             : {
     615         154 :         char *host = NULL;
     616             :         int portno, err;
     617         154 :         long sockopts = STREAM_SOCKOP_NONE;
     618         154 :         zval *tmpzval = NULL;
     619             : 
     620             : #ifdef AF_UNIX
     621         154 :         if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
     622             :                 struct sockaddr_un unix_addr;
     623             : 
     624           5 :                 sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
     625             : 
     626           5 :                 if (sock->socket == SOCK_ERR) {
     627           0 :                         if (xparam->want_errortext) {
     628           0 :                                 xparam->outputs.error_text = strpprintf(0, "Failed to create unix%s socket %s",
     629           0 :                                                 stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
     630             :                                                 strerror(errno));
     631             :                         }
     632           0 :                         return -1;
     633             :                 }
     634             : 
     635           5 :                 parse_unix_address(xparam, &unix_addr);
     636             : 
     637           5 :                 return bind(sock->socket, (const struct sockaddr *)&unix_addr,
     638             :                         (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen);
     639             :         }
     640             : #endif
     641             : 
     642         149 :         host = parse_ip_address(xparam, &portno);
     643             : 
     644         149 :         if (host == NULL) {
     645           0 :                 return -1;
     646             :         }
     647             : 
     648             : #ifdef IPV6_V6ONLY
     649         298 :         if (PHP_STREAM_CONTEXT(stream)
     650         149 :                 && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "ipv6_v6only")) != NULL
     651             :                 && Z_TYPE_P(tmpzval) != IS_NULL
     652             :         ) {
     653           0 :                 sockopts |= STREAM_SOCKOP_IPV6_V6ONLY;
     654           0 :                 sockopts |= STREAM_SOCKOP_IPV6_V6ONLY_ENABLED * zend_is_true(tmpzval);
     655             :         }
     656             : #endif
     657             : 
     658             : #ifdef SO_REUSEPORT
     659         298 :         if (PHP_STREAM_CONTEXT(stream)
     660         149 :                 && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_reuseport")) != NULL
     661             :                 && zend_is_true(tmpzval)
     662           0 :         ) {
     663           0 :                 sockopts |= STREAM_SOCKOP_SO_REUSEPORT;
     664             :         }
     665             : #endif
     666             : 
     667             : #ifdef SO_BROADCAST
     668         182 :         if (stream->ops == &php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
     669         171 :                 && PHP_STREAM_CONTEXT(stream)
     670          11 :                 && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
     671             :                 && zend_is_true(tmpzval)
     672           0 :         ) {
     673           0 :                 sockopts |= STREAM_SOCKOP_SO_BROADCAST;
     674             :         }
     675             : #endif
     676             : 
     677         298 :         sock->socket = php_network_bind_socket_to_local_addr(host, portno,
     678         149 :                         stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
     679             :                         sockopts,
     680         149 :                         xparam->want_errortext ? &xparam->outputs.error_text : NULL,
     681             :                         &err
     682             :                         );
     683             : 
     684         149 :         if (host) {
     685         149 :                 efree(host);
     686             :         }
     687             : 
     688         149 :         return sock->socket == -1 ? -1 : 0;
     689             : }
     690             : 
     691        3795 : static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
     692             :                 php_stream_xport_param *xparam)
     693             : {
     694        3795 :         char *host = NULL, *bindto = NULL;
     695        3795 :         int portno, bindport = 0;
     696        3795 :         int err = 0;
     697             :         int ret;
     698        3795 :         zval *tmpzval = NULL;
     699        3795 :         long sockopts = STREAM_SOCKOP_NONE;
     700             : 
     701             : #ifdef AF_UNIX
     702        3795 :         if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
     703             :                 struct sockaddr_un unix_addr;
     704             : 
     705        1709 :                 sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
     706             : 
     707        1709 :                 if (sock->socket == SOCK_ERR) {
     708           0 :                         if (xparam->want_errortext) {
     709           0 :                                 xparam->outputs.error_text = strpprintf(0, "Failed to create unix socket");
     710             :                         }
     711           0 :                         return -1;
     712             :                 }
     713             : 
     714        1709 :                 parse_unix_address(xparam, &unix_addr);
     715             : 
     716        1709 :                 ret = php_network_connect_socket(sock->socket,
     717             :                                 (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen,
     718             :                                 xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
     719        1709 :                                 xparam->want_errortext ? &xparam->outputs.error_text : NULL,
     720             :                                 &err);
     721             : 
     722        1709 :                 xparam->outputs.error_code = err;
     723             : 
     724        1709 :                 goto out;
     725             :         }
     726             : #endif
     727             : 
     728        2086 :         host = parse_ip_address(xparam, &portno);
     729             : 
     730        2086 :         if (host == NULL) {
     731           5 :                 return -1;
     732             :         }
     733             : 
     734        2081 :         if (PHP_STREAM_CONTEXT(stream) && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "bindto")) != NULL) {
     735           0 :                 if (Z_TYPE_P(tmpzval) != IS_STRING) {
     736           0 :                         if (xparam->want_errortext) {
     737           0 :                                 xparam->outputs.error_text = strpprintf(0, "local_addr context option is not a string.");
     738             :                         }
     739           0 :                         efree(host);
     740           0 :                         return -1;
     741             :                 }
     742           0 :                 bindto = parse_ip_address_ex(Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text);
     743             :         }
     744             : 
     745             : #ifdef SO_BROADCAST
     746        3928 :         if (stream->ops == &php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
     747        3314 :                 && PHP_STREAM_CONTEXT(stream)
     748         614 :                 && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
     749             :                 && zend_is_true(tmpzval)
     750           0 :         ) {
     751           0 :                 sockopts |= STREAM_SOCKOP_SO_BROADCAST;
     752             :         }
     753             : #endif
     754             : 
     755        6809 :         if (stream->ops != &php_stream_udp_socket_ops /* TCP_NODELAY is only applicable for TCP */
     756             : #ifdef AF_UNIX
     757        2081 :                 && stream->ops != &php_stream_unix_socket_ops
     758        1462 :                 && stream->ops != &php_stream_unixdg_socket_ops
     759             : #endif
     760        3094 :                 && PHP_STREAM_CONTEXT(stream)
     761         170 :                 && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL
     762             :                 && zend_is_true(tmpzval)
     763           2 :         ) {
     764           2 :                 sockopts |= STREAM_SOCKOP_TCP_NODELAY;
     765             :         }
     766             : 
     767             :         /* Note: the test here for php_stream_udp_socket_ops is important, because we
     768             :          * want the default to be TCP sockets so that the openssl extension can
     769             :          * re-use this code. */
     770             : 
     771        6243 :         sock->socket = php_network_connect_socket_to_host(host, portno,
     772        2081 :                         stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
     773             :                         xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
     774             :                         xparam->inputs.timeout,
     775        2081 :                         xparam->want_errortext ? &xparam->outputs.error_text : NULL,
     776             :                         &err,
     777             :                         bindto,
     778             :                         bindport,
     779             :                         sockopts
     780             :                         );
     781             : 
     782        2081 :         ret = sock->socket == -1 ? -1 : 0;
     783        2081 :         xparam->outputs.error_code = err;
     784             : 
     785        2081 :         if (host) {
     786        2081 :                 efree(host);
     787             :         }
     788        2081 :         if (bindto) {
     789           0 :                 efree(bindto);
     790             :         }
     791             : 
     792             : #ifdef AF_UNIX
     793             : out:
     794             : #endif
     795             : 
     796        3790 :         if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
     797             :                 /* indicates pending connection */
     798           1 :                 return 1;
     799             :         }
     800             : 
     801        3789 :         return ret;
     802             : }
     803             : 
     804           1 : static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
     805             :                 php_stream_xport_param *xparam STREAMS_DC)
     806             : {
     807             :         int clisock;
     808           1 :         zend_bool nodelay = 0;
     809           1 :         zval *tmpzval = NULL;
     810             : 
     811           1 :         xparam->outputs.client = NULL;
     812             : 
     813           2 :         if ((NULL != PHP_STREAM_CONTEXT(stream)) &&
     814           1 :                 (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
     815           0 :                 zend_is_true(tmpzval)) {
     816           0 :                 nodelay = 1;
     817             :         }
     818             : 
     819           5 :         clisock = php_network_accept_incoming(sock->socket,
     820           1 :                 xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
     821           1 :                 xparam->want_addr ? &xparam->outputs.addr : NULL,
     822           1 :                 xparam->want_addr ? &xparam->outputs.addrlen : NULL,
     823             :                 xparam->inputs.timeout,
     824           1 :                 xparam->want_errortext ? &xparam->outputs.error_text : NULL,
     825             :                 &xparam->outputs.error_code,
     826             :                 nodelay);
     827             : 
     828           1 :         if (clisock >= 0) {
     829           1 :                 php_netstream_data_t *clisockdata = (php_netstream_data_t*) emalloc(sizeof(*clisockdata));
     830             : 
     831           1 :                 memcpy(clisockdata, sock, sizeof(*clisockdata));
     832           1 :                 clisockdata->socket = clisock;
     833             : 
     834           1 :                 xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
     835           1 :                 if (xparam->outputs.client) {
     836           1 :                         xparam->outputs.client->ctx = stream->ctx;
     837           1 :                         if (stream->ctx) {
     838           1 :                                 GC_REFCOUNT(stream->ctx)++;
     839             :                         }
     840             :                 }
     841             :         }
     842             : 
     843           1 :         return xparam->outputs.client == NULL ? -1 : 0;
     844             : }
     845             : 
     846        5891 : static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam)
     847             : {
     848        5891 :         php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
     849             :         php_stream_xport_param *xparam;
     850             : 
     851        5891 :         switch(option) {
     852             :                 case PHP_STREAM_OPTION_XPORT_API:
     853        4100 :                         xparam = (php_stream_xport_param *)ptrparam;
     854             : 
     855        4100 :                         switch(xparam->op) {
     856             :                                 case STREAM_XPORT_OP_CONNECT:
     857             :                                 case STREAM_XPORT_OP_CONNECT_ASYNC:
     858        3795 :                                         xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam);
     859        3795 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     860             : 
     861             :                                 case STREAM_XPORT_OP_BIND:
     862         154 :                                         xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam);
     863         154 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     864             : 
     865             : 
     866             :                                 case STREAM_XPORT_OP_ACCEPT:
     867           1 :                                         xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC);
     868           1 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     869             :                                 default:
     870             :                                         /* fall through */
     871             :                                         ;
     872             :                         }
     873             :         }
     874        1941 :         return php_sockop_set_option(stream, option, value, ptrparam);
     875             : }
     876             : 
     877             : 
     878        2344 : PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, size_t protolen,
     879             :                 const char *resourcename, size_t resourcenamelen,
     880             :                 const char *persistent_id, int options, int flags,
     881             :                 struct timeval *timeout,
     882             :                 php_stream_context *context STREAMS_DC)
     883             : {
     884        2344 :         php_stream *stream = NULL;
     885             :         php_netstream_data_t *sock;
     886             :         php_stream_ops *ops;
     887             : 
     888             :         /* which type of socket ? */
     889        2344 :         if (strncmp(proto, "tcp", protolen) == 0) {
     890           0 :                 ops = &php_stream_socket_ops;
     891        2344 :         } else if (strncmp(proto, "udp", protolen) == 0) {
     892         630 :                 ops = &php_stream_udp_socket_ops;
     893             :         }
     894             : #ifdef AF_UNIX
     895        1714 :         else if (strncmp(proto, "unix", protolen) == 0) {
     896        1712 :                 ops = &php_stream_unix_socket_ops;
     897           2 :         } else if (strncmp(proto, "udg", protolen) == 0) {
     898           2 :                 ops = &php_stream_unixdg_socket_ops;
     899             :         }
     900             : #endif
     901             :         else {
     902             :                 /* should never happen */
     903           0 :                 return NULL;
     904             :         }
     905             : 
     906        2344 :         sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
     907        2344 :         memset(sock, 0, sizeof(php_netstream_data_t));
     908             : 
     909        2344 :         sock->is_blocked = 1;
     910        2344 :         sock->timeout.tv_sec = FG(default_socket_timeout);
     911        2344 :         sock->timeout.tv_usec = 0;
     912             : 
     913             :         /* we don't know the socket until we have determined if we are binding or
     914             :          * connecting */
     915        2344 :         sock->socket = -1;
     916             : 
     917        2344 :         stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
     918             : 
     919        2344 :         if (stream == NULL)     {
     920           0 :                 pefree(sock, persistent_id ? 1 : 0);
     921           0 :                 return NULL;
     922             :         }
     923             : 
     924        2344 :         if (flags == 0) {
     925           0 :                 return stream;
     926             :         }
     927             : 
     928        2344 :         return stream;
     929             : }
     930             : 
     931             : 
     932             : /*
     933             :  * Local variables:
     934             :  * tab-width: 4
     935             :  * c-basic-offset: 4
     936             :  * End:
     937             :  * vim600: noet sw=4 ts=4 fdm=marker
     938             :  * vim<600: noet sw=4 ts=4
     939             :  */

Generated by: LCOV version 1.10

Generated at Wed, 24 Aug 2016 12:20:36 +0000 (3 days ago)

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