API in Java

From MikroTik Wiki
Jump to navigation Jump to search

Summary

RouterOS API access library written in Java. This code is also capable to connect to IPv6 addresses.

Licensing

Code is provided as is and can be freely used freely. I, as a writer of code, am not responsible for anything that may arise from use of this code.

Usage

Simple example how this can be used. T3apiView class is the interface class that is not supplied here and is mentioned here only do show, how to start simple listener thread to receive the data replied by RouterOS

       ApiConnection ret = new ApiConnection("192.168.88.1", 8728);
       if (!ret.isConnected()) {
           ret.start();
           try {
               ret.join();
               if (ret.isConnected()) {
                   ret.login("admin", new char[0]);
               }
           } catch (InterruptedException ex) {
               Logger.getLogger(T3apiView.class.getName()).log(Level.SEVERE, null, ex);
               return null;
           }
       }
       aConn.sendCommand("/ip/address/print");
       DataReceiver dataRec = new DataReceiver(aConn, this);
       dataRec.start();


 import libAPI.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
  *
  * @author janisk
  */
 public class DataReceiver extends Thread {
 
   private ApiConnection aConn = null;
   T3apiView t3A = null;
 
   public DataReceiver(ApiConnection aConn, T3apiView t3A) {
       this.aConn = aConn;
       this.t3A = t3A;
   }
 
   @Override
   public void run() {
       String s = "";
       while (true) {
           try {
               s = aConn.getData();
               if (s != null) {
                   t3A.outputHere(s);
                   if (s.contains("!done")) {
                   }
               }
           } catch (InterruptedException ex) {
               Logger.getLogger(DataReceiver.class.getName()).log(Level.SEVERE, null, ex);
           }
       }
   }
 }

Code

Code that is ready to be compiled and used. In some places some comments may be missing. Compiled jar file of same java classes


ApiConn.java

Main file of the package

 package libAPI;
 
 /*
  * This contains connection. Everything should be here,
  * should operate with this class only
  */
 
 
 import java.io.*;
 import java.net.*;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
  *
  * @author janisk
  */
 public class ApiConn extends Thread {
 
   private Socket sock = null;
   private DataOutputStream out = null;
   private DataInputStream in = null;
   private String ipAddress;
   private int ipPort;
   private boolean connected = false;
   private String message = "Not connected";
   private ReadCommand readCommand = null;
   private WriteCommand writeCommand = null;
   private Thread listener = null;
   LinkedBlockingQueue queue = new LinkedBlockingQueue(40);
 
   /**
    * Constructor of the connection class
    * @param ipAddress - IP address of the router you want to conenct to
    * @param ipPort - port used for connection, ROS default is 8728
    */
   public ApiConn(String ipAddress, int ipPort) {
       this.ipAddress = ipAddress;
       this.ipPort = ipPort;
       this.setName("settings");
   }
 
   /**
    * State of connection
    * @return - if connection is established to router it returns true.
    */
   public boolean isConnected() {
       return connected;
   }
   public void disconnect() throws IOException{
       listener.interrupt();
       sock.close();
   }
   private void listen() {
       if (this.isConnected()) {
           if (readCommand == null) {
               readCommand = new ReadCommand(in, queue);
           }
           listener = new Thread(readCommand);
           listener.setDaemon(true);
           listener.setName("listener");
           listener.start();
 
       }
   }
 
   /**
    * to get IP address of the connection. Reads data from socket created.
    * @return InetAddress
    */
   public InetAddress getIpAddress() {
       return sock == null ? null : sock.getInetAddress();
   }
 
   /**
    * returns ip address that socket is asociated with.
    * @return InetAddress
    */
   public InetAddress getLocalIpAddress() {
       return sock == null ? null : sock.getLocalAddress();
   }
 
   /**
    * Socket remote port number
    * @return
    */
   public int getPort() {
       return sock == null ? null : sock.getPort();
   }
 
   /**
    * return local prot used by socket
    * @return
    */
   public int getLocalPort() {
       return sock == null ? null : sock.getLocalPort();
   }
 
   /**
    * Returns status message set up bu class.
    * @return
    */
   public String getMessage() {
       return message;
   }
 
   /**
    * sets and exectues command (sends it to RouterOS host connected)
    * @param s - command will be sent to RouterOS for example "/ip/address/print\n=follow="
    * @return
    */
   public String sendCommand(String s) {
       return writeCommand.setCommand(s).runCommand();
   }
 
   /**
    * exeecutes already set command.
    * @return returns status of the command sent
    */
   public String runCommand() {
       return writeCommand.runCommand();
   }
 
   /**
    * Tries to fech data that is repllied to commands sent. It will wait till it can return something.
    * @return returns data sent by RouterOS
    * @throws java.lang.InterruptedException
    */
   public String getData() throws InterruptedException {
       String s = (String) queue.take();
       return s;
   }
 
   /**
    * returns command that is set at this moment. And will be exectued if runCommand is exectued.
    * @return
    */
   public String getCommand() {
       return writeCommand.getCommand();
   }
 
   /**
    * set up method that will log you in
    * @param name - username of the user on the router
    * @param password - password for the user
    * @return
    */
   public String login(String name, char[] password) {
       this.sendCommand("/login");
       String s = "a";
       try {
           s = this.getData();
       } catch (InterruptedException ex) {
           Logger.getLogger(ApiConn.class.getName()).log(Level.SEVERE, null, ex);
           return "failed read #1";
       }
       if (!s.contains("!trap") && s.length() > 4) {
           String[] tmp = s.trim().split("\n");
           if (tmp.length > 1) {
               tmp = tmp[1].split("=ret=");
               s = "";
               String transition = tmp[tmp.length - 1];
               String chal = "";
               chal = Hasher.hexStrToStr("00") + new String(password) + Hasher.hexStrToStr(transition);
               chal = Hasher.hashMD5(chal);
               String m = "/login\n=name=" + name + "\n=response=00" + chal;
               s = this.sendCommand(m);
               try {
                   s = this.getData();
               } catch (InterruptedException ex) {
                   Logger.getLogger(ApiConn.class.getName()).log(Level.SEVERE, null, ex);
                   return "failed read #2";
               }
               if (s.contains("!done")) {
                   if (!s.contains("!trap")) {
                       return "Login successful";
                   }
               }
           }
       }
       return "Login failed";
   }
   @Override
   public void run() {
       try {
           InetAddress ia = InetAddress.getByName(ipAddress);
           if (ia.isReachable(1000)) {
               sock = new Socket(ipAddress, ipPort);
               in = new DataInputStream(sock.getInputStream());
               out = new DataOutputStream(sock.getOutputStream());
               connected = true;
               readCommand = new ReadCommand(in, queue);
               writeCommand = new WriteCommand(out);
               this.listen();
               message = "Connected";
           } else {
               message = "Not reachable";
           }
       } catch (UnknownHostException ex) {
           connected = false;
           message = ex.getMessage();
           ex.printStackTrace();
       } catch (IOException ex) {
           connected = false;
           message = ex.getMessage();
           ex.printStackTrace();
       }
   }
 }

