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(&param, 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, &param);
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(&param, 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, &param);
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