1 /*
2 +----------------------------------------------------------------------+
3 | Swoole |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 2.0 of the Apache license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.apache.org/licenses/LICENSE-2.0.html |
9 | If you did not receive a copy of the Apache2.0 license and are unable|
10 | to obtain it through the world-wide-web, please send a note to |
11 | license@swoole.com so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Tianfeng Han <mikan.tenny@gmail.com> |
14 +----------------------------------------------------------------------+
15 */
16 #include "php_swoole_cxx.h"
17 #include "swoole_socket.h"
18 #include "swoole_util.h"
19
20 #include "thirdparty/php/standard/proc_open.h"
21 #ifdef SW_USE_CURL
22 #include "thirdparty/php/curl/curl_interface.h"
23 #endif
24
25 #if PHP_VERSION_ID >= 80000
26 #include "swoole_hook_sockets_arginfo.h"
27 #endif
28
29 #include <unordered_map>
30
31 /* openssl */
32 #ifndef OPENSSL_NO_ECDH
33 #define HAVE_ECDH 1
34 #endif
35 #ifndef OPENSSL_NO_TLSEXT
36 #define HAVE_TLS_SNI 1
37 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
38 #define HAVE_TLS_ALPN 1
39 #endif
40 #endif
41 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
42 #define HAVE_SEC_LEVEL 1
43 #endif
44
45 using swoole::Coroutine;
46 using swoole::PHPCoroutine;
47 using swoole::coroutine::Socket;
48 using swoole::coroutine::System;
49 using swoole::coroutine::PollSocket;
50
51 SW_EXTERN_C_BEGIN
52 static PHP_METHOD(swoole_runtime, enableCoroutine);
53 static PHP_METHOD(swoole_runtime, getHookFlags);
54 static PHP_METHOD(swoole_runtime, setHookFlags);
55 static PHP_FUNCTION(swoole_sleep);
56 static PHP_FUNCTION(swoole_usleep);
57 static PHP_FUNCTION(swoole_time_nanosleep);
58 static PHP_FUNCTION(swoole_time_sleep_until);
59 static PHP_FUNCTION(swoole_stream_select);
60 static PHP_FUNCTION(swoole_stream_socket_pair);
61 static PHP_FUNCTION(swoole_user_func_handler);
62 SW_EXTERN_C_END
63
64 static int socket_set_option(php_stream *stream, int option, int value, void *ptrparam);
65 static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count);
66 static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_t count);
67 static int socket_flush(php_stream *stream);
68 static int socket_close(php_stream *stream, int close_handle);
69 static int socket_stat(php_stream *stream, php_stream_statbuf *ssb);
70 static int socket_cast(php_stream *stream, int castas, void **ret);
71 static bool socket_ssl_set_options(Socket *sock, php_stream_context *context);
72 // clang-format off
73
74 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0)
75 ZEND_END_ARG_INFO()
76
77 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_runtime_enableCoroutine, 0, 0, 0)
78 ZEND_ARG_INFO(0, enable)
79 ZEND_ARG_INFO(0, flags)
80 ZEND_END_ARG_INFO()
81
82 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_runtime_setHookFlags, 0, 0, 1)
83 ZEND_ARG_INFO(0, flags)
84 ZEND_END_ARG_INFO()
85
86 static zend_class_entry *swoole_runtime_ce;
87
88 static php_stream_ops socket_ops {
89 socket_write,
90 socket_read,
91 socket_close,
92 socket_flush,
93 "tcp_socket/coroutine",
94 nullptr, /* seek */
95 socket_cast,
96 socket_stat,
97 socket_set_option,
98 };
99
100 struct php_swoole_netstream_data_t {
101 php_netstream_data_t stream;
102 Socket *socket;
103 bool blocking;
104 };
105
106 static bool runtime_hook_init = false;
107 static int runtime_hook_flags = 0;
108
109 static struct {
110 php_stream_transport_factory tcp;
111 php_stream_transport_factory udp;
112 php_stream_transport_factory _unix;
113 php_stream_transport_factory udg;
114 php_stream_transport_factory ssl;
115 php_stream_transport_factory tls;
116 } ori_factory = {
117 nullptr,
118 nullptr,
119 nullptr,
120 nullptr,
121 nullptr,
122 nullptr,
123 };
124
125 static std::vector<std::string> unsafe_functions {
126 "pcntl_fork",
127 "pcntl_wait",
128 "pcntl_waitpid",
129 "pcntl_sigtimedwait",
130 };
131
132 static const zend_function_entry swoole_runtime_methods[] =
133 {
134 PHP_ME(swoole_runtime, enableCoroutine, arginfo_swoole_runtime_enableCoroutine, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
135 PHP_ME(swoole_runtime, getHookFlags, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
136 PHP_ME(swoole_runtime, setHookFlags, arginfo_swoole_runtime_setHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
137 PHP_FE_END
138 };
139 // clang-format on
140
141 static php_stream_wrapper ori_php_plain_files_wrapper;
142 static php_stream_ops ori_php_stream_stdio_ops;
143
144 static void hook_func(const char *name, size_t l_name, zif_handler handler = nullptr, zend_internal_arg_info *arg_info = nullptr);
145 static void unhook_func(const char *name, size_t l_name);
146
get_arginfo(const char * name,size_t l_name)147 static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) {
148 zend_function *zf = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, l_name);
149 if (zf == nullptr) {
150 return nullptr;
151 }
152 return zf->internal_function.arg_info;
153 }
154
155 #define SW_HOOK_FUNC(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_##f))
156 #define SW_UNHOOK_FUNC(f) unhook_func(ZEND_STRL(#f))
157 #define SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_native_##f), get_arginfo(ZEND_STRL("swoole_native_" #f)))
158
159 #if PHP_VERSION_ID >= 80000
160 #define SW_HOOK_SOCKETS_FUNC(f) hook_func(ZEND_STRL(#f), nullptr, get_arginfo(ZEND_STRL("swoole_native_" #f)))
161
162 #define SW_HOOK_FE(name, arg_info) ZEND_RAW_FENTRY("swoole_native_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0)
163
164 static const zend_function_entry swoole_sockets_functions[] = {
165 SW_HOOK_FE(socket_create_listen, arginfo_swoole_native_socket_create_listen)
166 SW_HOOK_FE(socket_accept, arginfo_swoole_native_socket_accept)
167 SW_HOOK_FE(socket_set_nonblock, arginfo_swoole_native_socket_set_nonblock)
168 SW_HOOK_FE(socket_set_block, arginfo_swoole_native_socket_set_block)
169 SW_HOOK_FE(socket_listen, arginfo_swoole_native_socket_listen)
170 SW_HOOK_FE(socket_close, arginfo_swoole_native_socket_close)
171 SW_HOOK_FE(socket_write, arginfo_swoole_native_socket_write)
172 SW_HOOK_FE(socket_read, arginfo_swoole_native_socket_read)
173 SW_HOOK_FE(socket_getsockname, arginfo_swoole_native_socket_getsockname)
174 SW_HOOK_FE(socket_getpeername, arginfo_swoole_native_socket_getpeername)
175 SW_HOOK_FE(socket_create, arginfo_swoole_native_socket_create)
176 SW_HOOK_FE(socket_connect, arginfo_swoole_native_socket_connect)
177 SW_HOOK_FE(socket_strerror, arginfo_swoole_native_socket_strerror)
178 SW_HOOK_FE(socket_bind, arginfo_swoole_native_socket_bind)
179 SW_HOOK_FE(socket_recv, arginfo_swoole_native_socket_recv)
180 SW_HOOK_FE(socket_send, arginfo_swoole_native_socket_send)
181 SW_HOOK_FE(socket_recvfrom, arginfo_swoole_native_socket_recvfrom)
182 SW_HOOK_FE(socket_sendto, arginfo_swoole_native_socket_sendto)
183 SW_HOOK_FE(socket_get_option, arginfo_swoole_native_socket_get_option)
184 SW_HOOK_FE(socket_set_option, arginfo_swoole_native_socket_set_option)
185 SW_HOOK_FE(socket_getopt, arginfo_swoole_native_socket_getopt)
186 SW_HOOK_FE(socket_setopt, arginfo_swoole_native_socket_setopt)
187 SW_HOOK_FE(socket_shutdown, arginfo_swoole_native_socket_shutdown)
188 SW_HOOK_FE(socket_last_error, arginfo_swoole_native_socket_last_error)
189 SW_HOOK_FE(socket_clear_error, arginfo_swoole_native_socket_clear_error)
190 ZEND_FE_END
191 };
192 #else
193 #define SW_HOOK_SOCKETS_FUNC(f) hook_func(ZEND_STRL(#f))
194 #endif
195
196 static zend_array *tmp_function_table = nullptr;
197
198 SW_EXTERN_C_BEGIN
199 #include "ext/standard/file.h"
200 #include "thirdparty/php/streams/plain_wrapper.c"
201 SW_EXTERN_C_END
202
php_swoole_runtime_minit(int module_number)203 void php_swoole_runtime_minit(int module_number) {
204 SW_INIT_CLASS_ENTRY_BASE(
205 swoole_runtime, "Swoole\\Runtime", "swoole_runtime", nullptr, swoole_runtime_methods, nullptr);
206 SW_SET_CLASS_CREATE(swoole_runtime, sw_zend_create_object_deny);
207
208 #if PHP_VERSION_ID >= 80000
209 zend_unregister_functions(swoole_sockets_functions, -1, CG(function_table));
210 zend_register_functions(NULL, swoole_sockets_functions, NULL, MODULE_PERSISTENT);
211 #endif
212
213 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_TCP", PHPCoroutine::HOOK_TCP);
214 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UDP", PHPCoroutine::HOOK_UDP);
215 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UNIX", PHPCoroutine::HOOK_UNIX);
216 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UDG", PHPCoroutine::HOOK_UDG);
217 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SSL", PHPCoroutine::HOOK_SSL);
218 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_TLS", PHPCoroutine::HOOK_TLS);
219 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_FUNCTION", PHPCoroutine::HOOK_STREAM_FUNCTION);
220 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_SELECT", PHPCoroutine::HOOK_STREAM_FUNCTION); // backward compatibility
221 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_FILE", PHPCoroutine::HOOK_FILE);
222 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STDIO", PHPCoroutine::HOOK_STDIO);
223 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SLEEP", PHPCoroutine::HOOK_SLEEP);
224 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PROC", PHPCoroutine::HOOK_PROC);
225 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_CURL", PHPCoroutine::HOOK_CURL);
226 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_NATIVE_CURL", PHPCoroutine::HOOK_NATIVE_CURL);
227 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_BLOCKING_FUNCTION", PHPCoroutine::HOOK_BLOCKING_FUNCTION);
228 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SOCKETS", PHPCoroutine::HOOK_SOCKETS);
229 SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_ALL", PHPCoroutine::HOOK_ALL);
230 #ifdef SW_USE_CURL
231 swoole_native_curl_minit(module_number);
232 #endif
233 swoole_proc_open_init(module_number);
234 }
235
236 struct real_func {
237 zend_function *function;
238 zif_handler ori_handler;
239 zend_internal_arg_info *ori_arg_info;
240 uint32_t ori_fn_flags;
241 uint32_t ori_num_args;
242 zend_fcall_info_cache *fci_cache;
243 zval name;
244 };
245
php_swoole_runtime_rinit()246 void php_swoole_runtime_rinit() {
247 tmp_function_table = (zend_array *) emalloc(sizeof(zend_array));
248 zend_hash_init(tmp_function_table, 8, nullptr, nullptr, 0);
249 }
250
php_swoole_runtime_rshutdown()251 void php_swoole_runtime_rshutdown() {
252 void *ptr;
253 ZEND_HASH_FOREACH_PTR(tmp_function_table, ptr) {
254 real_func *rf = reinterpret_cast<real_func *>(ptr);
255 /**
256 * php library function
257 */
258 if (rf->fci_cache) {
259 zval_dtor(&rf->name);
260 efree(rf->fci_cache);
261 }
262 rf->function->internal_function.handler = rf->ori_handler;
263 rf->function->internal_function.arg_info = rf->ori_arg_info;
264 efree(rf);
265 }
266 ZEND_HASH_FOREACH_END();
267 zend_hash_destroy(tmp_function_table);
268 efree(tmp_function_table);
269 tmp_function_table = nullptr;
270 }
271
php_swoole_runtime_mshutdown()272 void php_swoole_runtime_mshutdown() {
273 #ifdef SW_USE_CURL
274 swoole_native_curl_mshutdown();
275 #endif
276 }
277
parse_ip_address_ex(const char * str,size_t str_len,int * portno,int get_err,zend_string ** err)278 static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err) {
279 char *colon;
280 char *host = nullptr;
281 char *p;
282
283 if (*(str) == '[' && str_len > 1) {
284 /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
285 p = (char *) memchr(str + 1, ']', str_len - 2);
286 if (!p || *(p + 1) != ':') {
287 if (get_err) {
288 *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str);
289 }
290 return nullptr;
291 }
292 *portno = atoi(p + 2);
293 return estrndup(str + 1, p - str - 1);
294 }
295 if (str_len) {
296 colon = (char *) memchr(str, ':', str_len - 1);
297 } else {
298 colon = nullptr;
299 }
300 if (colon) {
301 *portno = atoi(colon + 1);
302 host = estrndup(str, colon - str);
303 } else {
304 if (get_err) {
305 *err = strpprintf(0, "Failed to parse address \"%s\"", str);
306 }
307 return nullptr;
308 }
309
310 return host;
311 }
312
socket_write(php_stream * stream,const char * buf,size_t count)313 static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_t count) {
314 php_swoole_netstream_data_t *abstract;
315 Socket *sock;
316 ssize_t didwrite = -1;
317
318 abstract = (php_swoole_netstream_data_t *) stream->abstract;
319 if (UNEXPECTED(!abstract)) {
320 goto _exit;
321 }
322
323 sock = (Socket *) abstract->socket;
324 if (UNEXPECTED(!sock)) {
325 goto _exit;
326 }
327
328 if (abstract->blocking) {
329 didwrite = sock->send_all(buf, count);
330 } else {
331 didwrite = sock->get_socket()->send(buf, count, 0);
332 sock->set_err(errno);
333 }
334
335 if (didwrite < 0 || (size_t) didwrite != count) {
336 /* we do not expect the outer layer to continue to call the send syscall in a loop
337 * and didwrite is meaningless if it failed */
338 didwrite = -1;
339 abstract->stream.timeout_event = (sock->errCode == ETIMEDOUT);
340 php_error_docref(NULL,
341 E_NOTICE,
342 "Send of " ZEND_LONG_FMT " bytes failed with errno=%d %s",
343 (zend_long) count,
344 sock->errCode,
345 sock->errMsg);
346 } else {
347 php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), didwrite, 0);
348 }
349
350 if (didwrite < 0) {
351 if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_error(sock->errCode) == SW_WAIT) {
352 didwrite = 0;
353 } else {
354 stream->eof = 1;
355 }
356 } else if (didwrite == 0) {
357 stream->eof = 1;
358 }
359
360 _exit:
361 #if PHP_VERSION_ID < 70400
362 if (didwrite < 0) {
363 didwrite = 0;
364 }
365 #endif
366 return didwrite;
367 }
368
socket_read(php_stream * stream,char * buf,size_t count)369 static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count)
370 {
371 php_swoole_netstream_data_t *abstract;
372 Socket *sock;
373 ssize_t nr_bytes = -1;
374
375 abstract = (php_swoole_netstream_data_t *) stream->abstract;
376 if (UNEXPECTED(!abstract)) {
377 goto _exit;
378 }
379
380 sock = (Socket *) abstract->socket;
381 if (UNEXPECTED(!sock)) {
382 goto _exit;
383 }
384
385 if (abstract->blocking) {
386 nr_bytes = sock->recv(buf, count);
387 } else {
388 nr_bytes = sock->get_socket()->recv(buf, count, 0);
389 sock->set_err(errno);
390 }
391
392 if (nr_bytes > 0) {
393 php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0);
394 }
395
396 if (nr_bytes < 0) {
397 if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_error(sock->errCode) == SW_WAIT) {
398 nr_bytes = 0;
399 } else {
400 stream->eof = 1;
401 }
402 } else if (nr_bytes == 0) {
403 stream->eof = 1;
404 }
405
406 _exit:
407 #if PHP_VERSION_ID < 70400
408 if (nr_bytes < 0) {
409 nr_bytes = 0;
410 }
411 #endif
412 return nr_bytes;
413 }
414
socket_flush(php_stream * stream)415 static int socket_flush(php_stream *stream) {
416 return 0;
417 }
418
socket_close(php_stream * stream,int close_handle)419 static int socket_close(php_stream *stream, int close_handle) {
420 php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract;
421 if (UNEXPECTED(!abstract)) {
422 return FAILURE;
423 }
424 /** set it null immediately */
425 stream->abstract = nullptr;
426 Socket *sock = (Socket *) abstract->socket;
427 if (UNEXPECTED(!sock)) {
428 return FAILURE;
429 }
430 /**
431 * it's always successful (even if the destructor rule is violated)
432 * every calls passes through the hook function in PHP
433 * so there is unnecessary to worry about the null pointer.
434 */
435 sock->close();
436 delete sock;
437 efree(abstract);
438 return SUCCESS;
439 }
440
441 enum {
442 STREAM_XPORT_OP_BIND,
443 STREAM_XPORT_OP_CONNECT,
444 STREAM_XPORT_OP_LISTEN,
445 STREAM_XPORT_OP_ACCEPT,
446 STREAM_XPORT_OP_CONNECT_ASYNC,
447 STREAM_XPORT_OP_GET_NAME,
448 STREAM_XPORT_OP_GET_PEER_NAME,
449 STREAM_XPORT_OP_RECV,
450 STREAM_XPORT_OP_SEND,
451 STREAM_XPORT_OP_SHUTDOWN,
452 };
453
454 enum { STREAM_XPORT_CRYPTO_OP_SETUP, STREAM_XPORT_CRYPTO_OP_ENABLE };
455
socket_cast(php_stream * stream,int castas,void ** ret)456 static int socket_cast(php_stream *stream, int castas, void **ret) {
457 php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract;
458 if (UNEXPECTED(!abstract)) {
459 return FAILURE;
460 }
461 Socket *sock = (Socket *) abstract->socket;
462 if (UNEXPECTED(!sock)) {
463 return FAILURE;
464 }
465
466 switch (castas) {
467 case PHP_STREAM_AS_STDIO:
468 if (ret) {
469 *(FILE **) ret = fdopen(sock->get_fd(), stream->mode);
470 if (*ret) {
471 return SUCCESS;
472 }
473 return FAILURE;
474 }
475 return SUCCESS;
476 case PHP_STREAM_AS_FD_FOR_SELECT:
477 case PHP_STREAM_AS_FD:
478 case PHP_STREAM_AS_SOCKETD:
479 if (ret) *(php_socket_t *) ret = sock->get_fd();
480 return SUCCESS;
481 default:
482 return FAILURE;
483 }
484 }
485
socket_stat(php_stream * stream,php_stream_statbuf * ssb)486 static int socket_stat(php_stream *stream, php_stream_statbuf *ssb) {
487 php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract;
488 if (UNEXPECTED(!abstract)) {
489 return FAILURE;
490 }
491 Socket *sock = (Socket *) abstract->socket;
492 if (UNEXPECTED(!sock)) {
493 return FAILURE;
494 }
495 return zend_fstat(sock->get_fd(), &ssb->sb);
496 }
497
socket_connect(php_stream * stream,Socket * sock,php_stream_xport_param * xparam)498 static inline int socket_connect(php_stream *stream, Socket *sock, php_stream_xport_param *xparam) {
499 char *host = nullptr, *bindto = nullptr;
500 int portno = 0, bindport = 0;
501 int ret = 0;
502 zval *tmpzval = nullptr;
503 char *ip_address = nullptr;
504
505 if (UNEXPECTED(sock->get_fd() < 0)) {
506 return FAILURE;
507 }
508
509 if (sock->get_socket()->is_inet()) {
510 ip_address = parse_ip_address_ex(
511 xparam->inputs.name, xparam->inputs.namelen, &portno, xparam->want_errortext, &xparam->outputs.error_text);
512 host = ip_address;
513 if (sock->get_sock_type() == SOCK_STREAM) {
514 sock->get_socket()->set_tcp_nodelay();
515 }
516 } else {
517 host = xparam->inputs.name;
518 }
519 if (host == nullptr) {
520 return FAILURE;
521 }
522 ON_SCOPE_EXIT {
523 if (ip_address) {
524 efree(ip_address);
525 }
526 };
527 if (PHP_STREAM_CONTEXT(stream) &&
528 (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "bindto")) != nullptr) {
529 if (Z_TYPE_P(tmpzval) != IS_STRING) {
530 if (xparam->want_errortext) {
531 xparam->outputs.error_text = strpprintf(0, "local_addr context option is not a string.");
532 }
533 return FAILURE;
534 }
535 bindto = parse_ip_address_ex(
536 Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text);
537 if (bindto == nullptr) {
538 return FAILURE;
539 }
540 ON_SCOPE_EXIT {
541 if (bindto) {
542 efree(bindto);
543 }
544 };
545 if (!sock->bind(bindto, bindport)) {
546 return FAILURE;
547 }
548 }
549
550 if (xparam->inputs.timeout) {
551 sock->set_timeout(xparam->inputs.timeout, Socket::TIMEOUT_CONNECT);
552 }
553 if (sock->connect(host, portno) == false) {
554 xparam->outputs.error_code = sock->errCode;
555 if (sock->errMsg) {
556 xparam->outputs.error_text = zend_string_init(sock->errMsg, strlen(sock->errMsg), 0);
557 }
558 ret = -1;
559 }
560 return ret;
561 }
562
socket_bind(php_stream * stream,Socket * sock,php_stream_xport_param * xparam STREAMS_DC)563 static inline int socket_bind(php_stream *stream, Socket *sock, php_stream_xport_param *xparam STREAMS_DC) {
564 char *host = nullptr;
565 int portno = 0;
566 char *ip_address = nullptr;
567
568 if (sock->get_socket()->is_inet()) {
569 ip_address = parse_ip_address_ex(
570 xparam->inputs.name, xparam->inputs.namelen, &portno, xparam->want_errortext, &xparam->outputs.error_text);
571 host = ip_address;
572 } else {
573 host = xparam->inputs.name;
574 }
575 if (host == nullptr) {
576 sock->set_err(EINVAL);
577 return -1;
578 }
579 int ret = sock->bind(host, portno) ? 0 : -1;
580 if (ip_address) {
581 efree(ip_address);
582 }
583 return ret;
584 }
585
socket_accept(php_stream * stream,Socket * sock,php_stream_xport_param * xparam STREAMS_DC)586 static inline int socket_accept(php_stream *stream, Socket *sock, php_stream_xport_param *xparam STREAMS_DC) {
587 int tcp_nodelay = 0;
588 zval *tmpzval = nullptr;
589
590 xparam->outputs.client = nullptr;
591
592 if ((nullptr != PHP_STREAM_CONTEXT(stream)) &&
593 (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != nullptr &&
594 zval_is_true(tmpzval)) {
595 tcp_nodelay = 1;
596 }
597
598 zend_string **textaddr = xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr;
599 struct sockaddr **addr = xparam->want_addr ? &xparam->outputs.addr : nullptr;
600 socklen_t *addrlen = xparam->want_addr ? &xparam->outputs.addrlen : nullptr;
601
602 struct timeval *timeout = xparam->inputs.timeout;
603 zend_string **error_string = xparam->want_errortext ? &xparam->outputs.error_text : nullptr;
604 int *error_code = &xparam->outputs.error_code;
605
606 int error = 0;
607 php_sockaddr_storage sa;
608 socklen_t sl = sizeof(sa);
609
610 if (timeout) {
611 sock->set_timeout(timeout, Socket::TIMEOUT_READ);
612 }
613
614 Socket *clisock = sock->accept();
615
616 #ifdef SW_USE_OPENSSL
617 if (clisock != nullptr && clisock->ssl_is_enable()) {
618 if (!clisock->ssl_handshake()) {
619 sock->errCode = clisock->errCode;
620 delete clisock;
621 clisock = nullptr;
622 }
623 }
624 #endif
625
626 if (clisock == nullptr) {
627 error = sock->errCode;
628 if (error_code) {
629 *error_code = error;
630 }
631 if (error_string) {
632 *error_string = php_socket_error_str(error);
633 }
634 return FAILURE;
635 } else {
636 php_network_populate_name_from_sockaddr((struct sockaddr *) &sa, sl, textaddr, addr, addrlen);
637 #ifdef TCP_NODELAY
638 if (tcp_nodelay) {
639 clisock->get_socket()->set_tcp_nodelay(tcp_nodelay);
640 }
641 #endif
642 php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) emalloc(sizeof(*abstract));
643 memset(abstract, 0, sizeof(*abstract));
644
645 abstract->socket = clisock;
646 abstract->blocking = true;
647
648 xparam->outputs.client = php_stream_alloc_rel(stream->ops, (void *) abstract, nullptr, "r+");
649 if (xparam->outputs.client) {
650 xparam->outputs.client->ctx = stream->ctx;
651 if (stream->ctx) {
652 GC_ADDREF(stream->ctx);
653 }
654 }
655 return 0;
656 }
657 }
658
socket_recvfrom(Socket * sock,char * buf,size_t buflen,zend_string ** textaddr,struct sockaddr ** addr,socklen_t * addrlen)659 static inline int socket_recvfrom(
660 Socket *sock, char *buf, size_t buflen, zend_string **textaddr, struct sockaddr **addr, socklen_t *addrlen) {
661 int ret;
662 int want_addr = textaddr || addr;
663
664 if (want_addr) {
665 php_sockaddr_storage sa;
666 socklen_t sl = sizeof(sa);
667 ret = sock->recvfrom(buf, buflen, (struct sockaddr *) &sa, &sl);
668 if (sl) {
669 php_network_populate_name_from_sockaddr((struct sockaddr *) &sa, sl, textaddr, addr, addrlen);
670 } else {
671 if (textaddr) {
672 *textaddr = ZSTR_EMPTY_ALLOC();
673 }
674 if (addr) {
675 *addr = nullptr;
676 *addrlen = 0;
677 }
678 }
679 } else {
680 ret = sock->recv(buf, buflen);
681 }
682
683 return ret;
684 }
685
socket_sendto(Socket * sock,const char * buf,size_t buflen,struct sockaddr * addr,socklen_t addrlen)686 static inline int socket_sendto(
687 Socket *sock, const char *buf, size_t buflen, struct sockaddr *addr, socklen_t addrlen) {
688 if (addr) {
689 return sendto(sock->get_fd(), buf, buflen, 0, addr, addrlen);
690 } else {
691 return sock->send(buf, buflen);
692 }
693 }
694
695 #ifdef SW_USE_OPENSSL
696
697 #define GET_VER_OPT(name) \
698 (PHP_STREAM_CONTEXT(stream) && \
699 (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", name)) != nullptr)
700 #define GET_VER_OPT_STRING(name, str) \
701 if (GET_VER_OPT(name)) { \
702 convert_to_string_ex(val); \
703 str = Z_STRVAL_P(val); \
704 }
705 #define GET_VER_OPT_LONG(name, num) \
706 if (GET_VER_OPT(name)) { \
707 convert_to_long_ex(val); \
708 num = Z_LVAL_P(val); \
709 }
710
socket_setup_crypto(php_stream * stream,Socket * sock,php_stream_xport_crypto_param * cparam STREAMS_DC)711 static int socket_setup_crypto(php_stream *stream, Socket *sock, php_stream_xport_crypto_param *cparam STREAMS_DC) {
712 return 0;
713 }
714
socket_xport_crypto_setup(php_stream * stream)715 static int socket_xport_crypto_setup(php_stream *stream) {
716 php_stream_xport_crypto_param param;
717 int ret;
718
719 memset(¶m, 0, sizeof(param));
720 param.op = (decltype(param.op)) STREAM_XPORT_CRYPTO_OP_SETUP;
721 param.inputs.method = (php_stream_xport_crypt_method_t) 0;
722 param.inputs.session = NULL;
723
724 ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m);
725
726 if (ret == PHP_STREAM_OPTION_RETURN_OK) {
727 return param.outputs.returncode;
728 }
729
730 php_error_docref("streams.crypto", E_WARNING, "this stream does not support SSL/crypto");
731
732 return ret;
733 }
734
socket_xport_crypto_enable(php_stream * stream,int activate)735 static int socket_xport_crypto_enable(php_stream *stream, int activate) {
736 php_stream_xport_crypto_param param;
737 int ret;
738
739 memset(¶m, 0, sizeof(param));
740 param.op = (decltype(param.op)) STREAM_XPORT_CRYPTO_OP_ENABLE;
741 param.inputs.activate = activate;
742
743 ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m);
744
745 if (ret == PHP_STREAM_OPTION_RETURN_OK) {
746 return param.outputs.returncode;
747 }
748
749 php_error_docref("streams.crypto", E_WARNING, "this stream does not support SSL/crypto");
750
751 return ret;
752 }
753
php_openssl_capture_peer_certs(php_stream * stream,Socket * sslsock)754 static bool php_openssl_capture_peer_certs(php_stream *stream, Socket *sslsock) {
755 zval *val;
756
757 std::string peer_cert = sslsock->ssl_get_peer_cert();
758 if (peer_cert.empty()) {
759 return false;
760 }
761
762 zval argv[1];
763 ZVAL_STRINGL(&argv[0], peer_cert.c_str(), peer_cert.length());
764 zend::function::ReturnValue retval = zend::function::call("openssl_x509_read", 1, argv);
765 php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate", &retval.value);
766 zval_dtor(&argv[0]);
767
768 if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "capture_peer_cert_chain"))
769 && zend_is_true(val)) {
770 zval arr;
771 auto chain = sslsock->get_socket()->ssl_get_peer_cert_chain(INT_MAX);
772
773 if (!chain.empty()) {
774 array_init(&arr);
775 for (auto &cert : chain) {
776 zval argv[1];
777 ZVAL_STRINGL(&argv[0], cert.c_str(), cert.length());
778 zend::function::ReturnValue retval = zend::function::call("openssl_x509_read", 1, argv);
779 zval_add_ref(&retval.value);
780 add_next_index_zval(&arr, &retval.value);
781 zval_dtor(&argv[0]);
782 }
783 } else {
784 ZVAL_NULL(&arr);
785 }
786
787 php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate_chain", &arr);
788 zval_ptr_dtor(&arr);
789 }
790
791 return true;
792 }
793
socket_enable_crypto(php_stream * stream,Socket * sock,php_stream_xport_crypto_param * cparam STREAMS_DC)794 static int socket_enable_crypto(php_stream *stream, Socket *sock, php_stream_xport_crypto_param *cparam STREAMS_DC) {
795 php_stream_context *context = PHP_STREAM_CONTEXT(stream);
796 if (cparam->inputs.activate && !sock->ssl_is_available()) {
797 sock->enable_ssl_encrypt();
798 if (!sock->ssl_check_context()) {
799 return -1;
800 }
801 if (!socket_ssl_set_options(sock, context)) {
802 return -1;
803 }
804 if (!sock->ssl_handshake()) {
805 return -1;
806 }
807 return 0;
808 } else if (!cparam->inputs.activate && sock->ssl_is_available()) {
809 return sock->ssl_shutdown() ? 0 : -1;
810 }
811
812 if (context) {
813 zval *val = php_stream_context_get_option(context, "ssl", "capture_peer_cert");
814 if (val && zend_is_true(val) && !php_openssl_capture_peer_certs(stream, sock)) {
815 return -1;
816 }
817 }
818
819 return 0;
820 }
821 #endif
822
socket_xport_api(php_stream * stream,Socket * sock,php_stream_xport_param * xparam STREAMS_DC)823 static inline int socket_xport_api(php_stream *stream, Socket *sock, php_stream_xport_param *xparam STREAMS_DC) {
824 static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR};
825
826 switch (xparam->op) {
827 case STREAM_XPORT_OP_LISTEN: {
828 xparam->outputs.returncode = sock->listen(xparam->inputs.backlog) ? 0 : -1;
829 break;
830 }
831 case STREAM_XPORT_OP_CONNECT:
832 case STREAM_XPORT_OP_CONNECT_ASYNC:
833 xparam->outputs.returncode = socket_connect(stream, sock, xparam);
834 #ifdef SW_USE_OPENSSL
835 if (sock->ssl_is_enable()
836 && (socket_xport_crypto_setup(stream) < 0 || socket_xport_crypto_enable(stream, 1) < 0)) {
837 xparam->outputs.returncode = -1;
838 }
839 #endif
840 break;
841 case STREAM_XPORT_OP_BIND: {
842 if (sock->get_sock_domain() != AF_UNIX) {
843 zval *tmpzval = nullptr;
844 php_stream_context *ctx = PHP_STREAM_CONTEXT(stream);
845 if (!ctx) {
846 break;
847 }
848 #ifdef SO_REUSEADDR
849 sock->get_socket()->set_reuse_addr();
850 #endif
851
852 #ifdef IPV6_V6ONLY
853 if ((tmpzval = php_stream_context_get_option(ctx, "socket", "ipv6_v6only")) != nullptr &&
854 zval_is_true(tmpzval)) {
855 sock->get_socket()->set_option(IPPROTO_IPV6, IPV6_V6ONLY, 1);
856 }
857 #endif
858
859 #ifdef SO_REUSEPORT
860 if ((tmpzval = php_stream_context_get_option(ctx, "socket", "so_reuseport")) != nullptr &&
861 zval_is_true(tmpzval)) {
862 sock->get_socket()->set_reuse_port();
863 }
864 #endif
865
866 #ifdef SO_BROADCAST
867 if ((tmpzval = php_stream_context_get_option(ctx, "socket", "so_broadcast")) != nullptr &&
868 zval_is_true(tmpzval)) {
869 sock->set_option(SOL_SOCKET, SO_BROADCAST, 1);
870 }
871 #endif
872 }
873 xparam->outputs.returncode = socket_bind(stream, sock, xparam STREAMS_CC);
874 break;
875 }
876 case STREAM_XPORT_OP_ACCEPT:
877 xparam->outputs.returncode = socket_accept(stream, sock, xparam STREAMS_CC);
878 break;
879 case STREAM_XPORT_OP_GET_NAME:
880 xparam->outputs.returncode =
881 php_network_get_sock_name(sock->get_fd(),
882 xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr,
883 xparam->want_addr ? &xparam->outputs.addr : nullptr,
884 xparam->want_addr ? &xparam->outputs.addrlen : nullptr);
885 break;
886 case STREAM_XPORT_OP_GET_PEER_NAME:
887 xparam->outputs.returncode =
888 php_network_get_peer_name(sock->get_fd(),
889 xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr,
890 xparam->want_addr ? &xparam->outputs.addr : nullptr,
891 xparam->want_addr ? &xparam->outputs.addrlen : nullptr);
892 break;
893
894 case STREAM_XPORT_OP_SEND:
895 if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
896 php_swoole_error(E_WARNING, "STREAM_OOB flags is not supports");
897 xparam->outputs.returncode = -1;
898 break;
899 }
900 xparam->outputs.returncode =
901 socket_sendto(sock, xparam->inputs.buf, xparam->inputs.buflen, xparam->inputs.addr, xparam->inputs.addrlen);
902 if (xparam->outputs.returncode == -1) {
903 char *err = php_socket_strerror(php_socket_errno(), nullptr, 0);
904 php_error_docref(nullptr, E_WARNING, "%s\n", err);
905 efree(err);
906 }
907 break;
908
909 case STREAM_XPORT_OP_RECV:
910 if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
911 php_swoole_error(E_WARNING, "STREAM_OOB flags is not supports");
912 xparam->outputs.returncode = -1;
913 break;
914 }
915 if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) {
916 xparam->outputs.returncode = sock->peek(xparam->inputs.buf, xparam->inputs.buflen);
917 } else {
918 xparam->outputs.returncode = socket_recvfrom(sock,
919 xparam->inputs.buf,
920 xparam->inputs.buflen,
921 xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr,
922 xparam->want_addr ? &xparam->outputs.addr : nullptr,
923 xparam->want_addr ? &xparam->outputs.addrlen : nullptr);
924 }
925 break;
926 case STREAM_XPORT_OP_SHUTDOWN:
927 xparam->outputs.returncode = sock->shutdown(shutdown_how[xparam->how]);
928 break;
929 default:
930 #ifdef SW_DEBUG
931 php_swoole_fatal_error(E_WARNING, "socket_xport_api: unsupported option %d", xparam->op);
932 #endif
933 break;
934 }
935 return PHP_STREAM_OPTION_RETURN_OK;
936 }
937
socket_set_option(php_stream * stream,int option,int value,void * ptrparam)938 static int socket_set_option(php_stream *stream, int option, int value, void *ptrparam) {
939 php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract;
940 if (UNEXPECTED(!abstract || !abstract->socket)) {
941 return PHP_STREAM_OPTION_RETURN_ERR;
942 }
943 Socket *sock = (Socket *) abstract->socket;
944 switch (option) {
945 case PHP_STREAM_OPTION_BLOCKING:
946 if (abstract->blocking == (bool) value) {
947 break;
948 }
949 abstract->blocking = (bool) value;
950 break;
951 case PHP_STREAM_OPTION_XPORT_API: {
952 return socket_xport_api(stream, sock, (php_stream_xport_param *) ptrparam STREAMS_CC);
953 }
954 case PHP_STREAM_OPTION_META_DATA_API: {
955 #ifdef SW_USE_OPENSSL
956 SSL *ssl = sock->get_socket() ? sock->get_socket()->ssl : nullptr;
957 if (ssl) {
958 zval tmp;
959 const char *proto_str;
960 const SSL_CIPHER *cipher;
961
962 array_init(&tmp);
963 switch (SSL_version(ssl)) {
964 #ifdef TLS1_3_VERSION
965 case TLS1_3_VERSION:
966 proto_str = "TLSv1.3";
967 break;
968 #endif
969 #ifdef TLS1_2_VERSION
970 case TLS1_2_VERSION:
971 proto_str = "TLSv1.2";
972 break;
973 #endif
974 #ifdef TLS1_1_VERSION
975 case TLS1_1_VERSION:
976 proto_str = "TLSv1.1";
977 break;
978 #endif
979 case TLS1_VERSION:
980 proto_str = "TLSv1";
981 break;
982 #ifdef SSL3_VERSION
983 case SSL3_VERSION:
984 proto_str = "SSLv3";
985 break;
986 #endif
987 default:
988 proto_str = "UNKNOWN";
989 break;
990 }
991
992 cipher = SSL_get_current_cipher(ssl);
993 add_assoc_string(&tmp, "protocol", (char *) proto_str);
994 add_assoc_string(&tmp, "cipher_name", (char *) SSL_CIPHER_get_name(cipher));
995 add_assoc_long(&tmp, "cipher_bits", SSL_CIPHER_get_bits(cipher, nullptr));
996 add_assoc_string(&tmp, "cipher_version", (char *) SSL_CIPHER_get_version(cipher));
997 add_assoc_zval((zval *) ptrparam, "crypto", &tmp);
998 }
999 #endif
1000 add_assoc_bool((zval *) ptrparam, "timed_out", sock->errCode == ETIMEDOUT);
1001 add_assoc_bool((zval *) ptrparam, "eof", stream->eof);
1002 add_assoc_bool((zval *) ptrparam, "blocked", 1);
1003 break;
1004 }
1005 case PHP_STREAM_OPTION_READ_TIMEOUT: {
1006 abstract->socket->set_timeout((struct timeval *) ptrparam, Socket::TIMEOUT_READ);
1007 break;
1008 }
1009 #ifdef SW_USE_OPENSSL
1010 case PHP_STREAM_OPTION_CRYPTO_API: {
1011 php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *) ptrparam;
1012 switch (cparam->op) {
1013 case STREAM_XPORT_CRYPTO_OP_SETUP:
1014 cparam->outputs.returncode = socket_setup_crypto(stream, sock, cparam STREAMS_CC);
1015 return PHP_STREAM_OPTION_RETURN_OK;
1016 case STREAM_XPORT_CRYPTO_OP_ENABLE:
1017 cparam->outputs.returncode = socket_enable_crypto(stream, sock, cparam STREAMS_CC);
1018 return PHP_STREAM_OPTION_RETURN_OK;
1019 default:
1020 /* never here */
1021 SW_ASSERT(0);
1022 break;
1023 }
1024 break;
1025 }
1026 #endif
1027 case PHP_STREAM_OPTION_CHECK_LIVENESS: {
1028 return sock->check_liveness() ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
1029 }
1030 case PHP_STREAM_OPTION_READ_BUFFER:
1031 case PHP_STREAM_OPTION_WRITE_BUFFER: {
1032 // TODO: read/write buffer
1033 break;
1034 }
1035 default:
1036 #ifdef SW_DEBUG
1037 php_swoole_fatal_error(E_WARNING, "socket_set_option: unsupported option %d with value %d", option, value);
1038 #endif
1039 break;
1040 }
1041 return PHP_STREAM_OPTION_RETURN_OK;
1042 }
1043
socket_ssl_set_options(Socket * sock,php_stream_context * context)1044 static bool socket_ssl_set_options(Socket *sock, php_stream_context *context) {
1045 if (context && ZVAL_IS_ARRAY(&context->options)) {
1046 #ifdef SW_USE_OPENSSL
1047 zval *ztmp;
1048
1049 if (sock->ssl_is_enable() && php_swoole_array_get_value(Z_ARRVAL_P(&context->options), "ssl", ztmp) &&
1050 ZVAL_IS_ARRAY(ztmp)) {
1051
1052 zval zalias;
1053 array_init(&zalias);
1054 zend_array *options = Z_ARRVAL_P(ztmp);
1055
1056 auto add_alias = [&zalias, options](const char *name, const char *alias) {
1057 zval *ztmp;
1058 if (php_swoole_array_get_value_ex(options, name, ztmp)) {
1059 add_assoc_zval_ex(&zalias, alias, strlen(alias), ztmp);
1060 zval_add_ref(ztmp);
1061 }
1062 };
1063
1064 add_alias("peer_name", "ssl_host_name");
1065 add_alias("verify_peer", "ssl_verify_peer");
1066 add_alias("allow_self_signed", "ssl_allow_self_signed");
1067 add_alias("cafile", "ssl_cafile");
1068 add_alias("capath", "ssl_capath");
1069 add_alias("local_cert", "ssl_cert_file");
1070 add_alias("local_pk", "ssl_key_file");
1071 add_alias("passphrase", "ssl_passphrase");
1072 add_alias("verify_depth", "ssl_verify_depth");
1073 add_alias("disable_compression", "ssl_disable_compression");
1074
1075 php_swoole_socket_set_ssl(sock, &zalias);
1076 if (!sock->ssl_check_context()) {
1077 return false;
1078 }
1079 zval_dtor(&zalias);
1080 }
1081 #endif
1082 }
1083
1084 return true;
1085 }
1086
socket_create(const char * proto,size_t protolen,const char * resourcename,size_t resourcenamelen,const char * persistent_id,int options,int flags,struct timeval * timeout,php_stream_context * context STREAMS_DC)1087 static php_stream *socket_create(const char *proto,
1088 size_t protolen,
1089 const char *resourcename,
1090 size_t resourcenamelen,
1091 const char *persistent_id,
1092 int options,
1093 int flags,
1094 struct timeval *timeout,
1095 php_stream_context *context STREAMS_DC) {
1096 php_stream *stream = nullptr;
1097 php_swoole_netstream_data_t *abstract = nullptr;
1098 Socket *sock;
1099
1100 Coroutine::get_current_safe();
1101
1102 if (SW_STREQ(proto, protolen, "tcp")) {
1103 _tcp:
1104 sock = new Socket(resourcename[0] == '[' ? SW_SOCK_TCP6 : SW_SOCK_TCP);
1105 } else if (SW_STREQ(proto, protolen, "ssl") || SW_STREQ(proto, protolen, "tls")) {
1106 #ifdef SW_USE_OPENSSL
1107 sock = new Socket(resourcename[0] == '[' ? SW_SOCK_TCP6 : SW_SOCK_TCP);
1108 sock->enable_ssl_encrypt();
1109 #else
1110 php_swoole_error(E_WARNING,
1111 "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole");
1112 return nullptr;
1113 #endif
1114 } else if (SW_STREQ(proto, protolen, "unix")) {
1115 sock = new Socket(SW_SOCK_UNIX_STREAM);
1116 } else if (SW_STREQ(proto, protolen, "udp")) {
1117 sock = new Socket(SW_SOCK_UDP);
1118 } else if (SW_STREQ(proto, protolen, "udg")) {
1119 sock = new Socket(SW_SOCK_UNIX_DGRAM);
1120 } else {
1121 /* abort? */
1122 goto _tcp;
1123 }
1124
1125 if (UNEXPECTED(sock->get_fd() < 0)) {
1126 _failed:
1127 if (!stream) {
1128 delete sock;
1129 } else {
1130 php_stream_close(stream);
1131 }
1132 return nullptr;
1133 }
1134
1135 sock->set_zero_copy(true);
1136
1137 abstract = (php_swoole_netstream_data_t *) ecalloc(1, sizeof(*abstract));
1138 abstract->socket = sock;
1139 abstract->stream.socket = sock->get_fd();
1140 abstract->blocking = true;
1141
1142 persistent_id = nullptr; // prevent stream api in user level using pconnect to persist the socket
1143 stream = php_stream_alloc_rel(&socket_ops, abstract, persistent_id, "r+");
1144
1145 if (stream == nullptr) {
1146 goto _failed;
1147 }
1148
1149 if (!socket_ssl_set_options(sock, context)) {
1150 goto _failed;
1151 }
1152
1153 return stream;
1154 }
1155
ZEND_FUNCTION(swoole_display_disabled_function)1156 static ZEND_FUNCTION(swoole_display_disabled_function) {
1157 zend_error(E_WARNING, "%s() has been disabled for security reasons", get_active_function_name());
1158 }
1159
disable_func(const char * name,size_t l_name)1160 static bool disable_func(const char *name, size_t l_name) {
1161 real_func *rf = (real_func*) zend_hash_str_find_ptr(tmp_function_table, name, l_name);
1162 if (rf) {
1163 rf->function->internal_function.handler = ZEND_FN(swoole_display_disabled_function);
1164 return true;
1165 }
1166
1167 zend_function *zf = (zend_function*) zend_hash_str_find_ptr(EG(function_table), name, l_name);
1168 if (zf == nullptr) {
1169 return false;
1170 }
1171
1172 rf = (real_func*) emalloc(sizeof(real_func));
1173 sw_memset_zero(rf, sizeof(*rf));
1174 rf->function = zf;
1175 rf->ori_handler = zf->internal_function.handler;
1176 rf->ori_arg_info = zf->internal_function.arg_info;
1177 rf->ori_fn_flags = zf->internal_function.fn_flags;
1178 rf->ori_num_args = zf->internal_function.num_args;
1179
1180 zf->internal_function.handler = ZEND_FN(swoole_display_disabled_function);
1181 zf->internal_function.arg_info = nullptr;
1182 zf->internal_function.fn_flags &= ~(ZEND_ACC_VARIADIC | ZEND_ACC_HAS_TYPE_HINTS | ZEND_ACC_HAS_RETURN_TYPE);
1183 zf->internal_function.num_args = 0;
1184
1185 zend_hash_add_ptr(tmp_function_table, zf->common.function_name, rf);
1186 return true;
1187 }
1188
enable_func(const char * name,size_t l_name)1189 static bool enable_func(const char *name, size_t l_name) {
1190 real_func *rf = (real_func*) zend_hash_str_find_ptr(tmp_function_table, name, l_name);
1191 if (!rf) {
1192 return false;
1193 }
1194
1195 rf->function->internal_function.handler = rf->ori_handler;
1196 rf->function->internal_function.arg_info = rf->ori_arg_info;
1197 rf->function->internal_function.fn_flags = rf->ori_fn_flags;
1198 rf->function->internal_function.num_args = rf->ori_num_args;
1199
1200 return true;
1201 }
1202
disable_unsafe_function()1203 void PHPCoroutine::disable_unsafe_function() {
1204 for (auto &f : unsafe_functions) {
1205 disable_func(f.c_str(), f.length());
1206 }
1207 }
1208
enable_unsafe_function()1209 void PHPCoroutine::enable_unsafe_function() {
1210 for (auto &f : unsafe_functions) {
1211 enable_func(f.c_str(), f.length());
1212 }
1213 }
1214
enable_hook(uint32_t flags)1215 bool PHPCoroutine::enable_hook(uint32_t flags) {
1216 if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK)) {
1217 swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK, &flags);
1218 }
1219
1220 if (!runtime_hook_init) {
1221 HashTable *xport_hash = php_stream_xport_get_hash();
1222 // php_stream
1223 ori_factory.tcp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tcp"));
1224 ori_factory.udp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udp"));
1225 ori_factory._unix = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("unix"));
1226 ori_factory.udg = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udg"));
1227 ori_factory.ssl = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("ssl"));
1228 ori_factory.tls = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tls"));
1229
1230 // file
1231 memcpy((void *) &ori_php_plain_files_wrapper, &php_plain_files_wrapper, sizeof(php_plain_files_wrapper));
1232 memcpy((void *) &ori_php_stream_stdio_ops, &php_stream_stdio_ops, sizeof(php_stream_stdio_ops));
1233
1234 runtime_hook_init = true;
1235 }
1236 // php_stream
1237 if (flags & PHPCoroutine::HOOK_TCP) {
1238 if (!(runtime_hook_flags & PHPCoroutine::HOOK_TCP)) {
1239 if (php_stream_xport_register("tcp", socket_create) != SUCCESS) {
1240 flags ^= PHPCoroutine::HOOK_TCP;
1241 }
1242 }
1243 } else {
1244 if (runtime_hook_flags & PHPCoroutine::HOOK_TCP) {
1245 php_stream_xport_register("tcp", ori_factory.tcp);
1246 }
1247 }
1248 if (flags & PHPCoroutine::HOOK_UDP) {
1249 if (!(runtime_hook_flags & PHPCoroutine::HOOK_UDP)) {
1250 if (php_stream_xport_register("udp", socket_create) != SUCCESS) {
1251 flags ^= PHPCoroutine::HOOK_UDP;
1252 }
1253 }
1254 } else {
1255 if (runtime_hook_flags & PHPCoroutine::HOOK_UDP) {
1256 php_stream_xport_register("udp", ori_factory.udp);
1257 }
1258 }
1259 if (flags & PHPCoroutine::HOOK_UNIX) {
1260 if (!(runtime_hook_flags & PHPCoroutine::HOOK_UNIX)) {
1261 if (php_stream_xport_register("unix", socket_create) != SUCCESS) {
1262 flags ^= PHPCoroutine::HOOK_UNIX;
1263 }
1264 }
1265 } else {
1266 if (runtime_hook_flags & PHPCoroutine::HOOK_UNIX) {
1267 php_stream_xport_register("unix", ori_factory._unix);
1268 }
1269 }
1270 if (flags & PHPCoroutine::HOOK_UDG) {
1271 if (!(runtime_hook_flags & PHPCoroutine::HOOK_UDG)) {
1272 if (php_stream_xport_register("udg", socket_create) != SUCCESS) {
1273 flags ^= PHPCoroutine::HOOK_UDG;
1274 }
1275 }
1276 } else {
1277 if (runtime_hook_flags & PHPCoroutine::HOOK_UDG) {
1278 php_stream_xport_register("udg", ori_factory.udg);
1279 }
1280 }
1281 if (flags & PHPCoroutine::HOOK_SSL) {
1282 if (!(runtime_hook_flags & PHPCoroutine::HOOK_SSL)) {
1283 if (php_stream_xport_register("ssl", socket_create) != SUCCESS) {
1284 flags ^= PHPCoroutine::HOOK_SSL;
1285 }
1286 }
1287 } else {
1288 if (runtime_hook_flags & PHPCoroutine::HOOK_SSL) {
1289 if (ori_factory.ssl != nullptr) {
1290 php_stream_xport_register("ssl", ori_factory.ssl);
1291 } else {
1292 php_stream_xport_unregister("ssl");
1293 }
1294 }
1295 }
1296 if (flags & PHPCoroutine::HOOK_TLS) {
1297 if (!(runtime_hook_flags & PHPCoroutine::HOOK_TLS)) {
1298 if (php_stream_xport_register("tls", socket_create) != SUCCESS) {
1299 flags ^= PHPCoroutine::HOOK_TLS;
1300 }
1301 }
1302 } else {
1303 if (runtime_hook_flags & PHPCoroutine::HOOK_TLS) {
1304 if (ori_factory.tls != nullptr) {
1305 php_stream_xport_register("tls", ori_factory.tls);
1306 } else {
1307 php_stream_xport_unregister("tls");
1308 }
1309 }
1310 }
1311 if (flags & PHPCoroutine::HOOK_STREAM_FUNCTION) {
1312 if (!(runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION)) {
1313 SW_HOOK_FUNC(stream_select);
1314 SW_HOOK_FUNC(stream_socket_pair);
1315 }
1316 } else {
1317 if (runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION) {
1318 SW_UNHOOK_FUNC(stream_select);
1319 SW_UNHOOK_FUNC(stream_socket_pair);
1320 }
1321 }
1322 // file
1323 if (flags & PHPCoroutine::HOOK_FILE) {
1324 if (!(runtime_hook_flags & PHPCoroutine::HOOK_FILE)) {
1325 memcpy((void *) &php_plain_files_wrapper, &sw_php_plain_files_wrapper, sizeof(php_plain_files_wrapper));
1326 }
1327 } else {
1328 if (runtime_hook_flags & PHPCoroutine::HOOK_FILE) {
1329 memcpy((void *) &php_plain_files_wrapper, &ori_php_plain_files_wrapper, sizeof(php_plain_files_wrapper));
1330 }
1331 }
1332 // stdio
1333 if (flags & PHPCoroutine::HOOK_STDIO) {
1334 if (!(runtime_hook_flags & PHPCoroutine::HOOK_STDIO)) {
1335 memcpy((void *) &php_stream_stdio_ops, &sw_php_stream_stdio_ops, sizeof(php_stream_stdio_ops));
1336 }
1337 } else {
1338 if (runtime_hook_flags & PHPCoroutine::HOOK_STDIO) {
1339 memcpy((void *) &php_stream_stdio_ops, &ori_php_stream_stdio_ops, sizeof(php_stream_stdio_ops));
1340 }
1341 }
1342 // sleep
1343 if (flags & PHPCoroutine::HOOK_SLEEP) {
1344 if (!(runtime_hook_flags & PHPCoroutine::HOOK_SLEEP)) {
1345 SW_HOOK_FUNC(sleep);
1346 SW_HOOK_FUNC(usleep);
1347 SW_HOOK_FUNC(time_nanosleep);
1348 SW_HOOK_FUNC(time_sleep_until);
1349 }
1350 } else {
1351 if (runtime_hook_flags & PHPCoroutine::HOOK_SLEEP) {
1352 SW_UNHOOK_FUNC(sleep);
1353 SW_UNHOOK_FUNC(usleep);
1354 SW_UNHOOK_FUNC(time_nanosleep);
1355 SW_UNHOOK_FUNC(time_sleep_until);
1356 }
1357 }
1358 // proc_open
1359 if (flags & PHPCoroutine::HOOK_PROC) {
1360 if (!(runtime_hook_flags & PHPCoroutine::HOOK_PROC)) {
1361 SW_HOOK_FUNC(proc_open);
1362 SW_HOOK_FUNC(proc_close);
1363 SW_HOOK_FUNC(proc_get_status);
1364 SW_HOOK_FUNC(proc_terminate);
1365 }
1366 } else {
1367 if (runtime_hook_flags & PHPCoroutine::HOOK_PROC) {
1368 SW_UNHOOK_FUNC(proc_open);
1369 SW_UNHOOK_FUNC(proc_close);
1370 SW_UNHOOK_FUNC(proc_get_status);
1371 SW_UNHOOK_FUNC(proc_terminate);
1372 }
1373 }
1374 // blocking function
1375 if (flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) {
1376 if (!(runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION)) {
1377 hook_func(ZEND_STRL("gethostbyname"), PHP_FN(swoole_coroutine_gethostbyname));
1378 hook_func(ZEND_STRL("exec"));
1379 hook_func(ZEND_STRL("shell_exec"));
1380 }
1381 } else {
1382 if (runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) {
1383 SW_UNHOOK_FUNC(gethostbyname);
1384 SW_UNHOOK_FUNC(exec);
1385 SW_UNHOOK_FUNC(shell_exec);
1386 }
1387 }
1388 if (flags & PHPCoroutine::HOOK_SOCKETS) {
1389 if (!(runtime_hook_flags & PHPCoroutine::HOOK_SOCKETS)) {
1390 SW_HOOK_SOCKETS_FUNC(socket_create);
1391 SW_HOOK_SOCKETS_FUNC(socket_create_listen);
1392 SW_HOOK_SOCKETS_FUNC(socket_create_pair);
1393 SW_HOOK_SOCKETS_FUNC(socket_connect);
1394 SW_HOOK_SOCKETS_FUNC(socket_write);
1395 SW_HOOK_SOCKETS_FUNC(socket_read);
1396 SW_HOOK_SOCKETS_FUNC(socket_send);
1397 SW_HOOK_SOCKETS_FUNC(socket_recv);
1398 SW_HOOK_SOCKETS_FUNC(socket_sendto);
1399 SW_HOOK_SOCKETS_FUNC(socket_recvfrom);
1400 SW_HOOK_SOCKETS_FUNC(socket_bind);
1401 SW_HOOK_SOCKETS_FUNC(socket_listen);
1402 SW_HOOK_SOCKETS_FUNC(socket_accept);
1403 SW_HOOK_SOCKETS_FUNC(socket_getpeername);
1404 SW_HOOK_SOCKETS_FUNC(socket_getsockname);
1405 SW_HOOK_SOCKETS_FUNC(socket_getopt);
1406 SW_HOOK_SOCKETS_FUNC(socket_get_option);
1407 SW_HOOK_SOCKETS_FUNC(socket_setopt);
1408 SW_HOOK_SOCKETS_FUNC(socket_set_option);
1409 SW_HOOK_SOCKETS_FUNC(socket_set_block);
1410 SW_HOOK_SOCKETS_FUNC(socket_set_nonblock);
1411 SW_HOOK_SOCKETS_FUNC(socket_shutdown);
1412 SW_HOOK_SOCKETS_FUNC(socket_close);
1413 SW_HOOK_SOCKETS_FUNC(socket_clear_error);
1414 SW_HOOK_SOCKETS_FUNC(socket_last_error);
1415 }
1416 } else {
1417 if (runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) {
1418 SW_UNHOOK_FUNC(socket_create);
1419 SW_UNHOOK_FUNC(socket_create_listen);
1420 SW_UNHOOK_FUNC(socket_create_pair);
1421 SW_UNHOOK_FUNC(socket_connect);
1422 SW_UNHOOK_FUNC(socket_write);
1423 SW_UNHOOK_FUNC(socket_read);
1424 SW_UNHOOK_FUNC(socket_send);
1425 SW_UNHOOK_FUNC(socket_recv);
1426 SW_UNHOOK_FUNC(socket_sendto);
1427 SW_UNHOOK_FUNC(socket_recvfrom);
1428 SW_UNHOOK_FUNC(socket_bind);
1429 SW_UNHOOK_FUNC(socket_listen);
1430 SW_UNHOOK_FUNC(socket_accept);
1431 SW_UNHOOK_FUNC(socket_getpeername);
1432 SW_UNHOOK_FUNC(socket_getsockname);
1433 SW_UNHOOK_FUNC(socket_getopt);
1434 SW_UNHOOK_FUNC(socket_get_option);
1435 SW_UNHOOK_FUNC(socket_setopt);
1436 SW_UNHOOK_FUNC(socket_set_option);
1437 SW_UNHOOK_FUNC(socket_set_block);
1438 SW_UNHOOK_FUNC(socket_set_nonblock);
1439 SW_UNHOOK_FUNC(socket_shutdown);
1440 SW_UNHOOK_FUNC(socket_close);
1441 SW_UNHOOK_FUNC(socket_clear_error);
1442 SW_UNHOOK_FUNC(socket_last_error);
1443 }
1444 }
1445
1446 #ifdef SW_USE_CURL
1447 if (flags & PHPCoroutine::HOOK_NATIVE_CURL) {
1448 if (flags & PHPCoroutine::HOOK_CURL) {
1449 php_swoole_fatal_error(E_WARNING, "cannot enable both hooks HOOK_NATIVE_CURL and HOOK_CURL at same time");
1450 flags ^= PHPCoroutine::HOOK_CURL;
1451 }
1452 if (!(runtime_hook_flags & PHPCoroutine::HOOK_NATIVE_CURL)) {
1453 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_close);
1454 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_copy_handle);
1455 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_errno);
1456 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_error);
1457 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_exec);
1458 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_getinfo);
1459 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_init);
1460 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_setopt);
1461 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_setopt_array);
1462 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_reset);
1463 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_pause);
1464 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_escape);
1465 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_unescape);
1466
1467 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_init);
1468 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_add_handle);
1469 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_exec);
1470 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_errno);
1471 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_select);
1472 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_setopt);
1473 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_getcontent);
1474 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_info_read);
1475 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_remove_handle);
1476 SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_close);
1477 }
1478 } else {
1479 if (runtime_hook_flags & PHPCoroutine::HOOK_NATIVE_CURL) {
1480 SW_UNHOOK_FUNC(curl_close);
1481 SW_UNHOOK_FUNC(curl_copy_handle);
1482 SW_UNHOOK_FUNC(curl_errno);
1483 SW_UNHOOK_FUNC(curl_error);
1484 SW_UNHOOK_FUNC(curl_exec);
1485 SW_UNHOOK_FUNC(curl_getinfo);
1486 SW_UNHOOK_FUNC(curl_init);
1487 SW_UNHOOK_FUNC(curl_setopt);
1488 SW_UNHOOK_FUNC(curl_setopt_array);
1489 SW_UNHOOK_FUNC(curl_reset);
1490 SW_UNHOOK_FUNC(curl_pause);
1491 SW_UNHOOK_FUNC(curl_escape);
1492 SW_UNHOOK_FUNC(curl_unescape);
1493
1494 SW_UNHOOK_FUNC(curl_multi_init);
1495 SW_UNHOOK_FUNC(curl_multi_add_handle);
1496 SW_UNHOOK_FUNC(curl_multi_exec);
1497 SW_UNHOOK_FUNC(curl_multi_errno);
1498 SW_UNHOOK_FUNC(curl_multi_select);
1499 SW_UNHOOK_FUNC(curl_multi_setopt);
1500 SW_UNHOOK_FUNC(curl_multi_getcontent);
1501 SW_UNHOOK_FUNC(curl_multi_info_read);
1502 SW_UNHOOK_FUNC(curl_multi_remove_handle);
1503 SW_UNHOOK_FUNC(curl_multi_close);
1504 }
1505 }
1506 #endif
1507
1508 if (flags & PHPCoroutine::HOOK_CURL) {
1509 if (!(runtime_hook_flags & PHPCoroutine::HOOK_CURL)) {
1510 hook_func(ZEND_STRL("curl_init"));
1511 hook_func(ZEND_STRL("curl_setopt"));
1512 hook_func(ZEND_STRL("curl_setopt_array"));
1513 hook_func(ZEND_STRL("curl_exec"));
1514 hook_func(ZEND_STRL("curl_getinfo"));
1515 hook_func(ZEND_STRL("curl_errno"));
1516 hook_func(ZEND_STRL("curl_error"));
1517 hook_func(ZEND_STRL("curl_reset"));
1518 hook_func(ZEND_STRL("curl_close"));
1519 hook_func(ZEND_STRL("curl_multi_getcontent"));
1520 }
1521 } else {
1522 if (runtime_hook_flags & PHPCoroutine::HOOK_CURL) {
1523 SW_UNHOOK_FUNC(curl_init);
1524 SW_UNHOOK_FUNC(curl_setopt);
1525 SW_UNHOOK_FUNC(curl_setopt_array);
1526 SW_UNHOOK_FUNC(curl_exec);
1527 SW_UNHOOK_FUNC(curl_getinfo);
1528 SW_UNHOOK_FUNC(curl_errno);
1529 SW_UNHOOK_FUNC(curl_error);
1530 SW_UNHOOK_FUNC(curl_reset);
1531 SW_UNHOOK_FUNC(curl_close);
1532 SW_UNHOOK_FUNC(curl_multi_getcontent);
1533 }
1534 }
1535
1536 if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK)) {
1537 swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK, &flags);
1538 }
1539
1540 runtime_hook_flags = flags;
1541 return true;
1542 }
1543
disable_hook()1544 bool PHPCoroutine::disable_hook() {
1545 return enable_hook(0);
1546 }
1547
PHP_METHOD(swoole_runtime,enableCoroutine)1548 static PHP_METHOD(swoole_runtime, enableCoroutine) {
1549 if (!SWOOLE_G(cli)) {
1550 php_swoole_fatal_error(E_ERROR, "must be used in PHP CLI mode");
1551 RETURN_FALSE;
1552 }
1553 zval *zflags = nullptr;
1554 zend_long flags = PHPCoroutine::HOOK_ALL;
1555
1556 ZEND_PARSE_PARAMETERS_START(0, 2)
1557 Z_PARAM_OPTIONAL
1558 Z_PARAM_ZVAL(zflags) // or zenable
1559 Z_PARAM_LONG(flags)
1560 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1561
1562 if (zflags) {
1563 if (Z_TYPE_P(zflags) == IS_LONG) {
1564 flags = SW_MAX(0, Z_LVAL_P(zflags));
1565 } else if (ZVAL_IS_BOOL(zflags)) {
1566 if (!Z_BVAL_P(zflags)) {
1567 flags = 0;
1568 }
1569 } else {
1570 const char *space, *class_name = get_active_class_name(&space);
1571 zend_type_error("%s%s%s() expects parameter %d to be %s, %s given",
1572 class_name,
1573 space,
1574 get_active_function_name(),
1575 1,
1576 "bool or long",
1577 zend_zval_type_name(zflags));
1578 }
1579 }
1580
1581 PHPCoroutine::set_hook_flags(flags);
1582 RETURN_BOOL(PHPCoroutine::enable_hook(flags));
1583 }
1584
PHP_METHOD(swoole_runtime,getHookFlags)1585 static PHP_METHOD(swoole_runtime, getHookFlags) {
1586 if (runtime_hook_init) {
1587 RETURN_LONG(runtime_hook_flags);
1588 } else {
1589 RETURN_LONG(PHPCoroutine::get_hook_flags());
1590 }
1591 }
1592
PHP_METHOD(swoole_runtime,setHookFlags)1593 static PHP_METHOD(swoole_runtime, setHookFlags) {
1594 if (!SWOOLE_G(cli)) {
1595 php_swoole_fatal_error(E_ERROR, "must be used in PHP CLI mode");
1596 RETURN_FALSE;
1597 }
1598 zend_long flags = PHPCoroutine::HOOK_ALL;
1599
1600 ZEND_PARSE_PARAMETERS_START(1, 1)
1601 Z_PARAM_LONG(flags)
1602 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1603
1604 PHPCoroutine::set_hook_flags(flags);
1605 RETURN_BOOL(PHPCoroutine::enable_hook(flags));
1606 }
1607
PHP_FUNCTION(swoole_sleep)1608 static PHP_FUNCTION(swoole_sleep) {
1609 zend_long num;
1610 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num) == FAILURE) {
1611 RETURN_FALSE;
1612 }
1613 if (num < 0) {
1614 php_error_docref(nullptr, E_WARNING, "Number of seconds must be greater than or equal to 0");
1615 RETURN_FALSE;
1616 }
1617
1618 if (Coroutine::get_current()) {
1619 RETURN_LONG(System::sleep((double) num) < 0 ? num : 0);
1620 } else {
1621 RETURN_LONG(php_sleep(num));
1622 }
1623 }
1624
PHP_FUNCTION(swoole_usleep)1625 static PHP_FUNCTION(swoole_usleep) {
1626 zend_long num;
1627 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num) == FAILURE) {
1628 RETURN_FALSE;
1629 }
1630 if (num < 0) {
1631 php_error_docref(nullptr, E_WARNING, "Number of seconds must be greater than or equal to 0");
1632 RETURN_FALSE;
1633 }
1634 double sec = (double) num / 1000000;
1635 if (Coroutine::get_current()) {
1636 System::sleep(sec);
1637 } else {
1638 usleep((unsigned int) num);
1639 }
1640 }
1641
PHP_FUNCTION(swoole_time_nanosleep)1642 static PHP_FUNCTION(swoole_time_nanosleep) {
1643 zend_long tv_sec, tv_nsec;
1644 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &tv_sec, &tv_nsec) == FAILURE) {
1645 RETURN_FALSE;
1646 }
1647
1648 if (tv_sec < 0) {
1649 php_error_docref(nullptr, E_WARNING, "The seconds value must be greater than 0");
1650 RETURN_FALSE;
1651 }
1652 if (tv_nsec < 0) {
1653 php_error_docref(nullptr, E_WARNING, "The nanoseconds value must be greater than 0");
1654 RETURN_FALSE;
1655 }
1656 double _time = (double) tv_sec + (double) tv_nsec / 1000000000.00;
1657 if (Coroutine::get_current()) {
1658 System::sleep(_time);
1659 } else {
1660 struct timespec php_req, php_rem;
1661 php_req.tv_sec = (time_t) tv_sec;
1662 php_req.tv_nsec = (long) tv_nsec;
1663
1664 if (nanosleep(&php_req, &php_rem) == 0) {
1665 RETURN_TRUE;
1666 } else if (errno == EINTR) {
1667 array_init(return_value);
1668 add_assoc_long_ex(return_value, "seconds", sizeof("seconds") - 1, php_rem.tv_sec);
1669 add_assoc_long_ex(return_value, "nanoseconds", sizeof("nanoseconds") - 1, php_rem.tv_nsec);
1670 } else if (errno == EINVAL) {
1671 php_swoole_error(E_WARNING, "nanoseconds was not in the range 0 to 999 999 999 or seconds was negative");
1672 }
1673 }
1674 RETURN_TRUE;
1675 }
1676
PHP_FUNCTION(swoole_time_sleep_until)1677 static PHP_FUNCTION(swoole_time_sleep_until) {
1678 double d_ts, c_ts;
1679 struct timeval tm;
1680 struct timespec php_req, php_rem;
1681
1682 if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &d_ts) == FAILURE) {
1683 RETURN_FALSE;
1684 }
1685
1686 if (gettimeofday((struct timeval *) &tm, nullptr) != 0) {
1687 RETURN_FALSE;
1688 }
1689
1690 c_ts = (double) (d_ts - tm.tv_sec - tm.tv_usec / 1000000.00);
1691 if (c_ts < 0) {
1692 php_error_docref(nullptr, E_WARNING, "Sleep until to time is less than current time");
1693 RETURN_FALSE;
1694 }
1695
1696 php_req.tv_sec = (time_t) c_ts;
1697 if (php_req.tv_sec > c_ts) {
1698 php_req.tv_sec--;
1699 }
1700 php_req.tv_nsec = (long) ((c_ts - php_req.tv_sec) * 1000000000.00);
1701
1702 double _time = (double) php_req.tv_sec + (double) php_req.tv_nsec / 1000000000.00;
1703 if (Coroutine::get_current()) {
1704 System::sleep(_time);
1705 } else {
1706 while (nanosleep(&php_req, &php_rem)) {
1707 if (errno == EINTR) {
1708 php_req.tv_sec = php_rem.tv_sec;
1709 php_req.tv_nsec = php_rem.tv_nsec;
1710 } else {
1711 RETURN_FALSE;
1712 }
1713 }
1714 }
1715 RETURN_TRUE;
1716 }
1717
stream_array_to_fd_set(zval * stream_array,std::unordered_map<int,PollSocket> & fds,int event)1718 static void stream_array_to_fd_set(zval *stream_array, std::unordered_map<int, PollSocket> &fds, int event) {
1719 zval *elem;
1720 zend_ulong index;
1721 zend_string *key;
1722 php_socket_t sock;
1723
1724 if (!ZVAL_IS_ARRAY(stream_array)) {
1725 return;
1726 }
1727
1728 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), index, key, elem) {
1729 ZVAL_DEREF(elem);
1730 sock = php_swoole_convert_to_fd(elem);
1731 if (sock < 0) {
1732 continue;
1733 }
1734 auto i = fds.find(sock);
1735 if (i == fds.end()) {
1736 fds.emplace(std::make_pair(sock, PollSocket(event, new zend::KeyValue(index, key, elem))));
1737 } else {
1738 i->second.events |= event;
1739 }
1740 }
1741 ZEND_HASH_FOREACH_END();
1742 }
1743
stream_array_emulate_read_fd_set(zval * stream_array)1744 static int stream_array_emulate_read_fd_set(zval *stream_array) {
1745 zval *elem, *dest_elem, new_array;
1746 HashTable *ht;
1747 php_stream *stream;
1748 int ret = 0;
1749 zend_ulong num_ind;
1750 zend_string *key;
1751
1752 if (!ZVAL_IS_ARRAY(stream_array)) {
1753 return 0;
1754 }
1755
1756 array_init_size(&new_array, zend_hash_num_elements(Z_ARRVAL_P(stream_array)));
1757 ht = Z_ARRVAL(new_array);
1758
1759 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), num_ind, key, elem) {
1760 ZVAL_DEREF(elem);
1761 php_stream_from_zval_no_verify(stream, elem);
1762 if (stream == nullptr) {
1763 continue;
1764 }
1765 if ((stream->writepos - stream->readpos) > 0) {
1766 /* allow readable non-descriptor based streams to participate in stream_select.
1767 * Non-descriptor streams will only "work" if they have previously buffered the
1768 * data. Not ideal, but better than nothing.
1769 * This branch of code also allows blocking streams with buffered data to
1770 * operate correctly in stream_select.
1771 * */
1772 dest_elem = !key ? zend_hash_index_update(ht, num_ind, elem) : zend_hash_update(ht, key, elem);
1773 zval_add_ref(dest_elem);
1774 ret++;
1775 continue;
1776 }
1777 }
1778 ZEND_HASH_FOREACH_END();
1779
1780 if (ret > 0) {
1781 /* destroy old array and add new one */
1782 zend_array_destroy(Z_ARR_P(stream_array));
1783 ZVAL_ARR(stream_array, ht);
1784 } else {
1785 zend_array_destroy(ht);
1786 }
1787
1788 return ret;
1789 }
1790
PHP_FUNCTION(swoole_stream_select)1791 static PHP_FUNCTION(swoole_stream_select) {
1792 Coroutine::get_current_safe();
1793
1794 zval *r_array, *w_array, *e_array;
1795 zend_long sec, usec = 0;
1796 zend_bool secnull;
1797 int retval = 0;
1798
1799 ZEND_PARSE_PARAMETERS_START(4, 5)
1800 Z_PARAM_ARRAY_EX(r_array, 1, 1)
1801 Z_PARAM_ARRAY_EX(w_array, 1, 1)
1802 Z_PARAM_ARRAY_EX(e_array, 1, 1)
1803 Z_PARAM_LONG_EX(sec, secnull, 1, 0)
1804 Z_PARAM_OPTIONAL
1805 Z_PARAM_LONG(usec)
1806 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1807
1808 std::unordered_map<int, PollSocket> fds;
1809
1810 if (r_array != nullptr) {
1811 stream_array_to_fd_set(r_array, fds, SW_EVENT_READ);
1812 }
1813
1814 if (w_array != nullptr) {
1815 stream_array_to_fd_set(w_array, fds, SW_EVENT_WRITE);
1816 }
1817
1818 if (e_array != nullptr) {
1819 stream_array_to_fd_set(e_array, fds, SW_EVENT_ERROR);
1820 }
1821
1822 if (fds.size() == 0) {
1823 php_error_docref(nullptr, E_WARNING, "No stream arrays were passed");
1824 RETURN_FALSE;
1825 }
1826
1827 double timeout = -1;
1828 if (!secnull) {
1829 if (sec < 0) {
1830 php_error_docref(nullptr, E_WARNING, "The seconds parameter must be greater than 0");
1831 RETURN_FALSE;
1832 } else if (usec < 0) {
1833 php_error_docref(nullptr, E_WARNING, "The microseconds parameter must be greater than 0");
1834 RETURN_FALSE;
1835 }
1836 timeout = (double) sec + ((double) usec / 1000000);
1837 }
1838
1839 /* slight hack to support buffered data; if there is data sitting in the
1840 * read buffer of any of the streams in the read array, let's pretend
1841 * that we selected, but return only the readable sockets */
1842 if (r_array != nullptr) {
1843 retval = stream_array_emulate_read_fd_set(r_array);
1844 if (retval > 0) {
1845 if (w_array != nullptr) {
1846 zend_hash_clean(Z_ARRVAL_P(w_array));
1847 }
1848 if (e_array != nullptr) {
1849 zend_hash_clean(Z_ARRVAL_P(e_array));
1850 }
1851 RETURN_LONG(retval);
1852 }
1853 }
1854
1855 if (r_array != nullptr) {
1856 zend_hash_clean(Z_ARRVAL_P(r_array));
1857 }
1858 if (w_array != nullptr) {
1859 zend_hash_clean(Z_ARRVAL_P(w_array));
1860 }
1861 if (e_array != nullptr) {
1862 zend_hash_clean(Z_ARRVAL_P(e_array));
1863 }
1864
1865 /**
1866 * timeout or add failed
1867 */
1868 if (!System::socket_poll(fds, timeout)) {
1869 RETURN_LONG(0);
1870 }
1871
1872 for (auto &i : fds) {
1873 zend::KeyValue *kv = (zend::KeyValue *) i.second.ptr;
1874 int revents = i.second.revents;
1875 SW_ASSERT((revents & (~(SW_EVENT_READ | SW_EVENT_WRITE | SW_EVENT_ERROR))) == 0);
1876 if (revents > 0) {
1877 if ((revents & SW_EVENT_READ) && r_array) {
1878 kv->add_to(r_array);
1879 }
1880 if ((revents & SW_EVENT_WRITE) && w_array) {
1881 kv->add_to(w_array);
1882 }
1883 if ((revents & SW_EVENT_ERROR) && e_array) {
1884 kv->add_to(e_array);
1885 }
1886 retval++;
1887 }
1888 delete kv;
1889 }
1890
1891 RETURN_LONG(retval);
1892 }
1893
hook_func(const char * name,size_t l_name,zif_handler handler,zend_internal_arg_info * arg_info)1894 static void hook_func(const char *name, size_t l_name, zif_handler handler, zend_internal_arg_info *arg_info) {
1895 real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, l_name);
1896 bool use_php_func = false;
1897 /**
1898 * use php library function
1899 */
1900 if (handler == nullptr) {
1901 handler = PHP_FN(swoole_user_func_handler);
1902 use_php_func = true;
1903 }
1904 if (rf) {
1905 rf->function->internal_function.handler = handler;
1906 if (arg_info) {
1907 rf->function->internal_function.arg_info = arg_info;
1908 }
1909 return;
1910 }
1911
1912 zend_function *zf = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, l_name);
1913 if (zf == nullptr) {
1914 return;
1915 }
1916 #if PHP_VERSION_ID < 80000
1917 if (zf->internal_function.handler == ZEND_FN(display_disabled_function)) {
1918 return;
1919 }
1920 #endif
1921
1922 rf = (real_func *) emalloc(sizeof(real_func));
1923 sw_memset_zero(rf, sizeof(*rf));
1924 rf->function = zf;
1925 rf->ori_handler = zf->internal_function.handler;
1926 rf->ori_arg_info = zf->internal_function.arg_info;
1927 zf->internal_function.handler = handler;
1928 if (arg_info) {
1929 zf->internal_function.arg_info = arg_info;
1930 }
1931
1932 if (use_php_func) {
1933 char func[128];
1934 memcpy(func, ZEND_STRL("swoole_"));
1935 memcpy(func + 7, zf->common.function_name->val, zf->common.function_name->len);
1936
1937 ZVAL_STRINGL(&rf->name, func, zf->common.function_name->len + 7);
1938
1939 char *func_name;
1940 zend_fcall_info_cache *func_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache));
1941 if (!sw_zend_is_callable_ex(&rf->name, nullptr, 0, &func_name, nullptr, func_cache, nullptr)) {
1942 php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name);
1943 return;
1944 }
1945 efree(func_name);
1946 rf->fci_cache = func_cache;
1947 }
1948
1949 zend_hash_add_ptr(tmp_function_table, zf->common.function_name, rf);
1950 }
1951
unhook_func(const char * name,size_t l_name)1952 static void unhook_func(const char *name, size_t l_name) {
1953 real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, l_name);
1954 if (rf == nullptr) {
1955 return;
1956 }
1957 rf->function->internal_function.handler = rf->ori_handler;
1958 rf->function->internal_function.arg_info = rf->ori_arg_info;
1959 }
1960
php_swoole_create_stream_from_socket(php_socket_t _fd,int domain,int type,int protocol STREAMS_DC)1961 php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, int type, int protocol STREAMS_DC) {
1962 Socket *sock = new Socket(_fd, domain, type, protocol);
1963
1964 if (FG(default_socket_timeout) > 0) {
1965 sock->set_timeout((double) FG(default_socket_timeout));
1966 }
1967
1968 php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) ecalloc(1, sizeof(*abstract));
1969
1970 abstract->socket = sock;
1971 abstract->stream.timeout.tv_sec = FG(default_socket_timeout);
1972 abstract->stream.socket = sock->get_fd();
1973 abstract->blocking = true;
1974
1975 php_stream *stream = php_stream_alloc_rel(&socket_ops, abstract, nullptr, "r+");
1976
1977 if (stream == nullptr) {
1978 delete sock;
1979 } else {
1980 stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING;
1981 }
1982
1983 return stream;
1984 }
1985
php_swoole_get_ori_php_stream_stdio_ops()1986 php_stream_ops *php_swoole_get_ori_php_stream_stdio_ops() {
1987 return &ori_php_stream_stdio_ops;
1988 }
1989
PHP_FUNCTION(swoole_stream_socket_pair)1990 static PHP_FUNCTION(swoole_stream_socket_pair) {
1991 zend_long domain, type, protocol;
1992 php_stream *s1, *s2;
1993 php_socket_t pair[2];
1994
1995 ZEND_PARSE_PARAMETERS_START(3, 3)
1996 Z_PARAM_LONG(domain)
1997 Z_PARAM_LONG(type)
1998 Z_PARAM_LONG(protocol)
1999 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2000
2001 if (0 != socketpair((int) domain, (int) type, (int) protocol, pair)) {
2002 php_swoole_error(E_WARNING, "failed to create sockets: [%d]: %s", errno, strerror(errno));
2003 RETURN_FALSE;
2004 }
2005
2006 array_init(return_value);
2007
2008 php_swoole_check_reactor();
2009
2010 s1 = php_swoole_create_stream_from_socket(pair[0], domain, type, protocol STREAMS_CC);
2011 s2 = php_swoole_create_stream_from_socket(pair[1], domain, type, protocol STREAMS_CC);
2012
2013 /* set the __exposed flag.
2014 * php_stream_to_zval() does, add_next_index_resource() does not */
2015 php_stream_auto_cleanup(s1);
2016 php_stream_auto_cleanup(s2);
2017
2018 add_next_index_resource(return_value, s1->res);
2019 add_next_index_resource(return_value, s2->res);
2020 }
2021
PHP_FUNCTION(swoole_user_func_handler)2022 static PHP_FUNCTION(swoole_user_func_handler) {
2023 zend_fcall_info fci;
2024 fci.size = sizeof(fci);
2025 fci.object = nullptr;
2026 ZVAL_UNDEF(&fci.function_name);
2027 fci.retval = return_value;
2028 fci.param_count = ZEND_NUM_ARGS();
2029 fci.params = ZEND_CALL_ARG(execute_data, 1);
2030 #if PHP_VERSION_ID >= 80000
2031 fci.named_params = NULL;
2032 #else
2033 fci.no_separation = 1;
2034 #endif
2035
2036 real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, execute_data->func->common.function_name);
2037 zend_call_function(&fci, rf->fci_cache);
2038 }
2039