Community discussions

MikroTik App
 
User avatar
NAB
Trainer
Trainer
Topic Author
Posts: 542
Joined: Tue Feb 10, 2009 4:08 pm
Location: UK
Contact:

PHP API bug - read() fails on large replies - this fix OK?

Tue Feb 17, 2009 4:25 pm

I have been using the PHP API and found an interesting problem. When the read() function is called and the response is greater than 127 characters, it all goes horribly wrong.

This is because the original coder failed to take into account the reply length criteria as defined on http://wiki.mikrotik.com/wiki/API. It took me an hour or so to get my head around how the length works and therefore how to fix the PHP code. Unfortunately, my account doesn't have the ability to edit the API page on the wiki and so I can't update the page to make the description more clear. However.....

I have changed the read() function in the PHP class as follows:
	function read($parse = true) {

		$RESPONSE = array();

		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) );
			$LENGTH = 0;
			
			echo "$BYTE\n";

			// If the first bit is set then we need to remove the first four bits, shift left 8
			// and then read another byte in.
			// We repeat this for the second and third bits.
			// If the fourth bit is set, we need to remove anything left in the first byte
			// and then read in yet another byte.
			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 we have got more characters to read, read them in.
			if ($LENGTH > 0) {
				$_ = "";
				$retlen=0;
				while ($retlen < $LENGTH) {
					$toread = $LENGTH - $retlen ;
					$_ .= fread($this->socket, $toread);
					$retlen = strlen($_);
				}
				$RESPONSE[] = $_ ;
				$this->debug('>>> [' . $retlen . '/' . $LENGTH . ' bytes read.');
			}

			// If we get a !done, make a note of it.
			if ($_ == "!done")
				$receiveddone=true;

			$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'] && $receiveddone) )
				break;

		}

		if ($parse)
			$RESPONSE = $this->parse_response($RESPONSE);

		return $RESPONSE;

	}
Note that this incorporates the changes I suggested in a previous post (regarding the '!done' reply). Please can somebody with better PHP skills than me have a look and see if it can be optimised? If there's no response within a few days, I'll make the changes on the wiki.
 
stealth
newbie
Posts: 30
Joined: Fri May 26, 2006 4:55 pm

Re: PHP API bug - read() fails on large replies - this fix OK?

Sat Feb 21, 2009 12:14 am

Hi,
I can confirm that your implementation of read method runs very nice. The original wiki code was buggy, some commands does not executes at all.
I used moly's fix http://forum.mikrotik.com/viewtopic.php ... 26#p143426. But it locks for few seconds on every command. With this implementation is all fine. I would only inicialize $receiveddone = false at the beggining of method call to get rid of php notice.
Thank you!
 
cieplik206
Trainer
Trainer
Posts: 290
Joined: Sun Jul 01, 2007 12:25 am
Contact:

Re: PHP API bug - read() fails on large replies - this fix OK?

Wed Feb 25, 2009 12:04 pm

Hi

this works well but there is another problem with this implementation.

Problem is when you are sending "listen" command (ie: /interface/wireless/registration-table/listen ).

As listen never ends, there is no !done command, and from main program read method just freezes,
 
User avatar
NAB
Trainer
Trainer
Topic Author
Posts: 542
Joined: Tue Feb 10, 2009 4:08 pm
Location: UK
Contact:

Re: PHP API bug - read() fails on large replies - this fix OK?

Wed Feb 25, 2009 4:36 pm

this works well but there is another problem with this implementation.
You are quite right - as it exists at the moment, the class expects an immediate response to a command and doesn't work at all well when the response may never come or may come at some point in the future.

Nick.
 
cieplik206
Trainer
Trainer
Posts: 290
Joined: Sun Jul 01, 2007 12:25 am
Contact:

Re: PHP API bug - read() fails on large replies - this fix OK?

Fri Mar 06, 2009 12:40 pm

Solution for listen command

Make new method called listen and comment those two lines, and it will work
         if ($_ == "!done")
            $receiveddone=true;


Pawel Cieplinski
 
User avatar
janisk
MikroTik Support
MikroTik Support
Posts: 6263
Joined: Tue Feb 14, 2006 9:46 am
Location: Riga, Latvia

Re: PHP API bug - read() fails on large replies - this fix OK?

Mon Mar 09, 2009 11:44 am

well, listen commands have !done, when you send in cancel, also, listen (and all commands without the end) should have their tag set =tag=unendingStory

