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

Generated by: LCOV version 1.10

Generated at Wed, 22 Oct 2014 07:25:04 +0000 (3 days ago)

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