Added to GitHub

This commit is contained in:
GideonLeGrange 2013-07-11 15:32:18 +02:00
parent a57aecc05b
commit afec066586
20 changed files with 1318 additions and 0 deletions

36
.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# Java
*.class
*.jar
*.war
*.ear
# Scala
*.class
*.log
# sbt specific
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/
# Scala-IDE specific
.scala_dependencies
# Maven
target/
# Netbeans
build/
dist/
#nbproject/
# IntelliJ
.idea/
# Eclipse
.settings/
# Project
Mtik.java

25
pom.xml Normal file
View File

@ -0,0 +1,25 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>adept</groupId>
<artifactId>mikrotik</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mikrotik</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,31 @@
package me.legrange.mikrotik;
/**
* Thrown when the Mikrotik returns an error when receiving our command.
* @author GideonLeGrange
*/
public class ApiCommandException extends MikrotikApiException {
/** return the tag associated with this exception, if there is one */
public String getTag() {
return tag;
}
ApiCommandException(String msg) {
super(msg);
}
ApiCommandException(String msg, Throwable err) {
super(msg, err);
}
ApiCommandException(Error err) {
super(err.getMessage());
tag = err.getTag();
}
private String tag = null;
}

View File

@ -0,0 +1,430 @@
package me.legrange.mikrotik;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
/**
* The Mikrotik API connection. This is the class used to connect to a remote
* Mikrotik and send commands to it.
*
* @author GideonLeGrange
*/
public class ApiConnection {
/**
* Create a new API connection to the give device on the supplied port
*/
public static ApiConnection connect(String host, int port) throws ApiConnectionException {
ApiConnection con = new ApiConnection();
con.open(host, port);
return con;
}
/**
* Create a new API connection to the give device on the default API port
*/
public static ApiConnection connect(String host) throws ApiConnectionException {
return connect(host, DEFAULT_PORT);
}
/**
* State of connection
*
* @return - if connection is established to router it returns true.
*/
public boolean isConnected() {
return connected;
}
/**
* Disconnect from the remote API
*/
public void disconnect() throws ApiConnectionException {
if (!connected) {
throw new ApiConnectionException(("Not/no longer connected to remote Mikrotik"));
}
connected = false;
reader.interrupt();
try {
sock.close();
} catch (IOException ex) {
throw new ApiConnectionException(String.format("Error closing socket: %s", ex.getMessage()), ex);
}
}
/** cancel a command */
public void cancel(Command can) throws MikrotikApiException {
Command cmd = new Command("/cancel");
cmd.addParameter("tag", can.getTag());
execute(cmd);
}
/**
* 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 void login(String name, String pwd) throws MikrotikApiException, ApiCommandException, InterruptedException {
List<Result> list = execute("/login");
Result res = list.get(0);
String hash = res.get("ret");
String chal = Util.hexStrToStr("00") + new String(makePass(pwd)) + Util.hexStrToStr(hash);
chal = Util.hashMD5(chal);
execute("/login name=" + name + " response=00" + chal);
}
private List<Result> execute(Command cmd) throws MikrotikApiException {
SyncListener l = new SyncListener();
execute(cmd, l);
return l.getResults();
}
public List<Result> execute(String cmd) throws MikrotikApiException {
return execute(Parser.parse(cmd));
}
public Command execute(String cmd, ResultListener lis) throws MikrotikApiException {
return execute(Parser.parse(cmd), lis);
}
private Command execute(Command cmd, ResultListener lis) throws MikrotikApiException {
String tag = nextTag();
cmd.setTag(tag);
listeners.put(tag, lis);
try {
Util.write(cmd, out);
} catch (UnsupportedEncodingException ex) {
throw new ApiDataException(ex.getMessage(), ex);
} catch (IOException ex) {
throw new ApiConnectionException(ex.getMessage(), ex);
}
return cmd;
}
private ApiConnection() {
}
/**
* Start the API. Connects to the Mikrotik
*/
private void open(String host, int port) throws ApiConnectionException {
try {
InetAddress ia = InetAddress.getByName(host);
if (ia.isReachable(1000)) {
sock = new Socket(ia, port);
in = new DataInputStream(sock.getInputStream());
out = new DataOutputStream(sock.getOutputStream());
connected = true;
reader = new Reader();
reader.setDaemon(true);
reader.start();
processor = new Processor();
processor.setDaemon(true);
processor.start();
} else {
throw new ApiConnectionException(String.format("Host '%s' port %d is uncreachable", host, port));
}
} catch (UnknownHostException ex) {
connected = false;
throw new ApiConnectionException(String.format("Unknown host '%s'", host), ex);
} catch (IOException ex) {
connected = false;
throw new ApiConnectionException(String.format("Error connecting to '%s': %s", host, ex.getMessage()), ex);
}
}
private char[] makePass(String pass) {
if (true) {
return pass.toCharArray();
}
char[] res = new char[pass.length() + 1];
System.arraycopy(pass.toCharArray(), 0, res, 0, pass.length());
res[pass.length()] = 0x0;
return res;
}
private synchronized String nextTag() {
_tag++;
return Integer.toHexString(_tag);
}
private static final int DEFAULT_PORT = 8728;
private Socket sock = null;
private DataOutputStream out = null;
private DataInputStream in = null;
private boolean connected = false;
private Reader reader;
private Processor processor;
private final Map<String, ResultListener> listeners = new HashMap<String, ResultListener>();
private Integer _tag = 0;
/**
* thread to read data from the socket and process it into Strings
*/
private class Reader extends Thread {
private String take() throws ApiConnectionException, ApiDataException {
Object val = null;
try {
val = queue.take();
} catch (InterruptedException ex) {
throw new ApiConnectionException("Interrupted while reading data from queue.", ex);
}
if (val instanceof ApiConnectionException) {
throw (ApiConnectionException) val;
} else if (val instanceof ApiDataException) {
throw (ApiDataException) val;
}
return (String) val;
}
private boolean isEmpty() {
return queue.isEmpty();
}
@Override
public void run() {
while (connected) {
try {
String s = Util.decode(in);
if (s != null) {
queue.put(s);
}
} catch (ApiDataException ex) {
try {
queue.put(ex);
} catch (InterruptedException ex2) {
}
} catch (ApiConnectionException ex) {
} catch (InterruptedException ex1) {
}
}
}
private LinkedBlockingQueue queue = new LinkedBlockingQueue(40);
}
/**
* Thread to take the received strings and process it into Result objects
*/
private class Processor extends Thread {
@Override
public void run() {
while (connected) {
Response res;
try {
res = unpack();
} catch (ApiCommandException ex) {
String tag = ex.getTag();
if (tag != null) {
res = new Error(tag, ex.getMessage());
} else {
continue;
}
} catch (MikrotikApiException ex) {
ex.printStackTrace();
continue;
}
ResultListener l = listeners.get(res.getTag());
if (l != null) {
if (res instanceof Result) {
l.receive((Result) res);
} else {
if (res instanceof Done) {
listeners.remove(res.getTag());
}
if (l instanceof ResponseListener) {
ResponseListener rl = (ResponseListener) l;
if (res instanceof Done) {
rl.completed((Done) res);
} else if (res instanceof Error) {
rl.error((Error) res);
}
}
}
}
}
}
private void nextLine() throws ApiConnectionException, ApiDataException {
if (lines.isEmpty()) {
String block = reader.take();
String parts[] = block.split("\n");
lines.addAll(Arrays.asList(parts));
}
line = lines.remove(0);
}
private boolean hasNextLine() {
return !lines.isEmpty() || !reader.isEmpty();
}
private Response unpack() throws MikrotikApiException {
if (line == null) {
nextLine();
}
if (line.equals("!re")) {
return unpackRe();
} else if (line.equals("!done")) {
return unpackDone();
} else if (line.equals("!trap")) {
return unpackError();
} else if (line.equals("!halt")) {
return unpackError();
} else {
throw new ApiDataException(String.format("Unexpected line '%s'", line));
}
}
private Result unpackRe() throws ApiDataException, ApiConnectionException {
nextLine();
int l = 0;
Result res = new Result();
while (!line.startsWith(("!"))) {
l++;
if (line.startsWith(("="))) {
String parts[] = line.split("=", 3);
if (parts.length == 3) {
res.put(parts[1], parts[2]);
} else {
throw new ApiDataException(String.format("Malformed line '%s'", line));
}
} else if (line.startsWith(".tag=")) {
String parts[] = line.split("=", 2);
if (parts.length == 2) {
res.setTag(parts[1]);
}
} else {
throw new ApiDataException(String.format("Unexpected line '%s'", line));
}
if (hasNextLine()) {
nextLine();
} else {
line = null;
break;
}
}
return res;
}
private Done unpackDone() throws MikrotikApiException {
Done done = new Done(null);
if (hasNextLine()) {
nextLine();
while (!line.startsWith("!")) {
if (line.startsWith(".tag=")) {
String parts[] = line.split("=", 2);
if (parts.length == 2) {
done.setTag(parts[1]);
}
} else if (line.startsWith(("=ret"))) {
String parts[] = line.split("=", 3);
if (parts.length == 3) {
done.setHash(parts[2]);
} else {
throw new ApiDataException(String.format("Malformed line '%s'", line));
}
}
if (hasNextLine()) {
nextLine();
} else {
line = null;
break;
}
}
}
return done;
}
private Error unpackError() throws MikrotikApiException {
nextLine();
Error err = new Error();
if (hasNextLine()) {
while (!line.startsWith("!")) {
if (line.startsWith(".tag=")) {
String parts[] = line.split("=", 2);
if (parts.length == 2) {
err.setTag(parts[1]);
}
} else if (line.startsWith("=message=")) {
err.setMessage(line.split("=", 3)[2]);
}
if (hasNextLine()) {
nextLine();
} else {
line = null;
break;
}
}
}
return err;
}
private void queue(Response res) {
String tag = res.getTag();
if (tag != null) {
ResultListener rl = listeners.get(tag);
if (rl != null) {
if (res instanceof Result) {
rl.receive((Result) res);
} else {
// rl.error((Error)res);
}
}
}
}
private List<String> lines = new LinkedList<String>();
private String line;
}
private class SyncListener implements ResponseListener {
public synchronized void error(Error err) {
this.err = err;
notify();
}
public synchronized void completed(Done done) {
if (done.getHash() != null) {
Result res = new Result();
res.put("ret", done.getHash());
results.add(res);
}
notify();
}
public void receive(Result result) {
results.add(result);
}
private List<Result> getResults() throws ApiCommandException, ApiConnectionException {
try {
synchronized (this) {
wait();
}
} catch (InterruptedException ex) {
throw new ApiConnectionException(ex.getMessage(), ex);
}
if (err != null) {
throw new ApiCommandException(err);
}
return results;
}
private List<Result> results = new LinkedList<Result>();
private Error err;
}
}

