{"id":38,"date":"2009-05-05T07:08:09","date_gmt":"2009-05-05T07:08:09","guid":{"rendered":"urn:myopera-lunatic-blog-3219492"},"modified":"2009-05-05T07:08:09","modified_gmt":"2009-05-05T07:08:09","slug":"flash-actionscript-3-api-for-mikrotik-routeros-3-x-2","status":"publish","type":"post","link":"http:\/\/130.61.186.249\/index.php\/2009\/05\/05\/flash-actionscript-3-api-for-mikrotik-routeros-3-x-2\/","title":{"rendered":"Flash ActionScript 3 API for Mikrotik RouterOS 3.x"},"content":{"rendered":"<p>Lately I have been familiar with the RouterOS series, when we got us a RouterBoard 493. At first I was a bit sceptical since I had never heard of RouterOS or Mikrotik. But as soon as I started testing this nice little hardware\/software out, I found out that this was one exceptional nice software. And the nice hardware solutions is also nice. You can buy barebone RouterBoards without casing, and put them inside what you want, or buy a RouterOS license and build your own x86 router with standard x86 hardware.<br \/>\nThe great thing about these routers is that they are so configurable, and easy to administer. You should all try them out yourself. They are extremely cheap, and extremely full of features. I know a lot of ISP&#8217;s around the world use only mikrotik equiptment in their wireless networks. Since they are very easy to set up with several wireless cards, and they have exceptionally good drivers, where you can fine tune all the parameters.<br \/>\nAnywyas, in addition to supporting configuration over Telnet\/SSH\/MAC-telnet. They also have a so called RouterOS API. This is a nice little API that gives you computer-friendly access to all the features in the configuration of the box.<br \/>\nVia the API you can read info, set configuration (live), and even listen for events. For example, list out the dBm to all connected WLAN users. Or get notified when someoen pulls a network cable. etc.<br \/>\nThe API is <a href=\"http:\/\/wiki.mikrotik.com\/wiki\/API\" target=\"_blank\" rel=\"noopener\">&#8220;open documentation&#8221;<\/a> and you are welcome to create libraries to different languages and post on their site. They already have PHP, Ruby, Perl, Java, etc implementations, but I wanted to be able to create a Flash AIR application to display and configure routers. So I created a Flash ActionScript 3 class for it.<br \/>\nNow I can create nice interfaces displaying live info from the router, and simple(or andvanced) configuration of the device.<br \/>\nThe ActionScript classes looks like this:<\/p>\n<pre escaped=\"true\" lang=\"actionscript3\" line=\"1\">\/\/ ApiSocket.as\n\/\/\n\/\/ RouterOS API class\n\/\/ Author: H\u00e5kon Nessj\u00f8en\n\/\/ Date: 2. May 2009\n\/\/\npackage {\n\timport flash.errors.*;\n\timport flash.events.*;\n\timport flash.utils.ByteArray;\n\timport com.adobe.crypto.MD5;\n\timport flash.net.Socket;\n\tpublic class ApiSocket extends Socket {\n\t\tstatic public var RECEIVED:String = \"received\";\n\t\tstatic public var LOGIN:String = \"loggedin\";\n\t\tprivate var cmd:String;\n\t\tprivate var doLogin:int;\n\t\tprivate var user:String;\n\t\tprivate var password:String;\n\t\tprivate var returnData:Array;\n\t\tprivate var returnPos:int;\n\t\tprivate var toread:int;\n\t\tprivate var firstRe:int;\n\t\tprivate var tag:String;\n\t\tprivate var gotDone:Boolean;\n\t\tprivate var gotTrap:Boolean;\n\t\tprivate var gotFatal:Boolean;\n\t\tpublic function ApiSocket(host:String, port:uint) {\n\t\t\tsuper(host, port);\n\t\t\ttoread = 0;\n\t\t\tdoLogin = 0;\n\t\t\taddEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);\n\t\t}\n\t\tpublic function login(u:String, p:String) {\n\t\t\tdoLogin = 1;\n\t\t\tuser = u;\n\t\t\tpassword = p;\n\t\t\tsendRequest(\"\/login\");\n\t\t}\n\t\tpublic function sendRequest(... outData):void {\n\t\t\treturnData = new Array();\n\t\t\treturnPos = 0;\n\t\t\tfirstRe = 0;\n\t\t\tgotDone = false;\n\t\t\tgotTrap = false;\n\t\t\tgotFatal = false;\n\t\t\ttag = \"\";\n\t\t\tcmd = outData[0];\n\t\t\treturnData[returnPos] = new Object();\n\t\t\tfor (var i:int = 0; i &lt; outData.length; ++i) {\n\t\t\t\tvar data:ByteArray = new ByteArray();\n\t\t\t\tvar len:uint = outData[i].length;\n\t\t\t\tif (len &lt; 0x80)\n\t\t\t\t\tdata.writeByte(len);\n\t\t\t\telse\n\t\t\t\tif (len &lt; 0x4000) {\n\t\t\t\t\tlen |= 0x8000;\n\t\t\t\t\tdata.writeByte((len &gt;&gt; 8) &amp; 0xff);\n\t\t\t\t\tdata.writeByte(len &amp; 0xff);\n\t\t\t\t} else\n\t\t\t\tif (len &lt; 0x200000) {\n\t\t\t\t\tlen |= 0xC00000;\n\t\t\t\t\tdata.writeByte((len &gt;&gt; 16) &amp; 0xff);\n\t\t\t\t\tdata.writeByte((len &gt;&gt; 8) &amp; 0xff);\n\t\t\t\t\tdata.writeByte(len &amp; 0xff);\n\t\t\t\t} else\n\t\t\t\tif (len &lt; 0x10000000) {\n\t\t\t\t\tlen |= 0xE0000000;\n\t\t\t\t\tdata.writeByte((len &gt;&gt; 24) &amp; 0xff);\n\t\t\t\t\tdata.writeByte((len &gt;&gt; 16) &amp; 0xff);\n\t\t\t\t\tdata.writeByte((len &gt;&gt; 8) &amp; 0xff);\n\t\t\t\t\tdata.writeByte(len &amp; 0xff);\n\t\t\t\t} else {\n\t\t\t\t\tdata.writeByte(0xF0);\n\t\t\t\t\tdata.writeByte((len &gt;&gt; 24) &amp; 0xff);\n\t\t\t\t\tdata.writeByte((len &gt;&gt; 16) &amp; 0xff);\n\t\t\t\t\tdata.writeByte((len &gt;&gt; 8) &amp; 0xff);\n\t\t\t\t\tdata.writeByte(len &amp; 0xff);\n\t\t\t\t}\n\t\t\t\twriteBytes(data);\n\t\t\t\twriteUTFBytes(outData[i]);\n\t\t\t}\n\t\t\twriteByte(0);\n\t\t\tflush();\n\t\t}\n\t\tprivate function readResponse():void {\n\t\t\tvar len:int;\n\t\t\tif (toread == 0) {\n\t\t\t\tvar len1:uint = readUnsignedByte();\n\t\t\t\tif (len1 == 0) {\n\t\t\t\t\tif (gotDone || gotTrap || gotFatal) {\n\t\t\t\t\t\tif (doLogin == 1) {\n\t\t\t\t\t\t\tif (returnData[0].ret) {\n\t\t\t\t\t\t\t\tvar chal:ByteArray = new ByteArray();\n\t\t\t\t\t\t\t\tvar md5:ByteArray = new ByteArray();\n\t\t\t\t\t\t\t\tfor (var i:int = 0; i &lt; returnData[0].ret.length; i += 2) {\n\t\t\t\t\t\t\t\t\tchal.writeByte(int(\"0x\" + returnData[0].ret.substr(i,2)));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tmd5.writeByte(0);\n\t\t\t\t\t\t\t\tmd5.writeUTFBytes(password);\n\t\t\t\t\t\t\t\tmd5.writeBytes(chal);\n\t\t\t\t\t\t\t\tdoLogin++;\n\t\t\t\t\t\t\t\t\/\/ Send challenge response\n\t\t\t\t\t\t\t\tsendRequest(\"\/login\", \"=name=\" + user, \"=response=00\" + MD5.hashBytes(md5));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (doLogin == 2) {\n\t\t\t\t\t\t\tdoLogin = 0;\n\t \t\t\t\t\t\tdispatchEvent(new ApiEvent(ApiSocket.LOGIN, \"\", returnData, gotDone\u00a0? 'done'\u00a0: (gotFatal\u00a0? 'fatal'\u00a0: 'trap')));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdispatchEvent(new ApiEvent(ApiSocket.RECEIVED, tag, returnData, gotDone\u00a0? 'done'\u00a0: (gotFatal\u00a0? 'fatal'\u00a0: 'trap')));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (bytesAvailable)\n\t\t\t\t\t\treadResponse();\n\t\t\t\t\telse\n\t\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (len1 &gt;= 0xF0) {\n\t\t\t\t\tlen = readUnsignedByte();\n\t\t\t\t\tlen = (len &lt;&lt; 8) + readUnsignedByte();\n\t\t\t\t\tlen = (len &lt;&lt; 8) + readUnsignedByte();\n\t\t\t\t\tlen = (len &lt;&lt; 8) + readUnsignedByte();\n\t\t\t\t} else\n\t\t\t\tif (len1 &gt;= 0xE0) {\n\t\t\t\t\tlen = ((len1 &amp; 15) &lt;&lt; 8) + readUnsignedByte();\n\t\t\t\t\tlen = (len &lt;&lt; 8) + readUnsignedByte();\n\t\t\t\t\tlen = (len &lt;&lt; 8) + readUnsignedByte();\n\t\t\t\t} else\n\t\t\t\tif (len1 &gt;= 0xC0) {\n\t\t\t\t\tlen = ((len1 &amp; 31) &lt;&lt; 8) + readUnsignedByte();\n\t\t\t\t\tlen = (len &lt;&lt; 8) + readUnsignedByte();\n\t\t\t\t} else\n\t\t\t\tif (len1 &gt;= 0x80) {\n\t\t\t\t\tlen = ((len1 &amp; 63) &lt;&lt; 8) + readUnsignedByte();\n\t\t\t\t} else\n\t\t\t\t\tlen = len1;\n\t\t\t\ttoread = len;\n\t\t\t}\n\t\t\t\/\/ Calculate how much data of the full length that is available right now\n\t\t\tvar slen:int = bytesAvailable &gt; toread\u00a0? toread\u00a0: bytesAvailable;\n\t\t\t\/\/ Calculate how much data that has to be read later\n\t\t\ttoread = toread &gt; bytesAvailable\u00a0? toread - bytesAvailable\u00a0: 0;\n\t\t\t\/\/ Read relevant data\n\t\t\tvar str:String = readUTFBytes(slen);\n\t\t\tif (toread == 0) {\n\t\t\t\tif (str == '!re') {\n\t\t\t\t\tfirstRe++;\n\t\t\t\t\tif (firstRe &gt; 1) {\n\t\t\t\t\t\treturnPos++\n\t\t\t\t\t\treturnData[returnPos] = new Object();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (str == '!trap')\n\t\t\t\t\tgotTrap = true;\n\t\t\t\tif (str == '!fatal')\n\t\t\t\t\tgotFatal = true;\n\t\t\t\tif (str == '!done')\n\t\t\t\t\tgotDone = true;\n\t\t\t\t\/\/ Parse key-value pair\n\t\t\t\tif (str.substr(0,1) == '=') {\n\t\t\t\t\tvar tmpPos:int = str.indexOf('=',1);\n\t\t\t\t\tvar tmpKey:String = str.substr(1,tmpPos-1);\n\t\t\t\t\tvar tmpVal:String = str.substr(tmpPos+1);\n\t\t\t\t\treturnData[returnPos][tmpKey] = tmpVal;\n\t\t\t\t}\n\t\t\t\t\/\/ Reset tag\n\t\t\t\tif (str.substr(0,1) == '!')\n\t\t\t\t\ttag = \"\";\n\t\t\t\t\/\/ Set tag\n\t\t\t\tif (str.substr(0,5) == '.tag=')\n\t\t\t\t\ttag = str.substr(5);\n\t\t\t\t\/\/ Are there more packets available\n\t\t\t\tif (bytesAvailable)\n\t\t\t\t\treadResponse();\n\t\t\t}\n\t\t}\n\t\tprivate function socketDataHandler(event:ProgressEvent):void {\n\t\t\treadResponse();\n\t\t}\n\t}\n}\n\/\/ ApiEvent.as\n\/\/\n\/\/ RouterOS API Event class\n\/\/ Author: H\u00e5kon Nessj\u00f8en\n\/\/ Date: 2. May 2009\n\/\/\npackage {\n\timport flash.events.Event;\n\tpublic class ApiEvent extends Event {\n\t\tstatic public var RECEIVED:String = \"received\";\n\t\tstatic public var LOGIN:String = \"loggedin\";\n\t\tpublic var data:Array;\n\t\tpublic var result:String;\n\t\tpublic var tag:String;\n\t\tpublic function ApiEvent(type:String, tg:String, dta:Array, res:String){\n\t\t\tsuper(type);\n\t\t\tdata = dta;\n\t\t\tresult = res;\n\t\t\ttag = tg;\n\t\t}\n\t}\n}\n<\/pre>\n<p>And here&#8217;s an<br \/>\n<span style=\"font-size: medium;\">Example<\/span><\/p>\n<pre escaped=\"true\" lang=\"actionscript3\" line=\"1\">\/\/ Short example that outputs wireless registration-table every second\nimport ApiSocket;\nimport ApiEvent;\nvar sock:ApiSocket;\nvar myTimer:Timer = new Timer(1000);\nmyButton.addEventListener(MouseEvent.CLICK, testAPI);\nmyTimer.addEventListener(TimerEvent.TIMER, timedFunction);\nfunction testAPI(evt:MouseEvent):void {\n\tsock = new ApiSocket(\"172.17.1.1\", 8728);\n\tsock.addEventListener(ApiEvent.RECEIVED, receive);\n\tsock.addEventListener(Event.CONNECT, connected);\n\tsock.addEventListener(ApiEvent.LOGIN, loggedin);\n}\nfunction connected(evt:Event) {\n\ttrace(\"Connected. Logging in.\");\n\tsock.login(\"admin\",\"password\");\n}\nfunction loggedin(evt:ApiEvent) {\n\t\/\/ result can be done, trap or fatal\n\tif (evt.result == 'done') {\n\t\tmyTimer.start();\n\t}\n}\nfunction timedFunction(e:TimerEvent) {\n\tsock.sendRequest(\"\/interface\/wireless\/registration-table\/print\", \".tag=mytag\");\n}\nfunction receive(evt:ApiEvent) {\n\ttrace(\"Got Event with tag \" + evt.tag + \" result: \" + evt.result);\n\tif (evt.tag == 'mytag' &amp;&amp; evt.result == 'done') {\n\t\ttrace(\"Got rows: \" + evt.data.length);\n\t\tfor (var i:int = 0; i &lt; evt.data.length; ++i) {\n\t\t\ttrace(evt.data[i]['mac-address'])\n\t\t\ttrace(\"  \" + evt.data[i]['signal-strength'])\n\t\t}\n\t}\n}\n<\/pre>\n<p>Anyways, the code is nice to use in Flash AIR applications, since Flash AIR doesn&#8217;t have any security limitations to connecting with sockets. But if you were to use this class in a SWF file, you would need to do the right measures to let Flash accept your &#8220;cross domain scripting&#8221;.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Lately I have been familiar with the RouterOS series, when we got us a RouterBoard 493. At first I was a bit sceptical since I had never heard of RouterOS or Mikrotik. But as soon as I started testing this nice little hardware\/software out, I found out that this was one exceptional nice software. And [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,15,22,27],"tags":[],"class_list":["post-38","post","type-post","status-publish","format-standard","hentry","category-actionscript","category-flash","category-mikrotik","category-routeros"],"_links":{"self":[{"href":"http:\/\/130.61.186.249\/index.php\/wp-json\/wp\/v2\/posts\/38","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/130.61.186.249\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/130.61.186.249\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/130.61.186.249\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/130.61.186.249\/index.php\/wp-json\/wp\/v2\/comments?post=38"}],"version-history":[{"count":0,"href":"http:\/\/130.61.186.249\/index.php\/wp-json\/wp\/v2\/posts\/38\/revisions"}],"wp:attachment":[{"href":"http:\/\/130.61.186.249\/index.php\/wp-json\/wp\/v2\/media?parent=38"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/130.61.186.249\/index.php\/wp-json\/wp\/v2\/categories?post=38"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/130.61.186.249\/index.php\/wp-json\/wp\/v2\/tags?post=38"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}