215 lines
6.7 KiB
PHP
215 lines
6.7 KiB
PHP
<?php
|
|
/**
|
|
* RouterOS API client implementation.
|
|
*
|
|
* @author Denis Basta
|
|
* @copyright 2008-2013 Denis Basta
|
|
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
|
* @version 1.6
|
|
*/
|
|
|
|
class RouterosAPI
|
|
{
|
|
public $debug = false; // Show debug information
|
|
public $connected = false; // Connection status
|
|
public $port = 8728; // RouterOS API port
|
|
public $timeout = 3; // Connection timeout
|
|
public $attempts = 5; // Connection attempts
|
|
public $delay = 3; // Delay between connection attempts
|
|
|
|
private $socket; // Socket resource
|
|
private $error_no; // Error number
|
|
private $error_str; // Error string
|
|
|
|
/**
|
|
* Connect to RouterOS
|
|
*
|
|
* @param string $ip Hostname (IP or domain) of the RouterOS server
|
|
* @param string $login The RouterOS username
|
|
* @param string $password The RouterOS password
|
|
*
|
|
* @return boolean Connection status
|
|
*/
|
|
public function connect($ip, $login, $password)
|
|
{
|
|
for ($ATTEMPT = 1; $ATTEMPT <= $this->attempts; $ATTEMPT++) {
|
|
$this->connected = false;
|
|
$this->debug('Connection attempt #' . $ATTEMPT . ' to ' . $ip . ':' . $this->port . '...');
|
|
$this->socket = @fsockopen($ip, $this->port, $this->error_no, $this->error_str, $this->timeout);
|
|
if ($this->socket) {
|
|
socket_set_timeout($this->socket, $this->timeout);
|
|
$this->write('/login');
|
|
$RESPONSE = $this->read(false);
|
|
if (isset($RESPONSE[0]) && $RESPONSE[0] == '!done') {
|
|
if (preg_match_all('/[^=]+/i', $RESPONSE[1], $MATCHES)) {
|
|
if ($MATCHES[0][0] == 'ret' && strlen($MATCHES[0][1]) == 32) {
|
|
$this->write('/login', false);
|
|
$this->write('=name=' . $login, false);
|
|
$this->write('=response=00' . md5(chr(0) . $password . pack('H*', $MATCHES[0][1])));
|
|
$RESPONSE = $this->read(false);
|
|
if (isset($RESPONSE[0]) && $RESPONSE[0] == '!done') {
|
|
$this->connected = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fclose($this->socket);
|
|
}
|
|
sleep($this->delay);
|
|
}
|
|
|
|
if ($this->connected) {
|
|
$this->debug('Connected successfully to ' . $ip . ':' . $this->port);
|
|
} else {
|
|
$this->debug('Error connecting to ' . $ip . ':' . $this->port);
|
|
}
|
|
return $this->connected;
|
|
}
|
|
|
|
/**
|
|
* Disconnect from RouterOS
|
|
*/
|
|
public function disconnect()
|
|
{
|
|
fclose($this->socket);
|
|
$this->connected = false;
|
|
$this->debug('Disconnected');
|
|
}
|
|
|
|
/**
|
|
* Parse response from RouterOS
|
|
*
|
|
* @param array $response Response data
|
|
*
|
|
* @return array Parsed data
|
|
*/
|
|
public function parseResponse($response)
|
|
{
|
|
if (is_array($response)) {
|
|
$PARSED = array();
|
|
$CURRENT = null;
|
|
$singlevalue = null;
|
|
foreach ($response as $x) {
|
|
if (in_array($x, array('!fatal', '!re', '!trap'))) {
|
|
if ($x == '!re') {
|
|
$CURRENT =& $PARSED[];
|
|
} else {
|
|
$CURRENT =& $PARSED[$x][];
|
|
}
|
|
} elseif ($x != '!done') {
|
|
if (preg_match_all('/[^=]+/i', $x, $MATCHES)) {
|
|
if ($MATCHES[0][0] == 'ret') {
|
|
$singlevalue = $MATCHES[0][1];
|
|
}
|
|
$CURRENT[$MATCHES[0][0]] = (isset($MATCHES[0][1]) ? $MATCHES[0][1] : '');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (empty($PARSED) && !is_null($singlevalue)) {
|
|
$PARSED = $singlevalue;
|
|
}
|
|
|
|
return $PARSED;
|
|
} else {
|
|
return array();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read data from RouterOS
|
|
*
|
|
* @param boolean $parse Parse the data?
|
|
*
|
|
* @return array Data array
|
|
*/
|
|
public function read($parse = true)
|
|
{
|
|
$RESPONSE = array();
|
|
$line = '';
|
|
while (true) {
|
|
$BYTE = fread($this->socket, 1);
|
|
$line .= $BYTE;
|
|
if ($BYTE == "\0") {
|
|
$RESPONSE[] = $line;
|
|
if (substr($line, -5) == "!done\0") {
|
|
break;
|
|
}
|
|
$line = '';
|
|
}
|
|
}
|
|
|
|
if ($parse) {
|
|
return $this->parseResponse($RESPONSE);
|
|
} else {
|
|
return $RESPONSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write (send) data to RouterOS
|
|
*
|
|
* @param string $command A string with the command to send
|
|
* @param boolean $param2 If we are sending a command, or a parameter
|
|
*
|
|
* @return void
|
|
*/
|
|
public function write($command, $param2 = true)
|
|
{
|
|
if ($command) {
|
|
$data = explode("\n", $command);
|
|
foreach ($data as $com) {
|
|
$com = trim($com);
|
|
fwrite($this->socket, $this->encodeLength(strlen($com)) . $com);
|
|
$this->debug('<<< ' . $com);
|
|
}
|
|
|
|
if (gettype($param2) == 'integer') {
|
|
fwrite($this->socket, $this->encodeLength(strlen('.tag=' . $param2)) . '.tag=' . $param2 . "\0");
|
|
$this->debug('<<< .tag=' . $param2);
|
|
} elseif (gettype($param2) == 'boolean') {
|
|
fwrite($this->socket, ($param2 ? '' : "\0"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Encode length of the string
|
|
*
|
|
* @param integer $length Length of the string
|
|
*
|
|
* @return string Encoded length
|
|
*/
|
|
private function encodeLength($length)
|
|
{
|
|
if ($length < 0x80) {
|
|
return chr($length);
|
|
}
|
|
if ($length < 0x4000) {
|
|
return chr(($length >> 8) | 0x80) . chr($length & 0xFF);
|
|
}
|
|
if ($length < 0x200000) {
|
|
return chr(($length >> 16) | 0xC0) . chr(($length >> 8) & 0xFF) . chr($length & 0xFF);
|
|
}
|
|
if ($length < 0x10000000) {
|
|
return chr(($length >> 24) | 0xE0) . chr(($length >> 16) & 0xFF) . chr(($length >> 8) & 0xFF) . chr($length & 0xFF);
|
|
}
|
|
return chr(0xF0) . chr(($length >> 24) & 0xFF) . chr(($length >> 16) & 0xFF) . chr(($length >> 8) & 0xFF) . chr($length & 0xFF);
|
|
}
|
|
|
|
/**
|
|
* Print debug information
|
|
*
|
|
* @param string $text Debug text
|
|
*
|
|
* @return void
|
|
*/
|
|
private function debug($text)
|
|
{
|
|
if ($this->debug) {
|
|
echo $text . "\n";
|
|
}
|
|
}
|
|
}
|