View File

@ -0,0 +1,19 @@
package me.legrange.mikrotik;
/**
* Exception thrown if the Api experiences a connection problem
* @author GideonLeGrange
*/
public class ApiConnectionException extends MikrotikApiException {
ApiConnectionException(String msg) {
super(msg);
}
ApiConnectionException(String msg, Throwable err) {
super(msg, err);
}
}

View File

@ -0,0 +1,19 @@
package me.legrange.mikrotik;
/**
* Thrown if there is a problem unpacking data from the Api.
* @author GideonLeGrange
*/
public class ApiDataException extends MikrotikApiException {
ApiDataException(String msg) {
super(msg);
}
ApiDataException(String msg, Throwable err) {
super(msg, err);
}
}

View File

@ -0,0 +1,18 @@
package me.legrange.mikrotik;
/**
* Thrown if the API cannot log in
* @author GideonLeGrange
*/
public class ApiLoginException extends MikrotikApiException {
ApiLoginException(String msg) {
super(msg);
}
ApiLoginException(String msg, Throwable err) {
super(msg, err);
}
}

View File

@ -0,0 +1,81 @@
package me.legrange.mikrotik;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/**
* A command sent to a Mikrotik. This internal class is used to build complex commands
* with parameters, queries and property lists.
*
* @author GideonLeGrange
*/
public class Command {
@Override
public String toString() {
return String.format("cmd[%s] = %s, params = %s, queries = %s, props=%s ", tag, cmd, params, queries, properties);
}
Command(String cmd) {
if (!cmd.startsWith("/")) {
cmd = "/" + cmd;
}
this.cmd = cmd;
}
String getCommand() {
return cmd;
}
/**
* Add a parameter to a command.
*/
void addParameter(String name, String value) {
params.add(new Parameter(name, value));
}
/**
* Add a valueless parameter to the command
*/
void addParameter(Parameter param) {
params.add(param);
}
/**
* Add a property to include in a result
*/
void addProperty(String... names) {
properties.addAll(Arrays.asList(names));
}
void addQuery(String... queries) {
this.queries.addAll(Arrays.asList(queries));
}
void setTag(String tag) {
this.tag = tag;
}
List<String> getQueries() {
return queries;
}
String getTag() {
return tag;
}
List<String> getProperties() {
return properties;
}
List<Parameter> getParameters() {
return params;
}
private String cmd;
private List<Parameter> params = new LinkedList<Parameter>();
private List<String> queries = new LinkedList<String>();
private List<String> properties = new LinkedList<String>();
private String tag;
}

