Back to index

php5  5.3.10
transports.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: transports.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #include "php.h"
00022 #include "php_streams_int.h"
00023 #include "ext/standard/file.h"
00024 
00025 static HashTable xport_hash;
00026 
00027 PHPAPI HashTable *php_stream_xport_get_hash(void)
00028 {
00029        return &xport_hash;
00030 }
00031 
00032 PHPAPI int php_stream_xport_register(char *protocol, php_stream_transport_factory factory TSRMLS_DC)
00033 {
00034        return zend_hash_update(&xport_hash, protocol, strlen(protocol) + 1, &factory, sizeof(factory), NULL);
00035 }
00036 
00037 PHPAPI int php_stream_xport_unregister(char *protocol TSRMLS_DC)
00038 {
00039        return zend_hash_del(&xport_hash, protocol, strlen(protocol) + 1);
00040 }
00041 
00042 #define ERR_REPORT(out_err, fmt, arg) \
00043        if (out_err) { spprintf(out_err, 0, fmt, arg); } \
00044        else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, arg); }
00045 
00046 #define ERR_RETURN(out_err, local_err, fmt) \
00047        if (out_err) { *out_err = local_err; } \
00048        else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, local_err ? local_err : "Unspecified error"); \
00049               if (local_err) { efree(local_err); local_err = NULL; } \
00050        }
00051        
00052 PHPAPI php_stream *_php_stream_xport_create(const char *name, long namelen, int options,
00053               int flags, const char *persistent_id,
00054               struct timeval *timeout,
00055               php_stream_context *context,
00056               char **error_string,
00057               int *error_code
00058               STREAMS_DC TSRMLS_DC)
00059 {
00060        php_stream *stream = NULL;
00061        php_stream_transport_factory *factory = NULL;
00062        const char *p, *protocol = NULL;
00063        int n = 0, failed = 0;
00064        char *error_text = NULL;
00065        struct timeval default_timeout = { 0, 0 };
00066        
00067        default_timeout.tv_sec = FG(default_socket_timeout);
00068 
00069        if (timeout == NULL) {
00070               timeout = &default_timeout;
00071        }
00072        
00073        /* check for a cached persistent socket */
00074        if (persistent_id) {
00075               switch(php_stream_from_persistent_id(persistent_id, &stream TSRMLS_CC)) {
00076                      case PHP_STREAM_PERSISTENT_SUCCESS:
00077                             /* use a 0 second timeout when checking if the socket
00078                              * has already died */
00079                             if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL)) {
00080                                    return stream;
00081                             }
00082                             /* dead - kill it */
00083                             php_stream_pclose(stream);
00084                             stream = NULL;
00085 
00086                             /* fall through */
00087 
00088                      case PHP_STREAM_PERSISTENT_FAILURE:
00089                      default:
00090                             /* failed; get a new one */
00091                             ;
00092               }
00093        }
00094 
00095        for (p = name; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
00096               n++;
00097        }
00098 
00099        if ((*p == ':') && (n > 1) && !strncmp("://", p, 3)) {
00100               protocol = name;
00101               name = p + 3;
00102               namelen -= n + 3;
00103        } else {
00104               protocol = "tcp";
00105               n = 3;
00106        }
00107 
00108        if (protocol) {
00109               char *tmp = estrndup(protocol, n);
00110               if (FAILURE == zend_hash_find(&xport_hash, (char*)tmp, n + 1, (void**)&factory)) {
00111                      char wrapper_name[32];
00112 
00113                      if (n >= sizeof(wrapper_name))
00114                             n = sizeof(wrapper_name) - 1;
00115                      PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
00116               
00117                      ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?",
00118                                    wrapper_name);
00119 
00120                      efree(tmp);
00121                      return NULL;
00122               }
00123               efree(tmp);
00124        }
00125 
00126        if (factory == NULL) {
00127               /* should never happen */
00128               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find a factory !?");
00129               return NULL;
00130        }
00131 
00132        stream = (*factory)(protocol, n,
00133                      (char*)name, namelen, persistent_id, options, flags, timeout,
00134                      context STREAMS_REL_CC TSRMLS_CC);
00135 
00136        if (stream) {
00137               php_stream_context_set(stream, context);
00138 
00139               if ((flags & STREAM_XPORT_SERVER) == 0) {
00140                      /* client */
00141 
00142                      if (flags & (STREAM_XPORT_CONNECT|STREAM_XPORT_CONNECT_ASYNC)) {
00143                             if (-1 == php_stream_xport_connect(stream, name, namelen,
00144                                                  flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0,
00145                                                  timeout, &error_text, error_code TSRMLS_CC)) {
00146 
00147                                    ERR_RETURN(error_string, error_text, "connect() failed: %s");
00148 
00149                                    failed = 1;
00150                             }
00151                      }
00152 
00153               } else {
00154                      /* server */
00155                      if (flags & STREAM_XPORT_BIND) {
00156                             if (0 != php_stream_xport_bind(stream, name, namelen, &error_text TSRMLS_CC)) {
00157                                    ERR_RETURN(error_string, error_text, "bind() failed: %s");
00158                                    failed = 1;
00159                             } else if (flags & STREAM_XPORT_LISTEN) {
00160                                    zval **zbacklog = NULL;
00161                                    int backlog = 32;
00162                                    
00163                                    if (stream->context && php_stream_context_get_option(stream->context, "socket", "backlog", &zbacklog) == SUCCESS) {
00164                                           zval *ztmp = *zbacklog;
00165                                           
00166                                           convert_to_long_ex(&ztmp);
00167                                           backlog = Z_LVAL_P(ztmp);
00168                                           if (ztmp != *zbacklog) {
00169                                                  zval_ptr_dtor(&ztmp);
00170                                           }
00171                                    }
00172                                    
00173                                    if (0 != php_stream_xport_listen(stream, backlog, &error_text TSRMLS_CC)) {
00174                                           ERR_RETURN(error_string, error_text, "listen() failed: %s");
00175                                           failed = 1;
00176                                    }
00177                             }
00178                      }
00179               }
00180        }
00181 
00182        if (failed) {
00183               /* failure means that they don't get a stream to play with */
00184               if (persistent_id) {
00185                      php_stream_pclose(stream);
00186               } else {
00187                      php_stream_close(stream);
00188               }
00189               stream = NULL;
00190        }
00191 
00192        return stream;
00193 }
00194 
00195 /* Bind the stream to a local address */
00196 PHPAPI int php_stream_xport_bind(php_stream *stream,
00197               const char *name, long namelen,
00198               char **error_text
00199               TSRMLS_DC)
00200 {
00201        php_stream_xport_param param;
00202        int ret;
00203        
00204        memset(&param, 0, sizeof(param));
00205        param.op = STREAM_XPORT_OP_BIND;
00206        param.inputs.name = (char*)name;
00207        param.inputs.namelen = namelen;
00208        param.want_errortext = error_text ? 1 : 0;
00209 
00210        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
00211 
00212        if (ret == PHP_STREAM_OPTION_RETURN_OK) {
00213               if (error_text) {
00214                      *error_text = param.outputs.error_text;
00215               }
00216 
00217               return param.outputs.returncode;
00218        }
00219 
00220        return ret;
00221 }
00222 
00223 /* Connect to a remote address */
00224 PHPAPI int php_stream_xport_connect(php_stream *stream,
00225               const char *name, long namelen,
00226               int asynchronous,
00227               struct timeval *timeout,
00228               char **error_text,
00229               int *error_code
00230               TSRMLS_DC)
00231 {
00232        php_stream_xport_param param;
00233        int ret;
00234        
00235        memset(&param, 0, sizeof(param));
00236        param.op = asynchronous ? STREAM_XPORT_OP_CONNECT_ASYNC: STREAM_XPORT_OP_CONNECT;
00237        param.inputs.name = (char*)name;
00238        param.inputs.namelen = namelen;
00239        param.inputs.timeout = timeout;
00240 
00241        param.want_errortext = error_text ? 1 : 0;
00242        
00243        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
00244 
00245        if (ret == PHP_STREAM_OPTION_RETURN_OK) {
00246               if (error_text) {
00247                      *error_text = param.outputs.error_text;
00248               }
00249               if (error_code) {
00250                      *error_code = param.outputs.error_code;
00251               }
00252               return param.outputs.returncode;
00253        }
00254 
00255        return ret;
00256 
00257 }
00258 
00259 /* Prepare to listen */
00260 PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, char **error_text TSRMLS_DC)
00261 {
00262        php_stream_xport_param param;
00263        int ret;
00264        
00265        memset(&param, 0, sizeof(param));
00266        param.op = STREAM_XPORT_OP_LISTEN;
00267        param.inputs.backlog = backlog;
00268        param.want_errortext = error_text ? 1 : 0;
00269        
00270        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
00271 
00272        if (ret == PHP_STREAM_OPTION_RETURN_OK) {
00273               if (error_text) {
00274                      *error_text = param.outputs.error_text;
00275               }
00276 
00277               return param.outputs.returncode;
00278        }
00279 
00280        return ret;
00281 }
00282 
00283 /* Get the next client and their address (as a string) */
00284 PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
00285               char **textaddr, int *textaddrlen,
00286               void **addr, socklen_t *addrlen,
00287               struct timeval *timeout,
00288               char **error_text
00289               TSRMLS_DC)
00290 {
00291        php_stream_xport_param param;
00292        int ret;
00293 
00294        memset(&param, 0, sizeof(param));
00295 
00296        param.op = STREAM_XPORT_OP_ACCEPT;
00297        param.inputs.timeout = timeout;
00298        param.want_addr = addr ? 1 : 0;
00299        param.want_textaddr = textaddr ? 1 : 0;
00300        param.want_errortext = error_text ? 1 : 0;
00301        
00302        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
00303 
00304        if (ret == PHP_STREAM_OPTION_RETURN_OK) {
00305               *client = param.outputs.client;
00306               if (addr) {
00307                      *addr = param.outputs.addr;
00308                      *addrlen = param.outputs.addrlen;
00309               }
00310               if (textaddr) {
00311                      *textaddr = param.outputs.textaddr;
00312                      *textaddrlen = param.outputs.textaddrlen;
00313               }
00314               if (error_text) {
00315                      *error_text = param.outputs.error_text;
00316               }
00317 
00318               return param.outputs.returncode;
00319        }
00320        return ret;
00321 }
00322 
00323 PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
00324               char **textaddr, int *textaddrlen,
00325               void **addr, socklen_t *addrlen
00326               TSRMLS_DC)
00327 {
00328        php_stream_xport_param param;
00329        int ret;
00330 
00331        memset(&param, 0, sizeof(param));
00332 
00333        param.op = want_peer ? STREAM_XPORT_OP_GET_PEER_NAME : STREAM_XPORT_OP_GET_NAME;
00334        param.want_addr = addr ? 1 : 0;
00335        param.want_textaddr = textaddr ? 1 : 0;
00336        
00337        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
00338 
00339        if (ret == PHP_STREAM_OPTION_RETURN_OK) {
00340               if (addr) {
00341                      *addr = param.outputs.addr;
00342                      *addrlen = param.outputs.addrlen;
00343               }
00344               if (textaddr) {
00345                      *textaddr = param.outputs.textaddr;
00346                      *textaddrlen = param.outputs.textaddrlen;
00347               }
00348 
00349               return param.outputs.returncode;
00350        }
00351        return ret;
00352 }
00353 
00354 PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC)
00355 {
00356        php_stream_xport_crypto_param param;
00357        int ret;
00358 
00359        memset(&param, 0, sizeof(param));
00360        param.op = STREAM_XPORT_CRYPTO_OP_SETUP;
00361        param.inputs.method = crypto_method;
00362        param.inputs.session = session_stream;
00363        
00364        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);
00365 
00366        if (ret == PHP_STREAM_OPTION_RETURN_OK) {
00367               return param.outputs.returncode;
00368        }
00369 
00370        php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
00371        
00372        return ret;
00373 }
00374 
00375 PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate TSRMLS_DC)
00376 {
00377        php_stream_xport_crypto_param param;
00378        int ret;
00379 
00380        memset(&param, 0, sizeof(param));
00381        param.op = STREAM_XPORT_CRYPTO_OP_ENABLE;
00382        param.inputs.activate = activate;
00383        
00384        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);
00385 
00386        if (ret == PHP_STREAM_OPTION_RETURN_OK) {
00387               return param.outputs.returncode;
00388        }
00389 
00390        php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
00391        
00392        return ret;
00393 }
00394 
00395 /* Similar to recv() system call; read data from the stream, optionally
00396  * peeking, optionally retrieving OOB data */
00397 PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t buflen,
00398               long flags, void **addr, socklen_t *addrlen, char **textaddr, int *textaddrlen
00399               TSRMLS_DC)
00400 {
00401        php_stream_xport_param param;
00402        int ret = 0;
00403        int recvd_len = 0;
00404 #if 0
00405        int oob;
00406 
00407        if (flags == 0 && addr == NULL) {
00408               return php_stream_read(stream, buf, buflen);
00409        }
00410 
00411        if (stream->readfilters.head) {
00412               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot peek or fetch OOB data from a filtered stream");
00413               return -1;
00414        }
00415        
00416        oob = (flags & STREAM_OOB) == STREAM_OOB;
00417 
00418        if (!oob && addr == NULL) {
00419               /* must be peeking at regular data; copy content from the buffer
00420                * first, then adjust the pointer/len before handing off to the
00421                * stream */
00422               recvd_len = stream->writepos - stream->readpos;
00423               if (recvd_len > buflen) {
00424                      recvd_len = buflen;
00425               }
00426               if (recvd_len) {
00427                      memcpy(buf, stream->readbuf, recvd_len);
00428                      buf += recvd_len;
00429                      buflen -= recvd_len;
00430               }
00431               /* if we filled their buffer, return */
00432               if (buflen == 0) {
00433                      return recvd_len;
00434               }
00435        }
00436 #endif
00437 
00438        /* otherwise, we are going to bypass the buffer */
00439        
00440        memset(&param, 0, sizeof(param));
00441 
00442        param.op = STREAM_XPORT_OP_RECV;
00443        param.want_addr = addr ? 1 : 0;
00444        param.want_textaddr = textaddr ? 1 : 0;
00445        param.inputs.buf = buf;
00446        param.inputs.buflen = buflen;
00447        param.inputs.flags = flags;
00448        
00449        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
00450 
00451        if (ret == PHP_STREAM_OPTION_RETURN_OK) {
00452               if (addr) {
00453                      *addr = param.outputs.addr;
00454                      *addrlen = param.outputs.addrlen;
00455               }
00456               if (textaddr) {
00457                      *textaddr = param.outputs.textaddr;
00458                      *textaddrlen = param.outputs.textaddrlen;
00459               }
00460               return recvd_len + param.outputs.returncode;
00461        }
00462        return recvd_len ? recvd_len : -1;
00463 }
00464 
00465 /* Similar to send() system call; send data to the stream, optionally
00466  * sending it as OOB data */
00467 PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t buflen,
00468               long flags, void *addr, socklen_t addrlen TSRMLS_DC)
00469 {
00470        php_stream_xport_param param;
00471        int ret = 0;
00472        int oob;
00473 
00474 #if 0
00475        if (flags == 0 && addr == NULL) {
00476               return php_stream_write(stream, buf, buflen);
00477        }
00478 #endif
00479        
00480        oob = (flags & STREAM_OOB) == STREAM_OOB;
00481 
00482        if ((oob || addr) && stream->writefilters.head) {
00483               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot write OOB data, or data to a targeted address on a filtered stream");
00484               return -1;
00485        }
00486        
00487        memset(&param, 0, sizeof(param));
00488 
00489        param.op = STREAM_XPORT_OP_SEND;
00490        param.want_addr = addr ? 1 : 0;
00491        param.inputs.buf = (char*)buf;
00492        param.inputs.buflen = buflen;
00493        param.inputs.flags = flags;
00494        param.inputs.addr = addr;
00495        param.inputs.addrlen = addrlen;
00496        
00497        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
00498 
00499        if (ret == PHP_STREAM_OPTION_RETURN_OK) {
00500               return param.outputs.returncode;
00501        }
00502        return -1;
00503 }
00504 
00505 /* Similar to shutdown() system call; shut down part of a full-duplex
00506  * connection */
00507 PHPAPI int php_stream_xport_shutdown(php_stream *stream, stream_shutdown_t how TSRMLS_DC)
00508 {
00509        php_stream_xport_param param;
00510        int ret = 0;
00511 
00512        memset(&param, 0, sizeof(param));
00513 
00514        param.op = STREAM_XPORT_OP_SHUTDOWN;
00515        param.how = how;
00516        
00517        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
00518 
00519        if (ret == PHP_STREAM_OPTION_RETURN_OK) {
00520               return param.outputs.returncode;
00521        }
00522        return -1;
00523 }
00524 
00525 /*
00526  * Local variables:
00527  * tab-width: 4
00528  * c-basic-offset: 4
00529  * End:
00530  * vim600: noet sw=4 ts=4 fdm=marker
00531  * vim<600: noet sw=4 ts=4
00532  */