|
|
@ -3,51 +3,58 @@ |
|
|
namespace RouterOS; |
|
|
namespace RouterOS; |
|
|
|
|
|
|
|
|
use RouterOS\Exceptions\ClientException; |
|
|
use RouterOS\Exceptions\ClientException; |
|
|
use RouterOS\Interfaces\ClientInterface; |
|
|
|
|
|
use RouterOS\Interfaces\ConfigInterface; |
|
|
|
|
|
use RouterOS\Exceptions\ConfigException; |
|
|
use RouterOS\Exceptions\ConfigException; |
|
|
use RouterOS\Interfaces\QueryInterface; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Class Client for RouterOS management |
|
|
* Class Client for RouterOS management |
|
|
|
|
|
* |
|
|
* @package RouterOS |
|
|
* @package RouterOS |
|
|
* @since 0.1 |
|
|
|
|
|
|
|
|
* @since 0.1 |
|
|
*/ |
|
|
*/ |
|
|
class Client implements Interfaces\ClientInterface |
|
|
class Client implements Interfaces\ClientInterface |
|
|
{ |
|
|
{ |
|
|
/** |
|
|
/** |
|
|
* Socket resource |
|
|
* Socket resource |
|
|
|
|
|
* |
|
|
* @var resource|null |
|
|
* @var resource|null |
|
|
*/ |
|
|
*/ |
|
|
private $_socket; |
|
|
private $_socket; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Code of error |
|
|
* Code of error |
|
|
|
|
|
* |
|
|
* @var int |
|
|
* @var int |
|
|
*/ |
|
|
*/ |
|
|
private $_socket_err_num; |
|
|
private $_socket_err_num; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Description of socket error |
|
|
* Description of socket error |
|
|
|
|
|
* |
|
|
* @var string |
|
|
* @var string |
|
|
*/ |
|
|
*/ |
|
|
private $_socket_err_str; |
|
|
private $_socket_err_str; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Configuration of connection |
|
|
* Configuration of connection |
|
|
* @var ConfigInterface |
|
|
|
|
|
|
|
|
* |
|
|
|
|
|
* @var \RouterOS\Config |
|
|
*/ |
|
|
*/ |
|
|
private $_config; |
|
|
private $_config; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Client constructor. |
|
|
* Client constructor. |
|
|
* |
|
|
* |
|
|
* @param ConfigInterface $config |
|
|
|
|
|
|
|
|
* @param array|\RouterOS\Config $config |
|
|
* @throws ConfigException |
|
|
* @throws ConfigException |
|
|
* @throws ClientException |
|
|
* @throws ClientException |
|
|
*/ |
|
|
*/ |
|
|
public function __construct(ConfigInterface $config) |
|
|
|
|
|
|
|
|
public function __construct($config) |
|
|
{ |
|
|
{ |
|
|
|
|
|
// If array then need create object
|
|
|
|
|
|
if (\is_array($config)) { |
|
|
|
|
|
$config = new Config($config); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Check for important keys
|
|
|
// Check for important keys
|
|
|
$this->exceptionIfKeyNotExist(['host', 'user', 'pass'], $config); |
|
|
$this->exceptionIfKeyNotExist(['host', 'user', 'pass'], $config); |
|
|
|
|
|
|
|
|
@ -63,11 +70,11 @@ class Client implements Interfaces\ClientInterface |
|
|
/** |
|
|
/** |
|
|
* Check for important keys |
|
|
* Check for important keys |
|
|
* |
|
|
* |
|
|
* @param array $keys |
|
|
|
|
|
* @param ConfigInterface $config |
|
|
|
|
|
|
|
|
* @param array $keys |
|
|
|
|
|
* @param Config $config |
|
|
* @throws ConfigException |
|
|
* @throws ConfigException |
|
|
*/ |
|
|
*/ |
|
|
private function exceptionIfKeyNotExist(array $keys, ConfigInterface $config) |
|
|
|
|
|
|
|
|
private function exceptionIfKeyNotExist(array $keys, Config $config) |
|
|
{ |
|
|
{ |
|
|
$parameters = $config->getParameters(); |
|
|
$parameters = $config->getParameters(); |
|
|
foreach ($keys as $key) { |
|
|
foreach ($keys as $key) { |
|
|
@ -80,8 +87,9 @@ class Client implements Interfaces\ClientInterface |
|
|
/** |
|
|
/** |
|
|
* Get some parameter from config |
|
|
* Get some parameter from config |
|
|
* |
|
|
* |
|
|
* @param string $parameter |
|
|
|
|
|
|
|
|
* @param string $parameter Name of required parameter |
|
|
* @return mixed |
|
|
* @return mixed |
|
|
|
|
|
* @throws ConfigException |
|
|
*/ |
|
|
*/ |
|
|
private function config(string $parameter) |
|
|
private function config(string $parameter) |
|
|
{ |
|
|
{ |
|
|
@ -89,6 +97,17 @@ class Client implements Interfaces\ClientInterface |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
|
|
|
* Return socket resource if is exist |
|
|
|
|
|
* |
|
|
|
|
|
* @return \RouterOS\Config |
|
|
|
|
|
* @since 0.6 |
|
|
|
|
|
*/ |
|
|
|
|
|
public function getConfig(): Config |
|
|
|
|
|
{ |
|
|
|
|
|
return $this->_config; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
* Encode given length in RouterOS format |
|
|
* Encode given length in RouterOS format |
|
|
* |
|
|
* |
|
|
* @param string $string |
|
|
* @param string $string |
|
|
@ -101,16 +120,16 @@ class Client implements Interfaces\ClientInterface |
|
|
|
|
|
|
|
|
if ($length < 128) { |
|
|
if ($length < 128) { |
|
|
$orig_length = $length; |
|
|
$orig_length = $length; |
|
|
$offset = -1; |
|
|
|
|
|
|
|
|
$offset = -1; |
|
|
} elseif ($length < 16384) { |
|
|
} elseif ($length < 16384) { |
|
|
$orig_length = $length | 0x8000; |
|
|
$orig_length = $length | 0x8000; |
|
|
$offset = -2; |
|
|
|
|
|
|
|
|
$offset = -2; |
|
|
} elseif ($length < 2097152) { |
|
|
} elseif ($length < 2097152) { |
|
|
$orig_length = $length | 0xC00000; |
|
|
$orig_length = $length | 0xC00000; |
|
|
$offset = -3; |
|
|
|
|
|
|
|
|
$offset = -3; |
|
|
} elseif ($length < 268435456) { |
|
|
} elseif ($length < 268435456) { |
|
|
$orig_length = $length | 0xE0000000; |
|
|
$orig_length = $length | 0xE0000000; |
|
|
$offset = -4; |
|
|
|
|
|
|
|
|
$offset = -4; |
|
|
} else { |
|
|
} else { |
|
|
throw new ClientException("Unable to encode length of '$string'"); |
|
|
throw new ClientException("Unable to encode length of '$string'"); |
|
|
} |
|
|
} |
|
|
@ -173,13 +192,27 @@ class Client implements Interfaces\ClientInterface |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
|
|
|
* Alias for ->write()->read() combination of methods |
|
|
|
|
|
* |
|
|
|
|
|
* @param Query $query |
|
|
|
|
|
* @param bool $parse |
|
|
|
|
|
* @return array |
|
|
|
|
|
* @throws ClientException |
|
|
|
|
|
* @since 0.6 |
|
|
|
|
|
*/ |
|
|
|
|
|
public function wr(Query $query, bool $parse = true): array |
|
|
|
|
|
{ |
|
|
|
|
|
return $this->write($query)->read($parse); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
* Send write query to RouterOS (with or without tag) |
|
|
* Send write query to RouterOS (with or without tag) |
|
|
* |
|
|
* |
|
|
* @param QueryInterface $query |
|
|
|
|
|
* @return ClientInterface |
|
|
|
|
|
|
|
|
* @param Query $query |
|
|
|
|
|
* @return Client |
|
|
* @throws ClientException |
|
|
* @throws ClientException |
|
|
*/ |
|
|
*/ |
|
|
public function write(QueryInterface $query): ClientInterface |
|
|
|
|
|
|
|
|
public function write(Query $query): Client |
|
|
{ |
|
|
{ |
|
|
// Send commands via loop to router
|
|
|
// Send commands via loop to router
|
|
|
foreach ($query->getQuery() as $command) { |
|
|
foreach ($query->getQuery() as $command) { |
|
|
@ -208,7 +241,7 @@ class Client implements Interfaces\ClientInterface |
|
|
while (true) { |
|
|
while (true) { |
|
|
// Read the first byte of input which gives us some or all of the length
|
|
|
// Read the first byte of input which gives us some or all of the length
|
|
|
// of the remaining reply.
|
|
|
// of the remaining reply.
|
|
|
$byte = fread($this->_socket, 1); |
|
|
|
|
|
|
|
|
$byte = fread($this->_socket, 1); |
|
|
$length = $this->getLength(\ord($byte)); |
|
|
$length = $this->getLength(\ord($byte)); |
|
|
|
|
|
|
|
|
// Save only non empty strings
|
|
|
// Save only non empty strings
|
|
|
@ -245,8 +278,8 @@ class Client implements Interfaces\ClientInterface |
|
|
private function parseResponse(array $response): array |
|
|
private function parseResponse(array $response): array |
|
|
{ |
|
|
{ |
|
|
$result = []; |
|
|
$result = []; |
|
|
$i = -1; |
|
|
|
|
|
$lines = \count($response); |
|
|
|
|
|
|
|
|
$i = -1; |
|
|
|
|
|
$lines = \count($response); |
|
|
foreach ($response as $key => $value) { |
|
|
foreach ($response as $key => $value) { |
|
|
switch ($value) { |
|
|
switch ($value) { |
|
|
case '!re': |
|
|
case '!re': |
|
|
@ -283,7 +316,7 @@ class Client implements Interfaces\ClientInterface |
|
|
* Parse result from RouterOS by regular expression |
|
|
* Parse result from RouterOS by regular expression |
|
|
* |
|
|
* |
|
|
* @param string $value |
|
|
* @param string $value |
|
|
* @param array $matches |
|
|
|
|
|
|
|
|
* @param array $matches |
|
|
*/ |
|
|
*/ |
|
|
private function pregResponse(string $value, &$matches) |
|
|
private function pregResponse(string $value, &$matches) |
|
|
{ |
|
|
{ |
|
|
@ -295,20 +328,20 @@ class Client implements Interfaces\ClientInterface |
|
|
* |
|
|
* |
|
|
* @return bool |
|
|
* @return bool |
|
|
* @throws ClientException |
|
|
* @throws ClientException |
|
|
|
|
|
* @throws ConfigException |
|
|
*/ |
|
|
*/ |
|
|
private function login(): bool |
|
|
private function login(): bool |
|
|
{ |
|
|
{ |
|
|
// If legacy login scheme is enabled
|
|
|
// If legacy login scheme is enabled
|
|
|
if ($this->config('legacy')) { |
|
|
if ($this->config('legacy')) { |
|
|
// For the first we need get hash with salt
|
|
|
// For the first we need get hash with salt
|
|
|
$query = new Query('/login'); |
|
|
|
|
|
|
|
|
$query = new Query('/login'); |
|
|
$response = $this->write($query)->read(); |
|
|
$response = $this->write($query)->read(); |
|
|
|
|
|
|
|
|
// Now need use this hash for authorization
|
|
|
// Now need use this hash for authorization
|
|
|
$query = (new Query('/login')) |
|
|
$query = (new Query('/login')) |
|
|
->add('=name=' . $this->config('user')) |
|
|
->add('=name=' . $this->config('user')) |
|
|
->add('=response=00' . md5(\chr(0) . $this->config('pass') . pack('H*', |
|
|
|
|
|
$response['after']['ret']))); |
|
|
|
|
|
|
|
|
->add('=response=00' . md5(\chr(0) . $this->config('pass') . pack('H*', $response['after']['ret']))); |
|
|
} else { |
|
|
} else { |
|
|
// Just login with our credentials
|
|
|
// Just login with our credentials
|
|
|
$query = (new Query('/login')) |
|
|
$query = (new Query('/login')) |
|
|
@ -328,6 +361,7 @@ class Client implements Interfaces\ClientInterface |
|
|
* |
|
|
* |
|
|
* @return bool |
|
|
* @return bool |
|
|
* @throws ClientException |
|
|
* @throws ClientException |
|
|
|
|
|
* @throws ConfigException |
|
|
*/ |
|
|
*/ |
|
|
private function connect(): bool |
|
|
private function connect(): bool |
|
|
{ |
|
|
{ |
|
|
@ -385,14 +419,15 @@ class Client implements Interfaces\ClientInterface |
|
|
* Initiate socket session |
|
|
* Initiate socket session |
|
|
* |
|
|
* |
|
|
* @throws ClientException |
|
|
* @throws ClientException |
|
|
|
|
|
* @throws ConfigException |
|
|
*/ |
|
|
*/ |
|
|
private function openSocket() |
|
|
private function openSocket() |
|
|
{ |
|
|
{ |
|
|
// Default: Context for ssl
|
|
|
// Default: Context for ssl
|
|
|
$context = stream_context_create([ |
|
|
$context = stream_context_create([ |
|
|
'ssl' => [ |
|
|
'ssl' => [ |
|
|
'ciphers' => 'ADH:ALL', |
|
|
|
|
|
'verify_peer' => false, |
|
|
|
|
|
|
|
|
'ciphers' => 'ADH:ALL', |
|
|
|
|
|
'verify_peer' => false, |
|
|
'verify_peer_name' => false |
|
|
'verify_peer_name' => false |
|
|
] |
|
|
] |
|
|
]); |
|
|
]); |
|
|
|