View File

@ -0,0 +1,23 @@
package me.legrange.mikrotik;
/**
* Internal representation of !done
* @author GideonLeGrange
*/
public class Done extends Response {
Done(String tag) {
super(tag);
}
void setHash(String hash) {
this.hash = hash;
}
String getHash() {
return hash;
}
private String hash;
}

View File

@ -0,0 +1,28 @@
package me.legrange.mikrotik;
/**
* Used to encapsulate API error information. We need to pass both the message and the tag (if one was used).
* @author GideonLeGrange
*/
public class Error extends Response {
Error(String tag, String message) {
super(tag);
this.message = message;
}
Error() {
super(null);
}
String getMessage() {
return message;
}
void setMessage(String message) {
this.message = message;
}
private String message;
}

View File

@ -0,0 +1,17 @@
package me.legrange.mikrotik;
/**
* Thrown by the Mikrotik API to indicate errors
*
* @author GideonLeGrange
*/
public class MikrotikApiException extends Exception {
MikrotikApiException(String msg) {
super(msg);
}
MikrotikApiException(String msg, Throwable err) {
super(msg, err);
}
}

View File

@ -0,0 +1,41 @@
package me.legrange.mikrotik;
/**
* A command parameter
*
* @author GideonLeGrange
*/
class Parameter {
@Override
public String toString() {
if (hasValue()) {
return String.format("%s=%s", name, value);
} else {
return name;
}
}
Parameter(String name, String value) {
this.name = name;
this.value = value;
}
Parameter(String name) {
this(name, null);
}
boolean hasValue() {
return value != null;
}
String getName() {
return name;
}
String getValue() {
return value;
}
private String name;
private String value;
}

