root/main/streams/xp_socket.c

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

DEFINITIONS

This source file includes following definitions.
  1. php_sockop_write
  2. php_sock_stream_wait_for_data
  3. php_sockop_read
  4. php_sockop_close
  5. php_sockop_flush
  6. php_sockop_stat
  7. sock_sendto
  8. sock_recvfrom
  9. php_sockop_set_option
  10. php_sockop_cast
  11. parse_unix_address
  12. parse_ip_address_ex
  13. parse_ip_address
  14. php_tcp_sockop_bind
  15. php_tcp_sockop_connect
  16. php_tcp_sockop_accept
  17. php_tcp_sockop_set_option
  18. php_stream_generic_socket_factory

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

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