Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
324 lines (291 sloc) 11.7 KB
<?php
class RouterOS {
// Socket resource:
private $socket;
// Public variables:
public $connected = false;
public $debug_str = null;
public $error_str = null;
public $error_num = null;
// Constatns of class:
const PORT = 8728;
const DEBUG = false;
const ATTEMPTS = 3;
const TIMEOUT = 5;
const DELAY = 0;
/**
* Fills in the `$this->debug_str` variable if self::DEBUG is enabled
*
* @param string $string Debug message string
* @return boolean
*/
private function debug($string) {
if ( self::DEBUG ) {
$this->debug_str .= $string . '<br />' . PHP_EOL;
}
}
/**
* Establishes connection with RouterOS device via API
*
* @param string $hostname Device`s IP or hostname
* @param string $username Username
* @param string $password Password
* @return boolean $this->connected
*/
public function connect($hostname, $username, $password, $UseNewConnMode = false) {
// Connect to device:
for ( $attempt = 1; $attempt <= self::ATTEMPTS; $attempt++ ) {
$this->connected = false;
$this->debug('Connection attempt #' . $attempt . ' to ' . $hostname . ':' . self::PORT);
$this->socket = @fsockopen($hostname, self::PORT, $this->error_num, $this->error_str, self::TIMEOUT);
if ( $this->socket ) {
socket_set_timeout($this->socket, self::TIMEOUT);
if ($UseNewConnMode) {
$this->write('/login', false);
$this->write('=name=' . $username, false);
$this->write('=password=' . $password);
$response = $this->read(false);
if ($response[0] == '!done') {
$this->connected = true;
break;
}
} else {
$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=' . $username, false);
$this->write('=response=00' . md5(chr(0) . $password . pack('H*', $matches[0][1])));
$response = $this->read(false);
if ($response[0] == '!done') {
$this->connected = true;
break;
}
}
}
}
}
fclose($this->socket);
}
sleep(self::DELAY);
}
// Throw debug-message:
if ( $this->connected ) {
$this->debug('Connection with device is established...');
} else {
$this->debug('Couldn`t establish connection with device!');
}
// Return connection state:
return $this->connected;
}
/**
* Encodes length
*
* @param string $length Length to encode
* @return string
*/
private function encode_length($length) {
switch ( true ) {
case ( $length < 0x80 ):
$length = chr($length);
break;
case ( $length < 0x4000 ):
$length |= 0x8000;
$length = chr(($length >> 8) & 0xFF) . chr($length & 0xFF);
break;
case ( $length < 0x200000 ):
$length |= 0xC00000;
$length = chr(($length >> 16) & 0xFF) . chr(($length >> 8) & 0xFF) . chr($length & 0xFF);
break;
case ( $length < 0x10000000 ):
$length |= 0xE0000000;
$length = chr(($length >> 24) & 0xFF) . chr(($length >> 16) & 0xFF) . chr(($length >> 8) & 0xFF) . chr($length & 0xFF);
break;
case ( $length >= 0x10000000 ):
$length = chr(0xF0) . chr(($length >> 24) & 0xFF) . chr(($length >> 16) & 0xFF) . chr(($length >> 8) & 0xFF) . chr($length & 0xFF);
break;
}
return $length;
}
/**
* Parses RouterOS device`s reply to 'comfortable' array
*
* @param array $response RouterOS device`s reply
* @return array
*/
private function parse_response($response) {
$return = array();
if ( is_array($response) ) {
$current = null;
$single = null;
foreach ( $response as $key ) {
if ( in_array($key, array('!fatal', '!re', '!trap')) ) {
if ( $key == '!re' ) {
$current = & $return[];
} else $current = & $return[$key][];
} else if ( $key != '!done' ) {
if ( preg_match_all('/[^=]+/i', $key, $matches) ) {
if ( $matches[0][0] == 'ret' ) {
$single = $matches[0][1];
}
$current[$matches[0][0]] = ( isset($matches[0][1] ) ? $matches[0][1] : null);
}
}
}
if ( empty($return) && !is_null($single) ) {
$return = $single;
}
}
return $return;
}
/**
* Reads data from RouterOS device via API
*
* @param boolean $parse_response
* @return array
*/
public function read($parse_response = true) {
$response = array();
$_ = '';
while ( true ) {
$byte = ord(fread($this->socket, 1));
$length = 0;
if ($byte & 128) {
if (($byte & 192) == 128) {
$length = (($byte & 63) << 8) + ord(fread($this->socket, 1));
} else {
if (($byte & 224) == 192) {
$length = (($byte & 31) << 8) + ord(fread($this->socket, 1));
$length = ($length << 8) + ord(fread($this->socket, 1));
} else {
if (($byte & 240) == 224) {
$length = (($byte & 15) << 8) + ord(fread($this->socket, 1));
$length = ($length << 8) + ord(fread($this->socket, 1));
$length = ($length << 8) + ord(fread($this->socket, 1));
} else {
$length = ord(fread($this->socket, 1));
$length = ($length << 8) + ord(fread($this->socket, 1));
$length = ($length << 8) + ord(fread($this->socket, 1));
$length = ($length << 8) + ord(fread($this->socket, 1));
}
}
}
} else $length = $byte;
if ($length > 0) {
$retlen = 0;
$_ = '';
while ($retlen < $length) {
$toread = $length - $retlen;
$_ .= fread($this->socket, $toread);
$retlen = strlen($_);
}
$response[] = $_;
$this->debug('>>> [' . $retlen . '/' . $length . '] bytes read.');
}
$_done = ($_ == '!done') ? true : false;
$status = socket_get_status($this->socket);
if ($length > 0) {$this->debug('>>> [' . $length . ', ' . $status['unread_bytes'] . ']' . $_);}
if ( (!$this->connected && !$status['unread_bytes']) || ($this->connected && !$status['unread_bytes'] && $_done) ) {
break;
}
}
return ( $parse_response ) ? $this->parse_response($response) : $response;
}
/**
* Writes command to RouterOS device via API
*
* @param string $command Sending command's string
* @param boolean $param ...
* @return boolean
*/
public function write($command, $param = true) {
if ( $command ) {
$data = explode('\n', $command);
foreach ( $data as $cmd ) {
$cmd = trim($cmd);
fwrite($this->socket, $this->encode_length(strlen($cmd)) . $cmd);
$this->debug('<<< [' . strlen($cmd) . '] ' . $cmd);
}
$type = gettype($param);
switch ( $type) {
case 'integer':
fwrite($this->socket, $this->encode_length(strlen('.tag=' . $param)) . '.tag=' . $param . chr(0));
$this->debug('<<< [' . strlen('.tag=' . $param) . '] .tag=' . $param);
break;
case 'boolean':
fwrite($this->socket, ($param ? chr(0) : ''));
break;
}
}
return ( $command ) ? true : false;
}
/**
* Use it for sending commands to RouterOS device
*
* @param string $string RouterOS`s command string
* @param array $data Command`s parameters
* @return array
*/
public function command($string, $data = array()) {
$count = count($data);
$this->write($string, !$data);
$i = 0;
foreach ( $data as $key => $value ) {
switch ( $key[0] ) {
case '?':
$el = $key . '=' . $value;
break;
case '~':
$el = $key . '~' . $value;
break;
default:
$el = '=' . $key . '=' . $value;
break;
}
$this->write($el, ($i++ == $count - 1));
}
return $this->read();
}
/**
* Closes up established connection with RouterOS device
*/
public function disconnect() {
$this->debug('Closing up established connection with RouterOS device..');
$this->connected = ( !fclose($this->socket) ) ? true : false;
return !$this->connected;
}
/**
* Tries to get RouterOS version via SNMP or from login WEB page
*
* @param $Hostname
* @param int $WEBPort
* @param string $SNMPCommunity
* @return float
*/
public function determineVersion($Hostname, $WEBPort = 80, $SNMPCommunity = 'public') {
$Version = '';
// trying to get version via SNMP
$SNMP = new SNMPHelper();
$SNMP->setMode('native');
$OID = '.1.3.6.1.4.1.14988.1.1.4.4.0';
$tmpSNMP = $SNMP->walk($Hostname, $SNMPCommunity, $OID, false);
$Version = ( empty($tmpSNMP) && $tmpSNMP === "$OID = " ) ? '' : str_replace('"', '', trim( substr($tmpSNMP, stripos($tmpSNMP, ':') + 1) ));
// if first option failed - trying to parse login WEB page
if ( empty($Version) ) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $Hostname);
curl_setopt($curl, CURLOPT_PORT, $WEBPort);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$Result = curl_exec($curl);
curl_close($curl);
if ( !empty($Result) ) {
preg_match('/RouterOS v(.*?)</', $Result, $Match);
if ( isset($Match[1]) && !empty($Match[1]) ) { $Version = $Match[1]; }
}
}
return floatval($Version);
}
}
You can’t perform that action at this time.