so you can use /cancel with =tag=unendingStory to stop the command

then you will have your !done for the command
 
cieplik206
Trainer
Trainer
Posts: 290
Joined: Sun Jul 01, 2007 12:25 am
Contact:

Re: PHP API bug - read() fails on large replies - this fix OK?

Mon Mar 16, 2009 3:39 pm

well, listen commands have !done, when you send in cancel, also, listen (and all commands without the end) should have their tag set =tag=unendingStory

so you can use /cancel with =tag=unendingStory to stop the command

then you will have your !done for the command

Ok Janis, but what is the point in listening and cancelling ? If you want to react just after event occurs than you need to modify this API to be able to read without !done message.

Yes, you are right about using tags, but to use tags, this PHP API is absolutly useless.
 
dbai
just joined
Posts: 15
Joined: Mon Mar 16, 2009 1:22 am
Location: Prague, Czech Republic

Re: PHP API bug - read() fails on large replies - this fix OK?

Sat Mar 21, 2009 10:17 pm

BTW why is not possible to use command "/export" or eg. "/system/export" or any other export via API? The behavior of script is quite weird, sometimes ends immediately, sometimes returns zeroes, sometimes waits and returns nothing. In any case without useful response. After this query routerboard is often loaded at 100% (probably permanently) and you have to restart it, otherwise system become unstable and very slow. I want to get automatically some actual configurations from my RBs using API, but without any success.
 
User avatar
janisk
MikroTik Support
MikroTik Support
Posts: 6263
Joined: Tue Feb 14, 2006 9:46 am
Location: Riga, Latvia

Re: PHP API bug - read() fails on large replies - this fix OK?

Mon Mar 23, 2009 1:35 pm

you are able to listen and while listening you are receiving data. as it is endless command it can only be canceled. Once canceled, it will return !done

but all the data will be received previously.

this is basics how API works:
API client --->command ---> router
APi client <---data !done<---router
with endless commands
API client --->command listen ---> router
APi client <---data                <---router
APi client <---data                <---router
.. 
APi client <---data                <---router
APi client <---data                <---router
API client ---> /cancel ----------->router
APi client <---data !done<---        router

so you are able to run several commands simultaneously. it is just a matter of implementation, when you are able to view data. So that is good idea to tag commands, because each ret will have its tag assigned, so you can see what command is returning what.
 
talmage
just joined
Posts: 5
Joined: Mon Aug 04, 2008 12:53 am

Re: PHP API bug - read() fails on large replies - this fix OK?

Sat Mar 28, 2009 9:47 pm

I use this for reading the length. I derived it from the python example on the API wiki.

The $this->read(X) call goes to a method that reads X bytes off the socket buffer.
	private function readLength()
	{
		/* Get the value of the first byte */		
		$lengthDecoded = ord($this->read(1));
		
		/* Check if we are a single byte */
		if (((int)$lengthDecoded & 0x80) == 0x00)
		{
			
			/* Single byte length */
			return $lengthDecoded;
			
		}
		/* Check if we are a 2 byte length */
		else if (((int)$lengthDecoded & 0xC0) == 0x80)
		{
			
			/* Logical AND the length with the inverse of the encoding flag, producing the original value for the lower byte */
			$lengthDecoded = $lengthDecoded & (~0xC0);
			/* Shift the value 8 bytes */
			$lengthDecoded = (int)$lengthDecoded << 8;
			/* Add the next value onto the working value */
			$lengthDecoded += ord($this->read(1));
			
			return $lengthDecoded;
			
		}
		/* Check if we are a 3 byte length */
		else if (((int)$lengthDecoded & 0xE0) == 0xC0)
		{
			
			/* Logical AND the length with the inverse of the encoding flag, producing the original value for the lower byte */
			$lengthDecoded = $lengthDecoded & (~0xE0);
			/* Shift the value 8 bytes */
			$lengthDecoded = (int)$lengthDecoded << 8;
			/* Add the next value onto the working value */
			$lengthDecoded += ord($this->read(1));
			/* Shift the value 8 bytes */
			$lengthDecoded = (int)$lengthDecoded << 8;
			/* Add the next value onto the working value */
			$lengthDecoded += ord($this->read(1));		
			
			return $lengthDecoded;
			
		}
		/* Check if we are a 4 byte length */
		else if (((int)$lengthDecoded & 0xF0) == 0xE0)
		{
			
			/* Logical AND the length with the inverse of the encoding flag, producing the original value for the lower byte */
			$lengthDecoded = $lengthDecoded & (~0xF0);
			/* Shift the value 8 bytes */
			$lengthDecoded = (int)$lengthDecoded << 8;
			/* Add the next value onto the working value */
			$lengthDecoded += ord($this->read(1));
			/* Shift the value 8 bytes */
			$lengthDecoded = (int)$lengthDecoded << 8;
			/* Add the next value onto the working value */
			$lengthDecoded += ord($this->read(1));		
			/* Shift the value 8 bytes */
			$lengthDecoded = (int)$lengthDecoded << 8;
			/* Add the next value onto the working value */
			$lengthDecoded += ord($this->read(1));				
			
			return $lengthDecoded;
			
		}
		else
		{
			
			throw new RosWordTooLongException("First byte of length was: 0x".dechex($lengthDecoded));
			
		}
	
		
	}	
 
