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