Back to index

php5  5.3.10
xp_socket.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | PHP Version 5                                                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 1997-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt                                  |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Author: Wez Furlong <wez@thebrainroom.com>                           |
00016   +----------------------------------------------------------------------+
00017 */
00018 
00019 /* $Id: xp_socket.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #include "php.h"
00022 #include "ext/standard/file.h"
00023 #include "streams/php_streams_int.h"
00024 #include "php_network.h"
00025 
00026 #if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
00027 # undef AF_UNIX
00028 #endif
00029 
00030 #if defined(AF_UNIX)
00031 #include <sys/un.h>
00032 #endif
00033 
00034 #ifndef MSG_DONTWAIT
00035 # define MSG_DONTWAIT 0
00036 #endif
00037 
00038 #ifndef MSG_PEEK
00039 # define MSG_PEEK 0
00040 #endif
00041 
00042 php_stream_ops php_stream_generic_socket_ops;
00043 PHPAPI php_stream_ops php_stream_socket_ops;
00044 php_stream_ops php_stream_udp_socket_ops;
00045 #ifdef AF_UNIX
00046 php_stream_ops php_stream_unix_socket_ops;
00047 php_stream_ops php_stream_unixdg_socket_ops;
00048 #endif
00049 
00050 
00051 static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC);
00052 
00053 /* {{{ Generic socket stream operations */
00054 static size_t php_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
00055 {
00056        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
00057        int didwrite;
00058        struct timeval *ptimeout;
00059 
00060        if (sock->socket == -1) {
00061               return 0;
00062        }
00063 
00064        if (sock->timeout.tv_sec == -1)
00065               ptimeout = NULL;
00066        else
00067               ptimeout = &sock->timeout;
00068 
00069 retry:
00070        didwrite = send(sock->socket, buf, count, (sock->is_blocked && ptimeout) ? MSG_DONTWAIT : 0);
00071 
00072        if (didwrite <= 0) {
00073               long err = php_socket_errno();
00074               char *estr;
00075 
00076               if (sock->is_blocked && err == EWOULDBLOCK) {
00077                      int retval;
00078 
00079                      sock->timeout_event = 0;
00080 
00081                      do {
00082                             retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout);
00083 
00084                             if (retval == 0) {
00085                                    sock->timeout_event = 1;
00086                                    break;
00087                             }
00088 
00089                             if (retval > 0) {
00090                                    /* writable now; retry */
00091                                    goto retry;
00092                             }
00093 
00094                             err = php_socket_errno();
00095                      } while (err == EINTR);
00096               }
00097               estr = php_socket_strerror(err, NULL, 0);
00098               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "send of %ld bytes failed with errno=%ld %s",
00099                             (long)count, err, estr);
00100               efree(estr);
00101        }
00102 
00103        if (didwrite > 0) {
00104               php_stream_notify_progress_increment(stream->context, didwrite, 0);
00105        }
00106 
00107        if (didwrite < 0) {
00108               didwrite = 0;
00109        }
00110 
00111        return didwrite;
00112 }
00113 
00114 static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock TSRMLS_DC)
00115 {
00116        int retval;
00117        struct timeval *ptimeout;
00118 
00119        if (sock->socket == -1) {
00120               return;
00121        }
00122        
00123        sock->timeout_event = 0;
00124 
00125        if (sock->timeout.tv_sec == -1)
00126               ptimeout = NULL;
00127        else
00128               ptimeout = &sock->timeout;
00129 
00130        while(1) {
00131               retval = php_pollfd_for(sock->socket, PHP_POLLREADABLE, ptimeout);
00132 
00133               if (retval == 0)
00134                      sock->timeout_event = 1;
00135 
00136               if (retval >= 0)
00137                      break;
00138 
00139               if (php_socket_errno() != EINTR)
00140                      break;
00141        }
00142 }
00143 
00144 static size_t php_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
00145 {
00146        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
00147        int nr_bytes = 0;
00148 
00149        if (sock->socket == -1) {
00150               return 0;
00151        }
00152 
00153        if (sock->is_blocked) {
00154               php_sock_stream_wait_for_data(stream, sock TSRMLS_CC);
00155               if (sock->timeout_event)
00156                      return 0;
00157        }
00158 
00159        nr_bytes = recv(sock->socket, buf, count, (sock->is_blocked && sock->timeout.tv_sec != -1) ? MSG_DONTWAIT : 0);
00160 
00161        stream->eof = (nr_bytes == 0 || (nr_bytes == -1 && php_socket_errno() != EWOULDBLOCK));
00162 
00163        if (nr_bytes > 0) {
00164               php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
00165        }
00166 
00167        if (nr_bytes < 0) {
00168               nr_bytes = 0;
00169        }
00170 
00171        return nr_bytes;
00172 }
00173 
00174 
00175 static int php_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
00176 {
00177        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
00178 #ifdef PHP_WIN32
00179        int n;
00180 #endif
00181 
00182        if (close_handle) {
00183 
00184 #ifdef PHP_WIN32
00185               if (sock->socket == -1)
00186                      sock->socket = SOCK_ERR;
00187 #endif
00188               if (sock->socket != SOCK_ERR) {
00189 #ifdef PHP_WIN32
00190                      /* prevent more data from coming in */
00191                      shutdown(sock->socket, SHUT_RD);
00192 
00193                      /* try to make sure that the OS sends all data before we close the connection.
00194                       * Essentially, we are waiting for the socket to become writeable, which means
00195                       * that all pending data has been sent.
00196                       * We use a small timeout which should encourage the OS to send the data,
00197                       * but at the same time avoid hanging indefintely.
00198                       * */
00199                      do {
00200                             n = php_pollfd_for_ms(sock->socket, POLLOUT, 500);
00201                      } while (n == -1 && php_socket_errno() == EINTR);
00202 #endif
00203                      closesocket(sock->socket);
00204                      sock->socket = SOCK_ERR;
00205               }
00206 
00207        }
00208 
00209        pefree(sock, php_stream_is_persistent(stream));
00210        
00211        return 0;
00212 }
00213 
00214 static int php_sockop_flush(php_stream *stream TSRMLS_DC)
00215 {
00216 #if 0
00217        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
00218        return fsync(sock->socket);
00219 #endif
00220        return 0;
00221 }
00222 
00223 static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
00224 {
00225        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
00226 #if ZEND_WIN32
00227        return 0;
00228 #else
00229        return fstat(sock->socket, &ssb->sb);
00230 #endif
00231 }
00232 
00233 static inline int sock_sendto(php_netstream_data_t *sock, char *buf, size_t buflen, int flags,
00234               struct sockaddr *addr, socklen_t addrlen
00235               TSRMLS_DC)
00236 {
00237        int ret;
00238        if (addr) {
00239               ret = sendto(sock->socket, buf, buflen, flags, addr, addrlen);
00240               return (ret == SOCK_CONN_ERR) ? -1 : ret;
00241        }
00242        return ((ret = send(sock->socket, buf, buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
00243 }
00244 
00245 static inline int sock_recvfrom(php_netstream_data_t *sock, char *buf, size_t buflen, int flags,
00246               char **textaddr, long *textaddrlen,
00247               struct sockaddr **addr, socklen_t *addrlen
00248               TSRMLS_DC)
00249 {
00250        php_sockaddr_storage sa;
00251        socklen_t sl = sizeof(sa);
00252        int ret;
00253        int want_addr = textaddr || addr;
00254 
00255        if (want_addr) {
00256               ret = recvfrom(sock->socket, buf, buflen, flags, (struct sockaddr*)&sa, &sl);
00257               ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
00258               php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
00259                      textaddr, textaddrlen, addr, addrlen TSRMLS_CC);
00260        } else {
00261               ret = recv(sock->socket, buf, buflen, flags);
00262               ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
00263        }
00264 
00265        return ret;
00266 }
00267 
00268 static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
00269 {
00270        int oldmode, flags;
00271        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
00272        php_stream_xport_param *xparam;
00273        
00274        switch(option) {
00275               case PHP_STREAM_OPTION_CHECK_LIVENESS:
00276                      {
00277                             struct timeval tv;
00278                             char buf;
00279                             int alive = 1;
00280 
00281                             if (value == -1) {
00282                                    if (sock->timeout.tv_sec == -1) {
00283                                           tv.tv_sec = FG(default_socket_timeout);
00284                                           tv.tv_usec = 0;
00285                                    } else {
00286                                           tv = sock->timeout;
00287                                    }
00288                             } else {
00289                                    tv.tv_sec = value;
00290                                    tv.tv_usec = 0;
00291                             }
00292 
00293                             if (sock->socket == -1) {
00294                                    alive = 0;
00295                             } else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
00296                                    if (0 >= recv(sock->socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EWOULDBLOCK) {
00297                                           alive = 0;
00298                                    }
00299                             }
00300                             return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
00301                      }
00302                      
00303               case PHP_STREAM_OPTION_BLOCKING:
00304                      oldmode = sock->is_blocked;
00305                      if (SUCCESS == php_set_sock_blocking(sock->socket, value TSRMLS_CC)) {
00306                             sock->is_blocked = value;
00307                             return oldmode;
00308                      }
00309                      return PHP_STREAM_OPTION_RETURN_ERR;
00310 
00311               case PHP_STREAM_OPTION_READ_TIMEOUT:
00312                      sock->timeout = *(struct timeval*)ptrparam;
00313                      sock->timeout_event = 0;
00314                      return PHP_STREAM_OPTION_RETURN_OK;
00315 
00316               case PHP_STREAM_OPTION_META_DATA_API:
00317                      add_assoc_bool((zval *)ptrparam, "timed_out", sock->timeout_event);
00318                      add_assoc_bool((zval *)ptrparam, "blocked", sock->is_blocked);
00319                      add_assoc_bool((zval *)ptrparam, "eof", stream->eof);
00320                      return PHP_STREAM_OPTION_RETURN_OK;
00321               
00322               case PHP_STREAM_OPTION_XPORT_API:
00323                      xparam = (php_stream_xport_param *)ptrparam;
00324 
00325                      switch (xparam->op) {
00326                             case STREAM_XPORT_OP_LISTEN:
00327                                    xparam->outputs.returncode = (listen(sock->socket, xparam->inputs.backlog) == 0) ?  0: -1;
00328                                    return PHP_STREAM_OPTION_RETURN_OK;
00329 
00330                             case STREAM_XPORT_OP_GET_NAME:
00331                                    xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
00332                                                  xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
00333                                                  xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
00334                                                  xparam->want_addr ? &xparam->outputs.addr : NULL,
00335                                                  xparam->want_addr ? &xparam->outputs.addrlen : NULL
00336                                                  TSRMLS_CC);
00337                                    return PHP_STREAM_OPTION_RETURN_OK;
00338 
00339                             case STREAM_XPORT_OP_GET_PEER_NAME:
00340                                    xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
00341                                                  xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
00342                                                  xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
00343                                                  xparam->want_addr ? &xparam->outputs.addr : NULL,
00344                                                  xparam->want_addr ? &xparam->outputs.addrlen : NULL
00345                                                  TSRMLS_CC);
00346                                    return PHP_STREAM_OPTION_RETURN_OK;
00347 
00348                             case STREAM_XPORT_OP_SEND:
00349                                    flags = 0;
00350                                    if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
00351                                           flags |= MSG_OOB;
00352                                    }
00353                                    xparam->outputs.returncode = sock_sendto(sock,
00354                                                  xparam->inputs.buf, xparam->inputs.buflen,
00355                                                  flags,
00356                                                  xparam->inputs.addr,
00357                                                  xparam->inputs.addrlen TSRMLS_CC);
00358                                    if (xparam->outputs.returncode == -1) {
00359                                           char *err = php_socket_strerror(php_socket_errno(), NULL, 0);
00360                                           php_error_docref(NULL TSRMLS_CC, E_WARNING,
00361                                                  "%s\n", err);
00362                                           efree(err);
00363                                    }
00364                                    return PHP_STREAM_OPTION_RETURN_OK;
00365 
00366                             case STREAM_XPORT_OP_RECV:
00367                                    flags = 0;
00368                                    if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
00369                                           flags |= MSG_OOB;
00370                                    }
00371                                    if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) {
00372                                           flags |= MSG_PEEK;
00373                                    }
00374                                    xparam->outputs.returncode = sock_recvfrom(sock,
00375                                                  xparam->inputs.buf, xparam->inputs.buflen,
00376                                                  flags,
00377                                                  xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
00378                                                  xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
00379                                                  xparam->want_addr ? &xparam->outputs.addr : NULL,
00380                                                  xparam->want_addr ? &xparam->outputs.addrlen : NULL
00381                                                  TSRMLS_CC);
00382                                    return PHP_STREAM_OPTION_RETURN_OK;
00383 
00384 
00385 #ifdef HAVE_SHUTDOWN
00386 # ifndef SHUT_RD
00387 #  define SHUT_RD 0
00388 # endif
00389 # ifndef SHUT_WR
00390 #  define SHUT_WR 1
00391 # endif
00392 # ifndef SHUT_RDWR
00393 #  define SHUT_RDWR 2
00394 # endif
00395                             case STREAM_XPORT_OP_SHUTDOWN: {
00396                                    static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR};
00397 
00398                                    xparam->outputs.returncode = shutdown(sock->socket, shutdown_how[xparam->how]);
00399                                    return PHP_STREAM_OPTION_RETURN_OK;
00400                             }
00401 #endif
00402                             
00403                             case PHP_STREAM_OPTION_WRITE_BUFFER:
00404                                    php_stream_set_chunk_size(stream, (ptrparam ? *(size_t *)ptrparam : PHP_SOCK_CHUNK_SIZE));
00405                                    return PHP_STREAM_OPTION_RETURN_OK;
00406 
00407                             default:
00408                                    return PHP_STREAM_OPTION_RETURN_NOTIMPL;
00409                      }
00410 
00411               default:
00412                      return PHP_STREAM_OPTION_RETURN_NOTIMPL;
00413        }
00414 }
00415 
00416 static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
00417 {
00418        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
00419 
00420        switch(castas)       {
00421               case PHP_STREAM_AS_STDIO:
00422                      if (ret)      {
00423                             *(FILE**)ret = fdopen(sock->socket, stream->mode);
00424                             if (*ret)
00425                                    return SUCCESS;
00426                             return FAILURE;
00427                      }
00428                      return SUCCESS;
00429               case PHP_STREAM_AS_FD_FOR_SELECT:
00430               case PHP_STREAM_AS_FD:
00431               case PHP_STREAM_AS_SOCKETD:
00432                      if (ret)
00433                             *(int*)ret = sock->socket;
00434                      return SUCCESS;
00435               default:
00436                      return FAILURE;
00437        }
00438 }
00439 /* }}} */
00440 
00441 /* These may look identical, but we need them this way so that
00442  * we can determine which type of socket we are dealing with
00443  * by inspecting stream->ops.
00444  * A "useful" side-effect is that the user's scripts can then
00445  * make similar decisions using stream_get_meta_data.
00446  * */
00447 php_stream_ops php_stream_generic_socket_ops = {
00448        php_sockop_write, php_sockop_read,
00449        php_sockop_close, php_sockop_flush,
00450        "generic_socket",
00451        NULL, /* seek */
00452        php_sockop_cast,
00453        php_sockop_stat,
00454        php_sockop_set_option,
00455 };
00456 
00457 
00458 php_stream_ops php_stream_socket_ops = {
00459        php_sockop_write, php_sockop_read,
00460        php_sockop_close, php_sockop_flush,
00461        "tcp_socket",
00462        NULL, /* seek */
00463        php_sockop_cast,
00464        php_sockop_stat,
00465        php_tcp_sockop_set_option,
00466 };
00467 
00468 php_stream_ops php_stream_udp_socket_ops = {
00469        php_sockop_write, php_sockop_read,
00470        php_sockop_close, php_sockop_flush,
00471        "udp_socket",
00472        NULL, /* seek */
00473        php_sockop_cast,
00474        php_sockop_stat,
00475        php_tcp_sockop_set_option,
00476 };
00477 
00478 #ifdef AF_UNIX
00479 php_stream_ops php_stream_unix_socket_ops = {
00480        php_sockop_write, php_sockop_read,
00481        php_sockop_close, php_sockop_flush,
00482        "unix_socket",
00483        NULL, /* seek */
00484        php_sockop_cast,
00485        php_sockop_stat,
00486        php_tcp_sockop_set_option,
00487 };
00488 php_stream_ops php_stream_unixdg_socket_ops = {
00489        php_sockop_write, php_sockop_read,
00490        php_sockop_close, php_sockop_flush,
00491        "udg_socket",
00492        NULL, /* seek */
00493        php_sockop_cast,
00494        php_sockop_stat,
00495        php_tcp_sockop_set_option,
00496 };
00497 #endif
00498 
00499 
00500 /* network socket operations */
00501 
00502 #ifdef AF_UNIX
00503 static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr TSRMLS_DC)
00504 {
00505        memset(unix_addr, 0, sizeof(*unix_addr));
00506        unix_addr->sun_family = AF_UNIX;
00507 
00508        /* we need to be binary safe on systems that support an abstract
00509         * namespace */
00510        if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
00511               /* On linux, when the path begins with a NUL byte we are
00512                * referring to an abstract namespace.  In theory we should
00513                * allow an extra byte below, since we don't need the NULL.
00514                * BUT, to get into this branch of code, the name is too long,
00515                * so we don't care. */
00516               xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
00517        }
00518 
00519        memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
00520 
00521        return 1;
00522 }
00523 #endif
00524 
00525 static inline char *parse_ip_address_ex(const char *str, int str_len, int *portno, int get_err, char **err TSRMLS_DC)
00526 {
00527        char *colon;
00528        char *host = NULL;
00529 
00530 #ifdef HAVE_IPV6
00531        char *p;
00532 
00533        if (*(str) == '[' && str_len > 1) {
00534               /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
00535               p = memchr(str + 1, ']', str_len - 2);
00536               if (!p || *(p + 1) != ':') {
00537                      if (get_err) {
00538                             spprintf(err, 0, "Failed to parse IPv6 address \"%s\"", str);
00539                      }
00540                      return NULL;
00541               }
00542               *portno = atoi(p + 2);
00543               return estrndup(str + 1, p - str - 1);
00544        }
00545 #endif
00546        if (str_len) {
00547               colon = memchr(str, ':', str_len - 1);
00548        } else {
00549               colon = NULL;
00550        }
00551        if (colon) {
00552               *portno = atoi(colon + 1);
00553               host = estrndup(str, colon - str);
00554        } else {
00555               if (get_err) {
00556                      spprintf(err, 0, "Failed to parse address \"%s\"", str);
00557               }
00558               return NULL;
00559        }
00560 
00561        return host;
00562 }
00563 
00564 static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
00565 {
00566        return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
00567 }
00568 
00569 static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
00570               php_stream_xport_param *xparam TSRMLS_DC)
00571 {
00572        char *host = NULL;
00573        int portno, err;
00574 
00575 #ifdef AF_UNIX
00576        if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
00577               struct sockaddr_un unix_addr;
00578 
00579               sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
00580 
00581               if (sock->socket == SOCK_ERR) {
00582                      if (xparam->want_errortext) {
00583                             spprintf(&xparam->outputs.error_text, 0, "Failed to create unix%s socket %s",
00584                                           stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
00585                                           strerror(errno));
00586                      }
00587                      return -1;
00588               }
00589 
00590               parse_unix_address(xparam, &unix_addr TSRMLS_CC);
00591 
00592               return bind(sock->socket, (struct sockaddr *)&unix_addr, sizeof(unix_addr));
00593        }
00594 #endif
00595 
00596        host = parse_ip_address(xparam, &portno TSRMLS_CC);
00597 
00598        if (host == NULL) {
00599               return -1;
00600        }
00601 
00602        sock->socket = php_network_bind_socket_to_local_addr(host, portno,
00603                      stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
00604                      xparam->want_errortext ? &xparam->outputs.error_text : NULL,
00605                      &err
00606                      TSRMLS_CC);
00607        
00608        if (host) {
00609               efree(host);
00610        }
00611 
00612        return sock->socket == -1 ? -1 : 0;
00613 }
00614 
00615 static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
00616               php_stream_xport_param *xparam TSRMLS_DC)
00617 {
00618        char *host = NULL, *bindto = NULL;
00619        int portno, bindport = 0;
00620        int err = 0;
00621        int ret;
00622        zval **tmpzval = NULL;
00623 
00624 #ifdef AF_UNIX
00625        if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
00626               struct sockaddr_un unix_addr;
00627 
00628               sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
00629 
00630               if (sock->socket == SOCK_ERR) {
00631                      if (xparam->want_errortext) {
00632                             spprintf(&xparam->outputs.error_text, 0, "Failed to create unix socket");
00633                      }
00634                      return -1;
00635               }
00636 
00637               parse_unix_address(xparam, &unix_addr TSRMLS_CC);
00638 
00639               ret = php_network_connect_socket(sock->socket,
00640                             (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen,
00641                             xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
00642                             xparam->want_errortext ? &xparam->outputs.error_text : NULL,
00643                             &err);
00644 
00645               xparam->outputs.error_code = err;
00646 
00647               goto out;
00648        }
00649 #endif
00650 
00651        host = parse_ip_address(xparam, &portno TSRMLS_CC);
00652 
00653        if (host == NULL) {
00654               return -1;
00655        }
00656 
00657        if (stream->context && php_stream_context_get_option(stream->context, "socket", "bindto", &tmpzval) == SUCCESS) {
00658               if (Z_TYPE_PP(tmpzval) != IS_STRING) {
00659                      if (xparam->want_errortext) {
00660                             spprintf(&xparam->outputs.error_text, 0, "local_addr context option is not a string.");
00661                      }
00662                      efree(host);
00663                      return -1;
00664               }
00665               bindto = parse_ip_address_ex(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
00666        }
00667 
00668        /* Note: the test here for php_stream_udp_socket_ops is important, because we
00669         * want the default to be TCP sockets so that the openssl extension can
00670         * re-use this code. */
00671        
00672        sock->socket = php_network_connect_socket_to_host(host, portno,
00673                      stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
00674                      xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
00675                      xparam->inputs.timeout,
00676                      xparam->want_errortext ? &xparam->outputs.error_text : NULL,
00677                      &err,
00678                      bindto,
00679                      bindport
00680                      TSRMLS_CC);
00681        
00682        ret = sock->socket == -1 ? -1 : 0;
00683        xparam->outputs.error_code = err;
00684 
00685        if (host) {
00686               efree(host);
00687        }
00688        if (bindto) {
00689               efree(bindto);
00690        }
00691 
00692 #ifdef AF_UNIX
00693 out:
00694 #endif
00695 
00696        if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
00697               /* indicates pending connection */
00698               return 1;
00699        }
00700        
00701        return ret;
00702 }
00703 
00704 static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
00705               php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
00706 {
00707        int clisock;
00708 
00709        xparam->outputs.client = NULL;
00710 
00711        clisock = php_network_accept_incoming(sock->socket,
00712                      xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
00713                      xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
00714                      xparam->want_addr ? &xparam->outputs.addr : NULL,
00715                      xparam->want_addr ? &xparam->outputs.addrlen : NULL,
00716                      xparam->inputs.timeout,
00717                      xparam->want_errortext ? &xparam->outputs.error_text : NULL,
00718                      &xparam->outputs.error_code
00719                      TSRMLS_CC);
00720 
00721        if (clisock >= 0) {
00722               php_netstream_data_t *clisockdata;
00723 
00724               clisockdata = emalloc(sizeof(*clisockdata));
00725 
00726               if (clisockdata == NULL) {
00727                      close(clisock);
00728                      /* technically a fatal error */
00729               } else {
00730                      memcpy(clisockdata, sock, sizeof(*clisockdata));
00731                      clisockdata->socket = clisock;
00732 
00733                      xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
00734                      if (xparam->outputs.client) {
00735                             xparam->outputs.client->context = stream->context;
00736                             if (stream->context) {
00737                                    zend_list_addref(stream->context->rsrc_id);
00738                             }
00739                      }
00740               }
00741        }
00742        
00743        return xparam->outputs.client == NULL ? -1 : 0;
00744 }
00745 
00746 static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
00747 {
00748        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
00749        php_stream_xport_param *xparam;
00750 
00751        switch(option) {
00752               case PHP_STREAM_OPTION_XPORT_API:
00753                      xparam = (php_stream_xport_param *)ptrparam;
00754 
00755                      switch(xparam->op) {
00756                             case STREAM_XPORT_OP_CONNECT:
00757                             case STREAM_XPORT_OP_CONNECT_ASYNC:
00758                                    xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam TSRMLS_CC);
00759                                    return PHP_STREAM_OPTION_RETURN_OK;
00760 
00761                             case STREAM_XPORT_OP_BIND:
00762                                    xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam TSRMLS_CC);
00763                                    return PHP_STREAM_OPTION_RETURN_OK;
00764 
00765 
00766                             case STREAM_XPORT_OP_ACCEPT:
00767                                    xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC TSRMLS_CC);
00768                                    return PHP_STREAM_OPTION_RETURN_OK;
00769                             default:
00770                                    /* fall through */
00771                                    ;
00772                      }
00773        }
00774        return php_sockop_set_option(stream, option, value, ptrparam TSRMLS_CC);
00775 }
00776 
00777 
00778 PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, long protolen,
00779               char *resourcename, long resourcenamelen,
00780               const char *persistent_id, int options, int flags,
00781               struct timeval *timeout,
00782               php_stream_context *context STREAMS_DC TSRMLS_DC)
00783 {
00784        php_stream *stream = NULL;
00785        php_netstream_data_t *sock;
00786        php_stream_ops *ops;
00787 
00788        /* which type of socket ? */
00789        if (strncmp(proto, "tcp", protolen) == 0) {
00790               ops = &php_stream_socket_ops;
00791        } else if (strncmp(proto, "udp", protolen) == 0) {
00792               ops = &php_stream_udp_socket_ops;
00793        }
00794 #ifdef AF_UNIX
00795        else if (strncmp(proto, "unix", protolen) == 0) {
00796               ops = &php_stream_unix_socket_ops;
00797        } else if (strncmp(proto, "udg", protolen) == 0) {
00798               ops = &php_stream_unixdg_socket_ops;
00799        }
00800 #endif
00801        else {
00802               /* should never happen */
00803               return NULL;
00804        }
00805        
00806        sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
00807        memset(sock, 0, sizeof(php_netstream_data_t));
00808 
00809        sock->is_blocked = 1;
00810        sock->timeout.tv_sec = FG(default_socket_timeout);
00811        sock->timeout.tv_usec = 0;
00812 
00813        /* we don't know the socket until we have determined if we are binding or
00814         * connecting */
00815        sock->socket = -1;
00816        
00817        stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
00818 
00819        if (stream == NULL)  {
00820               pefree(sock, persistent_id ? 1 : 0);
00821               return NULL;
00822        }
00823 
00824        if (flags == 0) {
00825               return stream;
00826        }
00827 
00828        return stream;
00829 }
00830 
00831 
00832 /*
00833  * Local variables:
00834  * tab-width: 4
00835  * c-basic-offset: 4
00836  * End:
00837  * vim600: noet sw=4 ts=4 fdm=marker
00838  * vim<600: noet sw=4 ts=4
00839  */