View File

@ -0,0 +1,19 @@
package me.legrange.mikrotik;
/**
* Exception thrown if the parser encounters an error while parsing a command line.
* @author GideonLeGrange
*/
public class ParseException extends MikrotikApiException {
ParseException(String msg) {
super(msg);
}
ParseException(String msg, Throwable err) {
super(msg, err);
}
}

View File

@ -0,0 +1,208 @@
package me.legrange.mikrotik;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
* Parse the pseudo-command line into command objects.
* @author GideonLeGrange
*/
class Parser {
/** parse the given bit of text into a Command object */
static Command parse(String text) throws ParseException {
Parser parser = new Parser(text);
return parser.parse();
}
/** run parse on the internal data and return the command object */
private Command parse() throws ParseException {
next();
expect(Token.COMMAND);
cmd = new Command(text);
next();
while (!((token == Token.WHERE) || (token == Token.RETURN) || (token == Token.EOL))) {
param();
}
if (token == Token.WHERE) {
where();
}
if (token == Token.RETURN) {
returns();
}
expect(Token.EOL);
return cmd;
}
private void param() throws ParseException {
String name = text;
next();
if (token == Token.EQUALS) {
next();
expect(Token.NAME);
cmd.addParameter(new Parameter(name, text));
next();
}
else {
cmd.addParameter(new Parameter(name));
}
}
private void where() throws ParseException {
next(); // swallow the word "where"
expr();
}
private void expr() throws ParseException {
expect(Token.NOT, Token.NAME);
switch (token) {
case NOT:
notExpr();
break;
case NAME: {
String name = text;
next();
expect(Token.EQUALS, Token.LESS, Token.MORE);
switch (token) {
case EQUALS:
eqExpr(name);
break;
case LESS:
lessExpr(name);
break;
case MORE:
moreExpr(name);
break;
default:
hasExpr(name);
}
}
break;
}
// if you get here, you had a expression, see if you want more.
switch (token) {
case AND : andExpr();
break;
case OR : orExpr();
}
}
private void andExpr() throws ParseException {
next(); // eat and
expr();
cmd.addQuery("?#&");
}
private void orExpr() throws ParseException {
next(); // eat or
expr();
cmd.addQuery("?#|");
}
private void notExpr() throws ParseException {
next(); // eat not
expr();
cmd.addQuery("?#!");
}
private void eqExpr(String name) {
next(); // eat =
cmd.addQuery(String.format("?%s=%s", name, text));
next();
}
private void lessExpr(String name) {
next(); // eat <
cmd.addQuery(String.format("?<%s=%s", name, text));
next();
}
private void moreExpr(String name) {
next(); // eat >
cmd.addQuery(String.format("?>%s=%s", name, text));
next();
}
private void hasExpr(String name) {
cmd.addQuery(String.format("?%s", name));
}
private void returns() throws ParseException {
next();
expect(Token.NAME);
List<String> props = new LinkedList<String>();
while (!(token == Token.EOL)) {
if (token != Token.COMMA) {
props.add(text);
}
next();
}
cmd.addProperty(props.toArray(new String[]{}));
}
private void expect(Token...tokens) throws ParseException {
for (Token want : tokens) {
if (this.token == want) return;
}
throw new ParseException(String.format("Expected %s but found %s", Arrays.asList(tokens), this.token));
}
private void next() {
if (!words.isEmpty()) {
text = words.remove(0);
if (text.startsWith("/")) {
token = Token.COMMAND;
} else {
Token t = lookup.get(text);
if (t != null) {
token = t;
text = "";
} else {
token = Token.NAME;
}
}
} else {
token = Token.EOL;
text = "";
}
// System.out.printf("%s: %s\n", token, text);
}
private Parser(String text) {
text = text.trim();
StringTokenizer st = new StringTokenizer(text, " \t,=", true);
while (st.hasMoreElements()) {
String t = st.nextToken().trim();
if (!t.equals("")) {
words.add(t);
}
}
}
private final List<String> words = new LinkedList<String>();
private String text;
private Token token;
private Command cmd;
private static final Map<String, Token> lookup = new HashMap<String, Token>();
private enum Token {
COMMAND, WHERE, RETURN, EOL, NOT, AND, OR, NAME, EQUALS, MORE, LESS, COMMA;
}
static {
lookup.put("where", Token.WHERE);
lookup.put("return", Token.RETURN);
lookup.put("not", Token.NOT);
lookup.put("and", Token.AND);
lookup.put("or", Token.OR);
lookup.put("=", Token.EQUALS);
lookup.put(">", Token.MORE);
lookup.put("<", Token.LESS);
lookup.put(",", Token.COMMA);
}
}

