Skip to content

Commit

Permalink
Merge pull request #15 from ycgambo/dev
Browse files Browse the repository at this point in the history
Manager Improvements
  • Loading branch information
ycgambo authored May 15, 2018
2 parents a71bd79 + d626e83 commit f20b7f6
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 30 deletions.
9 changes: 7 additions & 2 deletions src/shadowrocket/Bin/Launcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ class Launcher
/* 2nd */
array('guarder'),
/* 3rd */
array('server', 'local', 'manager'),
array('server', 'local'),
/* 4th */
array('manager'),
);

/**
Expand Down Expand Up @@ -370,8 +372,11 @@ public static function isModuleReady($module_name)
return false;
}

public static function getModule($module_name)
public static function getModule($module_name = '')
{
if (empty($module_name)) {
return self::$_modules;
}
$module_name = strtolower($module_name);
return isset(self::$_modules[$module_name]) ? self::$_modules[$module_name] : null;
}
Expand Down
24 changes: 24 additions & 0 deletions src/shadowrocket/Exception/ConfigException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
/**
* This file is part of shadowrocket.
*
* @file ConfigException.php
* @author ycgambo
* @update 5/15/18 8:25 AM
* @copyright shadowrocket <https://github.com/ycgambo/shadowrocket>
* @license MIT License <http://www.opensource.org/licenses/mit-license.html>
*/

/**
* Author: ycgambo
* Date: 15/5/2018
* Time: 8:25 AM
*/

namespace ShadowRocket\Exception;


class ConfigException extends \UnexpectedValueException
{

}
20 changes: 20 additions & 0 deletions src/shadowrocket/Module/Base/ManagerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
/**
* This file is part of shadowrocket.
*
* @file ManagerInterface.php
* @author ycgambo
* @update 5/2/18 9:32 AM
* @copyright shadowrocket <https://github.com/ycgambo/shadowrocket>
* @license MIT License <http://www.opensource.org/licenses/mit-license.html>
*/

namespace ShadowRocket\Module\Base;

interface ManagerInterface
{

public function preBootCommands();

public function denyCommand($command, $full_command = '');
}
3 changes: 2 additions & 1 deletion src/shadowrocket/Module/Guarder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace ShadowRocket\Module;


use ShadowRocket\Exception\ConfigException;
use ShadowRocket\Module\Base\ConfigRequired;
use ShadowRocket\Module\Base\GuarderInterface;
use ShadowRocket\Module\Base\LauncherModuleInterface;
Expand All @@ -30,7 +31,7 @@ public function getReady()
$instance = $this->getConfig('instance');

if (!$instance instanceof GuarderInterface) {
throw new \Exception('A Guarder should implements ShadowRocket\Module\Base\GuarderInterface');
throw new ConfigException('A Guarder should implements ShadowRocket\Module\Base\GuarderInterface');
}
}

Expand Down
16 changes: 12 additions & 4 deletions src/shadowrocket/Module/Helper/ManagerCommandParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ public function __construct()
$getopt->setHelp(new CustomGetOptHelp());
$getopt->addCommands(array(
Command::create('server:add', '')
->setShortDescription('Add server on one or more port.')
->setDescription('Create server named as prefix_port on each port. ')
->setShortDescription('Add server on a port.')
->setDescription('Create server named as prefix_port. ')
->addOperands(array(
Operand::create('password', Operand::REQUIRED),
Operand::create('ports', Operand::MULTIPLE + Operand::REQUIRED),
Operand::create('port', Operand::REQUIRED),
))
->addOptions(array(
Option::create('n', 'name', GetOpt::OPTIONAL_ARGUMENT)
Expand All @@ -43,7 +43,15 @@ public function __construct()
->setDescription('process number.')
->setDefaultValue(4),
)),
Command::create('server:del', ''),
Command::create('server:del', '')
->setShortDescription('Delete added server according to their name.')
->addOperands(array(
Operand::create('names', Operand::MULTIPLE + Operand::REQUIRED),
)),
Command::create('server:list', '')
->setShortDescription('Simple server list.'),
Command::create('server:detail', '')
->setShortDescription('List server detail.'),
));

$this->_getopt = $getopt;
Expand Down
3 changes: 2 additions & 1 deletion src/shadowrocket/Module/Logger.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Monolog\Handler\HandlerInterface;
use Monolog\Registry;
use ShadowRocket\Exception\ConfigException;
use ShadowRocket\Module\Base\ConfigRequired;
use ShadowRocket\Module\Base\LauncherModuleInterface;