jarosoup
Long time Member
Long time Member
Posts: 596
Joined: Sun Aug 22, 2004 9:02 am

Re: PHP API bug - read() fails on large replies - this fix OK?

Fri Oct 30, 2009 7:56 pm

NAB, thank you for posting this script. I had to make one modification to it as I was getting the following error:

PHP Notice: Undefined variable: receiveddone in routeros_api.class.php on line 322

I had to add an else statement so that the var was valid:

         // If we get a !done, make a note of it.
         if ($_ == "!done")
            $receiveddone=true;

         else {
            $receiveddone=false;
         }

I'm using this script to get the equivalent of /ip hotspot active print count-only as I can not get my old 2.9 expect scripts to work quickly enough or properly with 3.0 due to all of the garbage you get when ssh'ing into a router (yes I've tried +ct and 80w).

Anyway, hope this helps...
 
erazor999
just joined
Posts: 2
Joined: Sat Nov 28, 2009 10:38 am

Re: PHP API bug - read() fails on large replies - this fix OK?

Sat Nov 28, 2009 10:41 am

I have problem with connection??? Sometimes connect, sometimes not!!!

Connection attempt #1 to 10.2.2.7:8728...
<<< [6] /login
>>> [5/5 bytes read.
>>> [5, 39] !done
>>> [37/37 bytes read.
>>> [37, 1] =ret=ce97ace24bd5dd4b4d451ca4ceddce40
Connection attempt #2 to 10.2.2.7:8728...
<<< [6] /login
>>> [5/5 bytes read.
>>> [5, 39] !done
>>> [37/37 bytes read.
>>> [37, 1] =ret=0cbc43a8886382dfa87122bccebf8f2c
Connection attempt #3 to 10.2.2.7:8728...
<<< [6] /login
>>> [5/5 bytes read.
>>> [5, 39] !done
>>> [37/37 bytes read.
>>> [37, 1] =ret=d20f103c25896266854f7796e2706af3
Connection attempt #4 to 10.2.2.7:8728...
<<< [6] /login
>>> [5/5 bytes read.
>>> [5, 39] !done
>>> [37/37 bytes read.
>>> [37, 1] =ret=195c5b110900cae2547a27294f2e888b
Connection attempt #5 to 10.2.2.7:8728...
<<< [6] /login
>>> [5/5 bytes read.
>>> [5, 39] !done
>>> [37/37 bytes read.
>>> [37, 1] =ret=9e95e5ad2525e86f2547e3afecf999bf
Error...
 
antenman
newbie
Posts: 25
Joined: Wed Jun 14, 2006 7:30 pm

Re: PHP API bug - read() fails on large replies - this fix OK?

Fri Dec 25, 2009 3:00 am

Hi there
$API->write('/tool/torch/<pppoe-user>');
returns
Array ( [!trap] => Array ( [0] => Array ( [message] => no such command ) ) ) 
Is API usable for this operation (torch for pppoe interfaces...)?
 
User avatar
Chupaka
Forum Guru
Forum Guru
Posts: 8709
Joined: Mon Jun 19, 2006 11:15 pm
Location: Minsk, Belarus
Contact:

Re: PHP API bug - read() fails on large replies - this fix OK?

Fri Dec 25, 2009 5:24 pm

there's no such command - '/tool/torch/<pppoe-user>'
correct syntax is
/tool/torch
=interface=bla-bla-bla
 
antenman
newbie
Posts: 25
Joined: Wed Jun 14, 2006 7:30 pm

Re: PHP API bug - read() fails on large replies - this fix OK?

Sun Dec 27, 2009 12:37 am

Thanks.


Now I have another problem....
$API->write('/tool/torch',false);
$API->write('=interface=<pppoe-user>',false);
$API->write('/cancel',false);
$API->write('=tag=unendingStory',true);
$READ = $API->read(false);
$ARRAY = $API->parse_response($READ);
Returns...
Array ( [!trap] => Array ( [0] => Array ( [message] => unknown parameter ) ) ) 
<pppoe-user> exists in interface list and traffic exist too...

I made few php scripts which works with API but I'm not sure how to deal with "never ending commands"

Best regards and happy Christmas
 
User avatar
Chupaka
Forum Guru
Forum Guru
Posts: 8709
Joined: Mon Jun 19, 2006 11:15 pm
Location: Minsk, Belarus
Contact:

Re: PHP API bug - read() fails on large replies - this fix OK?

Sun Dec 27, 2009 11:06 am

I think, for '/tool/torch' command, there's no such parameter - '/cancel' =)
 
antenman
newbie
Posts: 25
Joined: Wed Jun 14, 2006 7:30 pm

Re: PHP API bug - read() fails on large replies - this fix OK?

Mon Dec 28, 2009 12:27 pm

/cancel is because torch is "never ending command"

I'll see what I can do with it... :(

Best regards
 
User avatar
Chupaka
Forum Guru
Forum Guru
Posts: 8709
Joined: Mon Jun 19, 2006 11:15 pm
Location: Minsk, Belarus
Contact:

Re: PHP API bug - read() fails on large replies - this fix OK?

Mon Dec 28, 2009 1:14 pm

/cancel is just another command. please, read http://wiki.mikrotik.com/wiki/API#Tags

by the way, you may add 'duration' parameter - then Torch won't be 'never ending' =)
 
User avatar
janisk
MikroTik Support
MikroTik Support
Posts: 6263
Joined: Tue Feb 14, 2006 9:46 am
Location: Riga, Latvia

Re: PHP API bug - read() fails on large replies - this fix OK?

Tue Dec 29, 2009 11:31 am

also, for outputs and testing command i would suggest to use python example from wiki page, that way you see if command you are trying to run is capable to run at all. If it is, you should be able to get same or similar output from every other API implementation. Just that python example is made my MikroTik
 
Christiano
Frequent Visitor
Frequent Visitor
Posts: 52
Joined: Fri Aug 13, 2010 8:53 pm

Re: PHP API bug - read() fails on large replies - this fix O

Mon Sep 13, 2010 5:08 pm

Hi erazor999.
I have problem with connection??? Sometimes connect, sometimes not!!!

Connection attempt #1 to 10.2.2.7:8728...
<<< [6] /login
>>> [5/5 bytes read.
>>> [5, 39] !done
>>> [37/37 bytes read.
>>> [37, 1] =ret=ce97ace24bd5dd4b4d451ca4ceddce40
Connection attempt #2 to 10.2.2.7:8728...
<<< [6] /login
>>> [5/5 bytes read.
>>> [5, 39] !done
>>> [37/37 bytes read.
>>> [37, 1] =ret=0cbc43a8886382dfa87122bccebf8f2c
Connection attempt #3 to 10.2.2.7:8728...
<<< [6] /login
>>> [5/5 bytes read.
>>> [5, 39] !done
>>> [37/37 bytes read.
>>> [37, 1] =ret=d20f103c25896266854f7796e2706af3
Connection attempt #4 to 10.2.2.7:8728...
<<< [6] /login
>>> [5/5 bytes read.
>>> [5, 39] !done
>>> [37/37 bytes read.
>>> [37, 1] =ret=195c5b110900cae2547a27294f2e888b
Connection attempt #5 to 10.2.2.7:8728...
<<< [6] /login
>>> [5/5 bytes read.
>>> [5, 39] !done
>>> [37/37 bytes read.
>>> [37, 1] =ret=9e95e5ad2525e86f2547e3afecf999bf
Error...

I had the same problem. It was fixed when I followed the suggestion found here:

http://forum.mikrotik.com/viewtopic.php ... HP#p147918

The sleep before read()...
 
Christiano
Frequent Visitor
Frequent Visitor
Posts: 52
Joined: Fri Aug 13, 2010 8:53 pm

Re: PHP API bug - read() fails on large replies - this fix O

Mon Sep 13, 2010 5:51 pm

Solution for listen command

Make new method called listen and comment those two lines, and it will work
         if ($_ == "!done")
            $receiveddone=true;


Pawel Cieplinski

But if you do this, here it don´t work:
if ( (!$this->connected && !$STATUS['unread_bytes']) || ($this->connected && !$STATUS['unread_bytes'] && $receiveddone) )
The API check the variable. If you comment the if, you need delete the "&& $receiveddone" in the code.
 
Christiano
Frequent Visitor
Frequent Visitor
Posts: 52
Joined: Fri Aug 13, 2010 8:53 pm

Re: PHP API bug - read() fails on large replies - this fix O

Mon Sep 13, 2010 9:29 pm

NAB, thank you for posting this script. I had to make one modification to it as I was getting the following error:

PHP Notice: Undefined variable: receiveddone in routeros_api.class.php on line 322

I had to add an else statement so that the var was valid:
// If we get a !done, make a note of it.
if ($_ == "!done")
$receiveddone=true;
 else {
$receiveddone=false;
}
I'm using this script to get the equivalent of /ip hotspot active print count-only as I can not get my old 2.9 expect scripts to work quickly enough or properly with 3.0 due to all of the garbage you get when ssh'ing into a router (yes I've tried +ct and 80w).

Anyway, hope this helps...


Well,

i have done! in debug...
Connection attempt #1 to 111.111.111.111:8728...
<<< [6] /login
>>> [5/5 bytes read.
>>> [5, 39] !done
>>> [37/37 bytes read.
*******FALSE: ***********>>> [37, 1] =ret=54387e46d025b4f033629f67148100c3
*******FALSE: ***********<<< [6] /login
<<< [10] =name=zuca
<<< [44] =response=00adad56bad8c219b7d43b210d9160fc05
>>> [5/5 bytes read.
>>> [5, 1] !done

Connected...
<<< [17] /system/clock/set
<<< [17] =date=Sep/13/2010
<<< [14] =time=14:57:13
<<< [33] =time-zone-name=America/Sao_Paulo
>>> [5/5 bytes read.
>>> [5, 1] !done

<<< [21] /system/scheduler/add
<<< [18] =name=tarefa11111
<<< [23] =start-date=Sep/13/2010
<<< [20] =start-time=16:55:00
<<< [18] =interval=00:01:00
<<< [25] =policy=read,write,policy
<<< [81] =on-event=/user set password=1111111  manut; /system scheduler remove tarefa11111
>>> [5/5 bytes read.
>>> [5, 10] !done

>>> [8/8 bytes read.
*******FALSE: ***********>>> [8, 1] =ret=*32
*******FALSE: ***********Disconnected...

... but $receiveddone is false. To see that (*******FALSE: ***********), i make a small change in the "routeros_api.class.php":
// If we get a !done, make a note of it.
if ($_ == "!done" or !$STATUS['unread_bytes'])
{$receiveddone=true;}
else 
{$receiveddone=false; echo "*******FALSE: ".$receiveddone."***********"; }

How to explain that? :?

If i have a "done!", i think the variable always receive true. Am I wrong? :shock:

Can anyone help me clarify this case? :)


My code:
require('routeros_api.class.php'); // API PHP do MIKROTIK
$API = new routeros_api();
$API->debug = true; 
					
if ($API->connect($ip, ADMIN_SISTEMA, SENHA_SISTEMA)) // CONEXÃO COM A PLACA
{
$API->write('/system/clock/set', false);
$API->write('=date='.$data, false);
$API->write('=time='.$hora, false);
$API->write('=time-zone-name='.$regiao);
							
$ARRAY = $API->read();
			
$API->write('/system/scheduler/add', false);
$API->write('=name='.$tarefa, false);
$API->write('=start-date='.$dt, false);
$API->write('=start-time='.$hr, false);
$API->write('=interval='.$hrint, false);
$API->write('=policy='.$politica, false);
$API->write('=on-event='.$comando);

$ARRAY = $API->read();
							
$API->disconnect();
 
User avatar
luqasz
Member Candidate
Member Candidate
Posts: 101
Joined: Thu Aug 16, 2007 9:53 pm
Location: Poland

Re: PHP API bug - read() fails on large replies - this fix O

Tue Nov 30, 2010 9:51 am

NAB can you enable your read function to supress this:
 php-cgi -q mt.php

5
37
0
5
0
5
0
5
0
5
0
when not in debug mode ?

Who is online

Users browsing this forum: Bing [Bot] and 34 guests