1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2017 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 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 */
php_stream_xport_bind(php_stream * stream,const char * name,size_t namelen,zend_string ** error_text)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(¶m, 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, ¶m);
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 */
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)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(¶m, 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, ¶m);
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 */
php_stream_xport_listen(php_stream * stream,int backlog,zend_string ** error_text)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(¶m, 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, ¶m);
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) */
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)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(¶m, 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, ¶m);
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
php_stream_xport_get_name(php_stream * stream,int want_peer,zend_string ** textaddr,void ** addr,socklen_t * addrlen)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(¶m, 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, ¶m);
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
php_stream_xport_crypto_setup(php_stream * stream,php_stream_xport_crypt_method_t crypto_method,php_stream * session_stream)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(¶m, 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, ¶m);
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
php_stream_xport_crypto_enable(php_stream * stream,int activate)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(¶m, 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, ¶m);
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 */
php_stream_xport_recvfrom(php_stream * stream,char * buf,size_t buflen,int flags,void ** addr,socklen_t * addrlen,zend_string ** textaddr)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(¶m, 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, ¶m);
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 */
php_stream_xport_sendto(php_stream * stream,const char * buf,size_t buflen,int flags,void * addr,socklen_t addrlen)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(¶m, 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, ¶m);
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 */
php_stream_xport_shutdown(php_stream * stream,stream_shutdown_t how)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(¶m, 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, ¶m);
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 */
530