Expand All @@ -33,7 +34,7 @@ public function getReady()
$handlers = $this->getConfig('handlers');
foreach ($handlers as $handler) {
if (!($handler instanceof HandlerInterface)) {
throw new \Exception(
throw new ConfigException(
'Logger handlers should be an instance array of Monolog\Handler\HandlerInterface.'
);
}
Expand Down
134 changes: 114 additions & 20 deletions src/shadowrocket/Module/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,52 +12,74 @@
namespace ShadowRocket\Module;

use ShadowRocket\Bin\Launcher;
use ShadowRocket\Exception\ConfigException;
use ShadowRocket\Module\Base\ConfigRequired;
use ShadowRocket\Module\Base\LauncherModuleInterface;
use ShadowRocket\Module\Base\ManagerInterface;
use ShadowRocket\Module\Helper\CustomGetOptHelp;
use ShadowRocket\Module\Helper\ManagerCommandParser;
use ShadowRocket\Net\Connection;
use Workerman\Worker;
use GetOpt\ArgumentException;

class Manager extends ConfigRequired implements LauncherModuleInterface
class Manager extends ConfigRequired implements LauncherModuleInterface, ManagerInterface
{
protected static $servers = array();

public function init()
{
$this->declareRequiredConfig(array(
'port',
'token',
'process_num' => 1,
'instance' => new self(),
));
}

public function getReady()
{
$instance = $this->getConfig('instance');

if (!$instance instanceof ManagerInterface) {
throw new ConfigException('A Manager should implements ShadowRocket\Module\Base\ManagerInterface');
}

$this->preBoot();

$this->listen();
}

protected function listen()
{
$config = $this->getConfig();

$worker = new Worker('tcp://0.0.0.0:' . $config['port']);
$worker->count = $config['process_num'];
$worker->count = 1;
$worker->name = 'shadowsocks-manager';

$worker->onConnect = function ($client) use ($config) {
$client->stage = Connection::STAGE_INIT;
};

$worker->onMessage = function ($client, $buffer) use ($config) {
$parser = new ManagerCommandParser();
$manager = $this;

$worker->onMessage = function ($client, $buffer) use ($config, $manager) {
switch ($client->stage) {
case Connection::STAGE_INIT:
if ($buffer == $config['token']) {
$parser = new ManagerCommandParser();
$client->stage = Connection::VERIFIED;
$client->send($parser->getHelpText());
}
break;
case Connection::VERIFIED:
$parser = new ManagerCommandParser();
try {
if ($command = $parser->parseCommand($buffer)) {
Manager::handle($command, $parser);
$client->send(PHP_EOL . 'success' . PHP_EOL);
if ($manager->_denyCommand($command, $buffer)) {
$client->send(PHP_EOL . 'Failed: Illegal Command' . PHP_EOL);
} else {
$client->send(PHP_EOL . $manager->handle($command, $parser) . PHP_EOL);
}
} else {
$client->send($parser->getHelpText());
}
Expand All @@ -68,24 +90,96 @@ public function getReady()
};
}

/**
* @param $command
* @param $parser
* @throws \Exception
*/
protected static function handle($command, $parser)
protected function handle($command, $parser)
{
switch ($command) {
case 'server:add':
foreach ($parser->getOperand('ports') as $port) {
Launcher::superaddModule('server', array(
'name' => $parser->getOption('name') . '_' . $port,
'port' => $port,
'password' => $parser->getOperand('password'),
'process_num' => $parser->getOption('process'),
));
$port = $parser->getOperand('port');
Manager::serverAdd(array(
'name' => $parser->getOption('name') . '_' . $port,
'port' => $port,
'password' => $parser->getOperand('password'),
'process_num' => $parser->getOption('process'),
));
return 'Done' . PHP_EOL;
break;
case 'server:del':
foreach ($parser->getOperand('names') as $name) {
Manager::serverDel($name);
}
return 'Done' . PHP_EOL;
break;
case 'server:list':
return Manager::serverList();
break;
case 'server:detail':
return Manager::serverList(true);
break;
}
}

public static function serverAdd(array $config)
{
Launcher::superaddModule('server', $config);
self::$servers[$config['name']] = $config;
}

public static function serverDel($server_name)
{
try {
Launcher::removeModule($server_name);
unset(self::$servers[$server_name]);
} catch (\Exception $e) {
}
}

public static function serverList($detail = false)
{
$rtn = 'Total: ' . count(self::$servers) . PHP_EOL;

foreach (self::$servers as $name => $config) {
if ($detail) {
$rtn .= "{$config['port']}: $name with password {$config['password']}, {$config['process_num']} process" . PHP_EOL;
} else {
$rtn .= "{$config['port']}: $name" . PHP_EOL;
}
}

return $rtn;
}

protected function preBoot()
{
$commands = $this->getConfig('instance')->preBootCommands();
if ($commands && is_array($commands)) {
$parser = new ManagerCommandParser();

foreach ($commands as $buffer) {
try {
if ($command = $parser->parseCommand($buffer)) {
$this->handle($command, $parser);
}
} catch (\Exception $exception) {
throw new ConfigException('Manager preBoot Failed: ' . $exception->getMessage());
}
}
}
}

public function _denyCommand($command, $buffer = '')
{
return $this->getConfig('instance')->denyCommand($command, $buffer);

}

public function preBootCommands()
{
return array();
}

public function denyCommand($command, $full_command = '')
{
return false;
}

}
8 changes: 6 additions & 2 deletions src/shadowrocket/Module/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ protected function createWorker($protocol, $superadd = false)

if ($guarder = Launcher::getModuleIfReady('guarder')) {
if ($guarder->_deny($request, $config['port'])) {
$worker->stop();
if ($manager = Launcher::getModuleIfReady('manager')) {
$manager::serverDel($config['name']);
} else {
$worker->stop();
}
}

if ($guarder->_block($request, $config['port'])) {
Expand All @@ -109,7 +113,7 @@ protected function createWorker($protocol, $superadd = false)
// build tunnel to actual server
$address = "{$protocol}://{$request['dst_addr']}:{$request['dst_port']}";
$remote = ($protocol == 'udp')
? new UdpConnection(socket_create( AF_INET, SOCK_DGRAM, SOL_UDP ), $address)
? new UdpConnection(socket_create(AF_INET, SOCK_DGRAM, SOL_UDP), $address)
: new AsyncTcpConnection($address);

Connection::bind($client, $remote);
Expand Down

0 comments on commit f20b7f6

Please sign in to comment.