View File

@ -0,0 +1,28 @@
package me.legrange.mikrotik;
/**
* Super type of possible API responses
*
* @author GideonLeGrange
*/
abstract class Response {
public String getTag() {
return tag;
}
@Override
public String toString() {
return String.format("%s: tag=%s", getClass().getSimpleName(), tag);
}
void setTag(String tag) {
this.tag = tag;
}
protected Response(String tag) {
this.tag = tag;
}
private String tag;
}

View File

@ -0,0 +1,17 @@
package me.legrange.mikrotik;
/**
* A listener that receives life cycle command events from the Mikrotik API,
* and not just results.
* @author GideonLeGrange
*/
public interface ResponseListener extends ResultListener {
/** called if the command associated with this listener experiences a trap */
void error(Error err);
/** called when the command associated with this listener is done */
void completed(Done done);
}

View File

@ -0,0 +1,78 @@
package me.legrange.mikrotik;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* A result from an API command.
* @author GideonLeGrange
*/
public class Result extends Response implements Map<String, String> {
public Result() {
super(null);
this.map = new HashMap<String, String>();
}
public String get(String key) {
return map.get(key);
}
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public String toString() {
return String.format("tag=%s, data=%s", getTag(), map);
}
public int size() {
return map.size();
}
public boolean containsKey(Object o) {
return map.containsKey(o);
}
public boolean containsValue(Object o) {
return map.containsValue(o);
}
public String get(Object o) {
return map.get(o);
}
public String put(String k, String v) {
return map.put(k, v);
}
public String remove(Object o) {
return map.remove(o);
}
public void putAll(Map<? extends String, ? extends String> map) {
this.map.putAll(map);
}
public void clear() {
map.clear();
}
public Set<String> keySet() {
return map.keySet();
}
public Collection<String> values() {
return map.values();
}
public Set<Entry<String, String>> entrySet() {
return map.entrySet();
}
private final Map<String, String> map;
}

View File

@ -0,0 +1,11 @@
package me.legrange.mikrotik;
/**
* Implement this interface to receive command results from the Mikrotik Api.
* @author GideonLeGrange
*/
public interface ResultListener {
void receive(Result result);
}

View File

