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

Generated by: LCOV version 1.10

Generated at Sat, 13 Dec 2014 06:16:27 +0000 (9 days ago)

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