Hasher.java

Helper functions to perform some tasks


 package libAPI;
 
 /*
  * Helper.java
  *
  * Created on 08 June 2007, 11:25
  *
  * To change this template, choose Tools | Template Manager
  * and open the template in the editor.
  */
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 
 /**
  *
  * @author janisk
  */
 public class Hasher {
 
   /**
    * makes MD5 hash of string for use with RouterOS API
    * @param s - variable to make hacsh from
    * @return
    */
   static public String hashMD5(String s) {
       String md5val = "";
       MessageDigest algorithm = null;
       try {
           algorithm = MessageDigest.getInstance("MD5");
       } catch (NoSuchAlgorithmException nsae) {
           System.out.println("Cannot find digest algorithm");
           System.exit(1);
       }
       byte[] defaultBytes = new byte[s.length()];
       for (int i = 0; i < s.length(); i++) {
           defaultBytes[i] = (byte) (0xFF & s.charAt(i));
       }
       algorithm.reset();
       algorithm.update(defaultBytes);
       byte messageDigest[] = algorithm.digest();
       StringBuffer hexString = new StringBuffer();
       for (int i = 0; i < messageDigest.length; i++) {
           String hex = Integer.toHexString(0xFF & messageDigest[i]);
           if (hex.length() == 1) {
               hexString.append('0');
           }
           hexString.append(hex);
       }
       return hexString.toString();
   }
 
   /**
    * converts hex value string to normal strint for use with RouterOS API
    * @param s - hex string to convert to
    * @return - converted string.
    */
   static public String hexStrToStr(String s) {
       String ret = "";
       for (int i = 0; i < s.length(); i += 2) {
           ret += (char) Integer.parseInt(s.substring(i, i + 2), 16);
       }
       return ret;
   }
 }

ReadCommand.java