@ -0,0 +1,177 @@
package me.legrange.mikrotik;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
/**
* Utility library that handles the low level encoding required by the Mikrotik API.
* @author GideonLeGrange. Possibly some code by janisk left.
*/
class Util {
static void write(Command cmd, OutputStream out) throws UnsupportedEncodingException, IOException {
System.out.println("cmd = '" + cmd + "'");
encode(cmd.getCommand(), out);
for (Parameter param : cmd.getParameters()) {
encode(String.format("=%s=%s", param.getName(), param.hasValue() ? param.getValue() : "" ), out);
}
String tag = cmd.getTag();
if ((tag != null) && !tag.equals("")) {
encode(String.format(".tag=%s", tag), out);
}
List<String> props = cmd.getProperties();
if (!props.isEmpty()) {
StringBuilder buf = new StringBuilder("=.proplist=");
for (int i = 0; i < props.size(); ++i) {
if (i > 0)
buf.append(",");
buf.append(props.get(i));
}
encode(buf.toString(), out);
}
for (String query : cmd.getQueries()) {
encode(query, out);
}
out.write(0);
}
/** encode text using Mikrotik's encoding scheme and write it to an output stream. */
private static void encode(String word, OutputStream out) throws UnsupportedEncodingException, IOException {
byte bytes[] = word.getBytes("US-ASCII");
int len = bytes.length;
if (len < 0x80) {
out.write(len);
} else if (len < 0x4000) {
len = len | 0x8000;
out.write(len >> 8);
out.write(len);
} else if (len < 0x20000) {
len = len | 0xC00000;
out.write(len >> 16);
out.write(len >> 8);
out.write(len);
} else if (len < 0x10000000) {
len = len | 0xE0000000;
out.write(len >> 24);
out.write(len >> 16);
out.write(len >> 8);
out.write(len);
} else {
out.write(0xF0);
out.write(len >> 24);
out.write(len >> 16);
out.write(len >> 8);
out.write(len);
}
out.write(bytes);
}
/** decode bytes from an input stream of Mikrotik protocol sentences into text */
static String decode(InputStream in) throws ApiDataException, ApiConnectionException {
StringBuilder res = new StringBuilder();
decode(in, res);
return res.toString();
}
/** decode bytes from an input stream into Mikrotik protocol sentences */
private static void decode(InputStream in, StringBuilder result) throws ApiDataException, ApiConnectionException {
try {
int len = readLen(in);
if (len > 0) {
byte buf[] = new byte[len];
for (int i = 0; i < len; ++i) {
int c = in.read();
if (c < 0) {
throw new ApiDataException("Truncated data. Expected to read more bytes");
}
buf[i] = (byte) (c & 0xFF);
}
String res = new String(buf);
if (result.length() > 0)
result.append("\n");
result.append(res);
decode(in, result);
}
} catch (IOException ex) {
throw new ApiConnectionException(ex.getMessage(), ex);
}
}
/** read length bytes from stream and return length of coming word */
static private int readLen(InputStream in) throws IOException {
int c = in.read();
if (c > 0) {
if ((c & 0x80) == 0) {
} else if ((c & 0xC0) == 0x80) {
c = c & ~0xC0;
c = (c << 8) | in.read();
} else if ((c & 0xE0) == 0xC0) {
c = c & ~0xE0;
c = (c << 8) | in.read();
c = (c << 8) | in.read();
} else if ((c & 0xF0) == 0xE0) {
c = c & ~0xF0;
c = (c << 8) | in.read();
c = (c << 8) | in.read();
c = (c << 8) | in.read();
} else if ((c & 0xF8) == 0xF0) {
c = in.read();
c = (c << 8) | in.read();
c = (c << 8) | in.read();
c = (c << 8) | in.read();
c = (c << 8) | in.read();
}
}
return c;
}
/**
* makes MD5 hash of string for use with RouterOS API
*
* @param s - variable to make hash from
* @return - the md5 hash
*/
static String hashMD5(String s) throws ApiDataException {
MessageDigest algorithm = null;
try {
algorithm = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsae) {
throw new ApiDataException("Cannot find MD5 digest algorithm");
}
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();
StringBuilder hexString = new StringBuilder();
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 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;
}
}

View File

@ -0,0 +1,12 @@
command = action [ query ] [ return ]
action = ("/" word)+
query = "where" expr
expr = expr "and" expr | expr "or" expr | "not" expr | hasExpr | eqExpr | lessExpr | moreExpr
hasExpr = name
eqExpr = name "=" value
lessExpr = name "<" value
moreExpr = name ">" value
return = "return" (name)+