From 679c38d44bfc7fdf8a24a15411a40eec206dbee1 Mon Sep 17 00:00:00 2001 From: Paul Rock Date: Sun, 26 Aug 2018 05:35:48 +0300 Subject: [PATCH] encodeLength method updated --- examples/bridge_hosts.php | 26 +++++++++++++ examples/interface_print.php | 2 +- examples/ip_address_print.php | 6 +-- examples/system_package_print.php | 2 +- src/Client.php | 78 ++++++++++++++++++++++++--------------- 5 files changed, 79 insertions(+), 35 deletions(-) create mode 100644 examples/bridge_hosts.php diff --git a/examples/bridge_hosts.php b/examples/bridge_hosts.php new file mode 100644 index 0000000..0cf2130 --- /dev/null +++ b/examples/bridge_hosts.php @@ -0,0 +1,26 @@ +set('timeout', 1) + ->set('host', '127.0.0.1') + ->set('user', 'admin') + ->set('pass', 'admin'); + +// Initiate client with config object +$client = new Client($config); + +// Build query +$query = new Query('/interface/bridge/host/print'); + +// Send query to RouterOS +$response = $client->write($query)->read(); +print_r($response); diff --git a/examples/interface_print.php b/examples/interface_print.php index 31532f5..355ab16 100644 --- a/examples/interface_print.php +++ b/examples/interface_print.php @@ -10,7 +10,7 @@ use \RouterOS\Query; // Create config object with parameters $config = (new Config()) - ->set('host', '192.168.1.3') + ->set('host', '127.0.0.1') ->set('user', 'admin') ->set('pass', 'admin'); diff --git a/examples/ip_address_print.php b/examples/ip_address_print.php index 3a82373..dccc027 100644 --- a/examples/ip_address_print.php +++ b/examples/ip_address_print.php @@ -10,10 +10,10 @@ use \RouterOS\Query; // Create config object with parameters $config = (new Config()) - ->set('host', '192.168.1.3') + ->set('timeout', 1) + ->set('host', '127.0.0.1') ->set('user', 'admin') - ->set('pass', 'admin') - ->set('legacy', true); + ->set('pass', 'admin'); // Initiate client with config object $client = new Client($config); diff --git a/examples/system_package_print.php b/examples/system_package_print.php index b8e7a8b..e19d507 100644 --- a/examples/system_package_print.php +++ b/examples/system_package_print.php @@ -10,7 +10,7 @@ use \RouterOS\Query; // Create config object with parameters $config = (new Config()) - ->set('host', '192.168.1.31') + ->set('host', '127.0.0.1') ->set('user', 'admin') ->set('pass', 'admin'); diff --git a/src/Client.php b/src/Client.php index b08f5f5..0be0aed 100644 --- a/src/Client.php +++ b/src/Client.php @@ -89,37 +89,48 @@ class Client implements Interfaces\ClientInterface } /** - * Convert ordinary string to hex string + * Encode given length in RouterOS format * * @param string $string - * @return string + * @return string Encoded length + * @throws ClientException */ private function encodeLength(string $string): string { - // Yeah, that's insane, but was more ugly, you need read this post if you interesting a details: - // https://wiki.mikrotik.com/wiki/Manual:API#API_words - switch (true) { - case ($string < 0x80): - $string = \chr($string); - break; - case ($string < 0x4000): - $string |= 0x8000; - $string = \chr(($string >> 8) & 0xFF) . \chr($string & 0xFF); - break; - case ($string < 0x200000): - $string |= 0xC00000; - $string = \chr(($string >> 16) & 0xFF) . \chr(($string >> 8) & 0xFF) . \chr($string & 0xFF); - break; - case ($string < 0x10000000): - $string |= 0xE0000000; - $string = \chr(($string >> 24) & 0xFF) . \chr(($string >> 16) & 0xFF) . \chr(($string >> 8) & 0xFF) . \chr($string & 0xFF); - break; - case ($string >= 0x10000000): - $string = \chr(0xF0) . \chr(($string >> 24) & 0xFF) . \chr(($string >> 16) & 0xFF) . \chr(($string >> 8) & 0xFF) . \chr($string & 0xFF); - break; + $length = \strlen($string); + + if ($length < 128) { + $orig_length = $length; + $offset = -1; + } elseif ($length < 16384) { + $orig_length = $length | 0x8000; + $offset = -2; + } elseif ($length < 2097152) { + $orig_length = $length | 0xC00000; + $offset = -3; + } elseif ($length < 268435456) { + $orig_length = $length | 0xE0000000; + $offset = -4; + } else { + throw new ClientException("Unable to encode length of '$string'"); } - return $string; + // Pack string to binary format + $result = pack('I*', $orig_length); + // Parse binary string to array + $result = str_split($result); + // Reverse array + $result = array_reverse($result); + // Extract values from offset to end of array + $result = \array_slice($result, $offset); + + // Sew items into one line + $output = null; + foreach ($result as $item) { + $output .= $item; + } + + return $output; } /** @@ -166,13 +177,14 @@ class Client implements Interfaces\ClientInterface * * @param QueryInterface $query * @return ClientInterface + * @throws ClientException */ public function write(QueryInterface $query): ClientInterface { // Send commands via loop to router foreach ($query->getQuery() as $command) { $command = trim($command); - fwrite($this->_socket, $this->encodeLength(\strlen($command)) . $command); + fwrite($this->_socket, $this->encodeLength($command) . $command); } // Write zero-terminator @@ -196,10 +208,8 @@ class Client implements Interfaces\ClientInterface while (true) { // Read the first byte of input which gives us some or all of the length // of the remaining reply. - $byte = \ord(fread($this->_socket, 1)); - - // Read length of line - $length = $this->getLength($byte); + $byte = fread($this->_socket, 1); + $length = $this->getLength(\ord($byte)); // Save only non empty strings if ($length > 0) { @@ -267,6 +277,12 @@ class Client implements Interfaces\ClientInterface return $result; } + /** + * Parse result from RouterOS by regular expression + * + * @param string $value + * @param array $matches + */ private function pregResponse(string $value, &$matches) { preg_match_all('/^[=|\.](.*)=(.*)/', $value, $matches); @@ -276,6 +292,7 @@ class Client implements Interfaces\ClientInterface * Authorization logic * * @return bool + * @throws ClientException */ private function login(): bool { @@ -288,7 +305,8 @@ class Client implements Interfaces\ClientInterface // Now need use this hash for authorization $query = (new Query('/login')) ->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 { // Just login with our credentials $query = (new Query('/login'))