root/main/streams/transports.c

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

DEFINITIONS

This source file includes following definitions.
  1. php_stream_xport_get_hash
  2. php_stream_xport_register
  3. php_stream_xport_unregister
  4. _php_stream_xport_create
  5. php_stream_xport_bind
  6. php_stream_xport_connect
  7. php_stream_xport_listen
  8. php_stream_xport_accept
  9. php_stream_xport_get_name
  10. php_stream_xport_crypto_setup
  11. php_stream_xport_crypto_enable
  12. php_stream_xport_recvfrom
  13. php_stream_xport_sendto
  14. php_stream_xport_shutdown

   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 "php_streams_int.h"
  23 #include "ext/standard/file.h"
  24 
  25 static HashTable xport_hash;
  26 
  27 PHPAPI HashTable *php_stream_xport_get_hash(void)
  28 {
  29         return &xport_hash;
  30 }
  31 
  32 PHPAPI int php_stream_xport_register(const char *protocol, php_stream_transport_factory factory)
  33 {
  34         return zend_hash_str_update_ptr(&xport_hash, protocol, strlen(protocol), factory) ? SUCCESS : FAILURE;
  35 }
  36 
  37 PHPAPI int php_stream_xport_unregister(const char *protocol)
  38 {
  39         return zend_hash_str_del(&xport_hash, protocol, strlen(protocol));
  40 }
  41 
  42 #define ERR_REPORT(out_err, fmt, arg) \
  43         if (out_err) { *out_err = strpprintf(0, fmt, arg); } \
  44         else { php_error_docref(NULL, E_WARNING, fmt, arg); }
  45 
  46 #define ERR_RETURN(out_err, local_err, fmt) \
  47         if (out_err) { *out_err = local_err; } \
  48         else { php_error_docref(NULL, E_WARNING, fmt, local_err ? ZSTR_VAL(local_err) : "Unspecified error"); \
  49                 if (local_err) { zend_string_release(local_err); local_err = NULL; } \
  50         }
  51 
  52 PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, int options,
  53                 int flags, const char *persistent_id,
  54                 struct timeval *timeout,
  55                 php_stream_context *context,
  56                 zend_string **error_string,
  57                 int *error_code
  58                 STREAMS_DC)
  59 {
  60         php_stream *stream = NULL;
  61         php_stream_transport_factory factory = NULL;
  62         const char *p, *protocol = NULL;
  63         int n = 0, failed = 0;
  64         zend_string *error_text = NULL;
  65         struct timeval default_timeout = { 0, 0 };
  66 
  67         default_timeout.tv_sec = FG(default_socket_timeout);
  68 
  69         if (timeout == NULL) {
  70                 timeout = &default_timeout;
  71         }
  72 
  73         /* check for a cached persistent socket */
  74         if (persistent_id) {
  75                 switch(php_stream_from_persistent_id(persistent_id, &stream)) {
  76                         case PHP_STREAM_PERSISTENT_SUCCESS:
  77                                 /* use a 0 second timeout when checking if the socket
  78                                  * has already died */
  79                                 if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL)) {
  80                                         return stream;
  81                                 }
  82                                 /* dead - kill it */
  83                                 php_stream_pclose(stream);
  84                                 stream = NULL;
  85 
  86                                 /* fall through */
  87 
  88                         case PHP_STREAM_PERSISTENT_FAILURE:
  89                         default:
  90                                 /* failed; get a new one */
  91                                 ;
  92                 }
  93         }
  94 
  95         for (p = name; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
  96                 n++;
  97         }
  98 
  99         if ((*p == ':') && (n > 1) && !strncmp("://", p, 3)) {
 100                 protocol = name;
 101                 name = p + 3;
 102                 namelen -= n + 3;
 103         } else {
 104                 protocol = "tcp";
 105                 n = 3;
 106         }
 107 
 108         if (protocol) {
 109                 char *tmp = estrndup(protocol, n);
 110                 if (NULL == (factory = zend_hash_str_find_ptr(&xport_hash, tmp, n))) {
 111                         char wrapper_name[32];
 112 
 113                         if (n >= sizeof(wrapper_name))
 114                                 n = sizeof(wrapper_name) - 1;
 115                         PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
 116 
 117                         ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?",
 118                                         wrapper_name);
 119 
 120                         efree(tmp);
 121                         return NULL;
 122                 }
 123                 efree(tmp);
 124         }
 125 
 126         if (factory == NULL) {
 127                 /* should never happen */
 128                 php_error_docref(NULL, E_WARNING, "Could not find a factory !?");
 129                 return NULL;
 130         }
 131 
 132         stream = (factory)(protocol, n,
 133                         (char*)name, namelen, persistent_id, options, flags, timeout,
 134                         context STREAMS_REL_CC);
 135 
 136         if (stream) {
 137                 php_stream_context_set(stream, context);
 138 
 139                 if ((flags & STREAM_XPORT_SERVER) == 0) {
 140                         /* client */
 141 
 142                         if (flags & (STREAM_XPORT_CONNECT|STREAM_XPORT_CONNECT_ASYNC)) {
 143                                 if (-1 == php_stream_xport_connect(stream, name, namelen,
 144                                                         flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0,
 145                                                         timeout, &error_text, error_code)) {
 146 
 147                                         ERR_RETURN(error_string, error_text, "connect() failed: %s");
 148 
 149                                         failed = 1;
 150                                 }
 151                         }
 152 
 153                 } else {
 154                         /* server */
 155                         if (flags & STREAM_XPORT_BIND) {
 156                                 if (0 != php_stream_xport_bind(stream, name, namelen, &error_text)) {
 157                                         ERR_RETURN(error_string, error_text, "bind() failed: %s");
 158                                         failed = 1;
 159                                 } else if (flags & STREAM_XPORT_LISTEN) {
 160                                         zval *zbacklog = NULL;
 161                                         int backlog = 32;
 162 
 163                                         if (PHP_STREAM_CONTEXT(stream) && (zbacklog = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "backlog")) != NULL) {
 164                                                 zval *ztmp = zbacklog;
 165 
 166                                                 convert_to_long_ex(ztmp);
 167                                                 backlog = Z_LVAL_P(ztmp);
 168                                                 if (ztmp != zbacklog) {
 169                                                         zval_ptr_dtor(ztmp);
 170                                                 }
 171                                         }
 172 
 173                                         if (0 != php_stream_xport_listen(stream, backlog, &error_text)) {
 174                                                 ERR_RETURN(error_string, error_text, "listen() failed: %s");
 175                                                 failed = 1;
 176                                         }
 177                                 }
 178                         }
 179                 }
 180         }
 181 
 182         if (failed) {
 183                 /* failure means that they don't get a stream to play with */
 184                 if (persistent_id) {
 185                         php_stream_pclose(stream);
 186                 } else {
 187                         php_stream_close(stream);
 188                 }
 189                 stream = NULL;
 190         }
 191 
 192         return stream;
 193 }
 194 
 195 /* Bind the stream to a local address */
 196 PHPAPI int php_stream_xport_bind(php_stream *stream,
 197                 const char *name, size_t namelen,
 198                 zend_string **error_text
 199                 )
 200 {
 201         php_stream_xport_param param;
 202         int ret;
 203 
 204         memset(&param, 0, sizeof(param));
 205         param.op = STREAM_XPORT_OP_BIND;
 206         param.inputs.name = (char*)name;
 207         param.inputs.namelen = namelen;
 208         param.want_errortext = error_text ? 1 : 0;
 209 
 210         ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
 211 
 212         if (ret == PHP_STREAM_OPTION_RETURN_OK) {
 213                 if (error_text) {
 214                         *error_text = param.outputs.error_text;
 215                 }
 216 
 217                 return param.outputs.returncode;
 218         }
 219 
 220         return ret;
 221 }
 222 
 223 /* Connect to a remote address */
 224 PHPAPI int php_stream_xport_connect(php_stream *stream,
 225                 const char *name, size_t namelen,
 226                 int asynchronous,
 227                 struct timeval *timeout,
 228                 zend_string **error_text,
 229                 int *error_code
 230                 )
 231 {
 232         php_stream_xport_param param;
 233         int ret;
 234 
 235         memset(&param, 0, sizeof(param));
 236         param.op = asynchronous ? STREAM_XPORT_OP_CONNECT_ASYNC: STREAM_XPORT_OP_CONNECT;
 237         param.inputs.name = (char*)name;
 238         param.inputs.namelen = namelen;
 239         param.inputs.timeout = timeout;
 240 
 241         param.want_errortext = error_text ? 1 : 0;
 242 
 243         ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
 244 
 245         if (ret == PHP_STREAM_OPTION_RETURN_OK) {
 246                 if (error_text) {
 247                         *error_text = param.outputs.error_text;
 248                 }
 249                 if (error_code) {
 250                         *error_code = param.outputs.error_code;
 251                 }
 252                 return param.outputs.returncode;
 253         }
 254 
 255         return ret;
 256 
 257 }
 258 
 259 /* Prepare to listen */
 260 PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, zend_string **error_text)
 261 {
 262         php_stream_xport_param param;
 263         int ret;
 264 
 265         memset(&param, 0, sizeof(param));
 266         param.op = STREAM_XPORT_OP_LISTEN;
 267         param.inputs.backlog = backlog;
 268         param.want_errortext = error_text ? 1 : 0;
 269 
 270         ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
 271 
 272         if (ret == PHP_STREAM_OPTION_RETURN_OK) {
 273                 if (error_text) {
 274                         *error_text = param.outputs.error_text;
 275                 }
 276 
 277                 return param.outputs.returncode;
 278         }
 279 
 280         return ret;
 281 }
 282 
 283 /* Get the next client and their address (as a string) */
 284 PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
 285                 zend_string **textaddr,
 286                 void **addr, socklen_t *addrlen,
 287                 struct timeval *timeout,
 288                 zend_string **error_text
 289                 )
 290 {
 291         php_stream_xport_param param;
 292         int ret;
 293 
 294         memset(&param, 0, sizeof(param));
 295 
 296         param.op = STREAM_XPORT_OP_ACCEPT;
 297         param.inputs.timeout = timeout;
 298         param.want_addr = addr ? 1 : 0;
 299         param.want_textaddr = textaddr ? 1 : 0;
 300         param.want_errortext = error_text ? 1 : 0;
 301 
 302         ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
 303 
 304         if (ret == PHP_STREAM_OPTION_RETURN_OK) {
 305                 *client = param.outputs.client;
 306                 if (addr) {
 307                         *addr = param.outputs.addr;
 308                         *addrlen = param.outputs.addrlen;
 309                 }
 310                 if (textaddr) {
 311                         *textaddr = param.outputs.textaddr;
 312                 }
 313                 if (error_text) {
 314                         *error_text = param.outputs.error_text;
 315                 }
 316 
 317                 return param.outputs.returncode;
 318         }
 319         return ret;
 320 }
 321 
 322 PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
 323                 zend_string **textaddr,
 324                 void **addr, socklen_t *addrlen
 325                 )
 326 {
 327         php_stream_xport_param param;
 328         int ret;
 329 
 330         memset(&param, 0, sizeof(param));
 331 
 332         param.op = want_peer ? STREAM_XPORT_OP_GET_PEER_NAME : STREAM_XPORT_OP_GET_NAME;
 333         param.want_addr = addr ? 1 : 0;
 334         param.want_textaddr = textaddr ? 1 : 0;
 335 
 336         ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
 337 
 338         if (ret == PHP_STREAM_OPTION_RETURN_OK) {
 339                 if (addr) {
 340                         *addr = param.outputs.addr;
 341                         *addrlen = param.outputs.addrlen;
 342                 }
 343                 if (textaddr) {
 344                         *textaddr = param.outputs.textaddr;
 345                 }
 346 
 347                 return param.outputs.returncode;
 348         }
 349         return ret;
 350 }
 351 
 352 PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream)
 353 {
 354         php_stream_xport_crypto_param param;
 355         int ret;
 356 
 357         memset(&param, 0, sizeof(param));
 358         param.op = STREAM_XPORT_CRYPTO_OP_SETUP;
 359         param.inputs.method = crypto_method;
 360         param.inputs.session = session_stream;
 361 
 362         ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);
 363 
 364         if (ret == PHP_STREAM_OPTION_RETURN_OK) {
 365                 return param.outputs.returncode;
 366         }
 367 
 368         php_error_docref("streams.crypto", E_WARNING, "this stream does not support SSL/crypto");
 369 
 370         return ret;
 371 }
 372 
 373 PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate)
 374 {
 375         php_stream_xport_crypto_param param;
 376         int ret;
 377 
 378         memset(&param, 0, sizeof(param));
 379         param.op = STREAM_XPORT_CRYPTO_OP_ENABLE;
 380         param.inputs.activate = activate;
 381 
 382         ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);
 383 
 384         if (ret == PHP_STREAM_OPTION_RETURN_OK) {
 385                 return param.outputs.returncode;
 386         }
 387 
 388         php_error_docref("streams.crypto", E_WARNING, "this stream does not support SSL/crypto");
 389 
 390         return ret;
 391 }
 392 
 393 /* Similar to recv() system call; read data from the stream, optionally
 394  * peeking, optionally retrieving OOB data */
 395 PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t buflen,
 396                 int flags, void **addr, socklen_t *addrlen, zend_string **textaddr
 397                 )
 398 {
 399         php_stream_xport_param param;
 400         int ret = 0;
 401         int recvd_len = 0;
 402 #if 0
 403         int oob;
 404 
 405         if (flags == 0 && addr == NULL) {
 406                 return php_stream_read(stream, buf, buflen);
 407         }
 408 
 409         if (stream->readfilters.head) {
 410                 php_error_docref(NULL, E_WARNING, "cannot peek or fetch OOB data from a filtered stream");
 411                 return -1;
 412         }
 413 
 414         oob = (flags & STREAM_OOB) == STREAM_OOB;
 415 
 416         if (!oob && addr == NULL) {
 417                 /* must be peeking at regular data; copy content from the buffer
 418                  * first, then adjust the pointer/len before handing off to the
 419                  * stream */
 420                 recvd_len = stream->writepos - stream->readpos;
 421                 if (recvd_len > buflen) {
 422                         recvd_len = buflen;
 423                 }
 424                 if (recvd_len) {
 425                         memcpy(buf, stream->readbuf, recvd_len);
 426                         buf += recvd_len;
 427                         buflen -= recvd_len;
 428                 }
 429                 /* if we filled their buffer, return */
 430                 if (buflen == 0) {
 431                         return recvd_len;
 432                 }
 433         }
 434 #endif
 435 
 436         /* otherwise, we are going to bypass the buffer */
 437 
 438         memset(&param, 0, sizeof(param));
 439 
 440         param.op = STREAM_XPORT_OP_RECV;
 441         param.want_addr = addr ? 1 : 0;
 442         param.want_textaddr = textaddr ? 1 : 0;
 443         param.inputs.buf = buf;
 444         param.inputs.buflen = buflen;
 445         param.inputs.flags = flags;
 446 
 447         ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
 448 
 449         if (ret == PHP_STREAM_OPTION_RETURN_OK) {
 450                 if (addr) {
 451                         *addr = param.outputs.addr;
 452                         *addrlen = param.outputs.addrlen;
 453                 }
 454                 if (textaddr) {
 455                         *textaddr = param.outputs.textaddr;
 456                 }
 457                 return recvd_len + param.outputs.returncode;
 458         }
 459         return recvd_len ? recvd_len : -1;
 460 }
 461 
 462 /* Similar to send() system call; send data to the stream, optionally
 463  * sending it as OOB data */
 464 PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t buflen,
 465                 int flags, void *addr, socklen_t addrlen)
 466 {
 467         php_stream_xport_param param;
 468         int ret = 0;
 469         int oob;
 470 
 471 #if 0
 472         if (flags == 0 && addr == NULL) {
 473                 return php_stream_write(stream, buf, buflen);
 474         }
 475 #endif
 476 
 477         oob = (flags & STREAM_OOB) == STREAM_OOB;
 478 
 479         if ((oob || addr) && stream->writefilters.head) {
 480                 php_error_docref(NULL, E_WARNING, "cannot write OOB data, or data to a targeted address on a filtered stream");
 481                 return -1;
 482         }
 483 
 484         memset(&param, 0, sizeof(param));
 485 
 486         param.op = STREAM_XPORT_OP_SEND;
 487         param.want_addr = addr ? 1 : 0;
 488         param.inputs.buf = (char*)buf;
 489         param.inputs.buflen = buflen;
 490         param.inputs.flags = flags;
 491         param.inputs.addr = addr;
 492         param.inputs.addrlen = addrlen;
 493 
 494         ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
 495 
 496         if (ret == PHP_STREAM_OPTION_RETURN_OK) {
 497                 return param.outputs.returncode;
 498         }
 499         return -1;
 500 }
 501 
 502 /* Similar to shutdown() system call; shut down part of a full-duplex
 503  * connection */
 504 PHPAPI int php_stream_xport_shutdown(php_stream *stream, stream_shutdown_t how)
 505 {
 506         php_stream_xport_param param;
 507         int ret = 0;
 508 
 509         memset(&param, 0, sizeof(param));
 510 
 511         param.op = STREAM_XPORT_OP_SHUTDOWN;
 512         param.how = how;
 513 
 514         ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
 515 
 516         if (ret == PHP_STREAM_OPTION_RETURN_OK) {
 517                 return param.outputs.returncode;
 518         }
 519         return -1;
 520 }
 521 
 522 /*
 523  * Local variables:
 524  * tab-width: 4
 525  * c-basic-offset: 4
 526  * End:
 527  * vim600: noet sw=4 ts=4 fdm=marker
 528  * vim<600: noet sw=4 ts=4
 529  */

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