This reads returns of the API

 package libAPI;
 
 /*
  * CommandRead.java
  *
  * Created on 19 June 2007, 10:29
  *
  * To change this template, choose Tools | Template Manager
  * and open the template in the editor.
  */
 
 import java.io.*;
 import java.util.concurrent.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
  *
  * @author janisk
  */
 public class ReadCommand implements Runnable {
 
   private DataInputStream in = null;
   LinkedBlockingQueue queue = null;
 
   /**
    * Creates a new instance of CommandRead
    * @param in - Data input stream of socket
    * @param queue - data output inteface
    */
   public ReadCommand(DataInputStream in, LinkedBlockingQueue queue) {
       this.in = in;
       this.queue = queue;
   }
 
 
   @Override
   public void run() {
       byte b = 0;
       String s = "";
       char ch;
       int a = 0;
       while (true) {
           int sk = 0;
           try {
               a = in.read();
           } catch (IOException ex) {
               return;
           }
           if (a != 0 && a > 0) {
               if (a < 0x80) {
                   sk = a;
               } else {
                   if (a < 0xC0) {
                       a = a << 8;
                       try {
                           a += in.read();
                       } catch (IOException ex) {
                           return;
                       }
                       sk = a ^ 0x8000;
                   } else {
                       if (a < 0xE0) {
                           try {
                               for (int i = 0; i < 2; i++) {
                                   a = a << 8;
                                   a += in.read();
                               }
                           } catch (IOException ex) {
                               Logger.getLogger(ReadCommand.class.getName()).log(Level.SEVERE, null, ex);
                               return;
                           }
                           sk = a ^ 0xC00000;
                       } else {
                           if (a < 0xF0) {
                               try {
                                   for (int i = 0; i < 3; i++) {
                                       a = a << 8;
                                       a += in.read();
                                   }
                               } catch (IOException ex) {
                                   Logger.getLogger(ReadCommand.class.getName()).log(Level.SEVERE, null, ex);
                                   return;
                               }
                               sk = a ^ 0xE0000000;
                           } else {
                               if (a < 0xF8) {
                                   try {
                                       a = 0;
                                       for (int i = 0; i < 5; i++) {
                                           a = a << 8;
                                           a += in.read();
                                       }
                                   } catch (IOException ex) {
                                       Logger.getLogger(ReadCommand.class.getName()).log(Level.SEVERE, null, ex);
                                       return;
                                   }
                               } else {
                               }
                           }
                       }
                   }
               }
               s += "\n";
               byte[] bb = new byte[sk];
               try {
                   a = in.read(bb, 0, sk);
               } catch (IOException ex) {
                   a = 0;
                   ex.printStackTrace();
                   return;
               }
               if (a > 0) {
                   s += new String(bb);
               }
           } else if (b == -1) {
               System.out.println("Error, it should not happen ever, or connected to wrong port");
           } else {
               try {
                   queue.put(s);
               } catch (InterruptedException ex) {
                   ex.printStackTrace();
                   System.out.println("exiting reader");
                   return;
               }
               s = "";
           }
       }
   }
 }

WriteCommand.java

All writing to RouterOS API is done using this.


 package libAPI;
   
 /*
  * To change this template, choose Tools | Templates
  * and open the template in the editor.
  */
 
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
  *
  * @author janisk
  */
 public class WriteCommand {
 
   private byte[] len = {0};
   private DataOutputStream out = null;
   private String command = "";
 
   WriteCommand(DataOutputStream out, String command) {
       this.out = out;
       this.command = command.replaceAll("\n", "").trim();
   }
 
   WriteCommand(DataOutputStream out) {
       this.out = out;
   }
 
   WriteCommand setCommand(String command) {
       this.command = command.trim();
       return this;
   }
 
   String getCommand() {
       return command;
   }
 
   private byte[] writeLen(String command) {
       Integer i = null;
       String s = "";
       String ret = "";
       if (command.length() < 0x80) {
           i = command.length();
       } else if (command.length() < 0x4000) {
           i = Integer.reverseBytes(command.length() | 0x8000);
       } else if (command.length() < 0x20000) {
           i = Integer.reverseBytes(command.length() | 0xC00000);
       } else if (command.length() < 10000000) {
           i = Integer.reverseBytes(command.length() | 0xE0000000);
       } else {
           i = Integer.reverseBytes(command.length());
       }
       s = Integer.toHexString(i);
       if (s.length() < 2) {
           return new byte[]{i.byteValue()};
       } else {
           for (int j = 0; j < s.length(); j += 2) {
               ret += (char) Integer.parseInt(s.substring(j, j + 2), 16) != 0 ? (char) Integer.parseInt(s.substring(j, j + 2), 16) : "";
           }
       }
       char[] ch = ret.toCharArray();
       return ret.getBytes();
   }
 
   String runCommand() {
       try {
           byte[] ret = new byte[0];
           if (!command.contains("\n")) {
               int i = 0;
               byte[] b = writeLen(command);
               int retLen = b.length + command.length() + 1;
               ret = new byte[retLen];
               for (i = 0; i < b.length; i++) {
                   ret[i] = b[i];
               }
               for (byte c : command.getBytes("US-ASCII")) {
                   ret[i++] = c;
               }
           } else {
               String[] str = command.split("\n");
               int i = 1;
               int[] iTmp = new int[str.length];
               for (int a = 0; a < str.length; a++) {
                   iTmp[a] = writeLen(str[a]).length + str[a].length();
               }
               for (int b : iTmp) {
                   i += b;
               }
               ret = new byte[i];
               int counter = 0;
               for (int a = 0; a < str.length; a++) {
                   int j = 0;
                   byte[] b = writeLen(str[a]);
                   for (j = 0; j < b.length; j++) {
                       ret[counter++] = b[j];
                   }
                   for (byte c : str[a].getBytes("US-ASCII")) {
                       ret[counter++] = c;
                   }
               }
           }
           out.write(ret);
           return "Sent successfully";
       } catch (IOException ex) {
           Logger.getLogger(WriteCommand.class.getName()).log(Level.SEVERE, null, ex);
           return "failed";
       }
   }
 }

See also

API
Usage notes on API