commit 63f74c67bae37110c3c72653f16e685803b1c19f Author: mh Date: Tue Aug 18 21:54:15 2015 +0200 Initial diff --git a/MikroTikAPI/build.xml b/MikroTikAPI/build.xml new file mode 100644 index 0000000..f990724 --- /dev/null +++ b/MikroTikAPI/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project MikroTikAPI. + + + diff --git a/MikroTikAPI/nbproject/build-impl.xml b/MikroTikAPI/nbproject/build-impl.xml new file mode 100644 index 0000000..639de4d --- /dev/null +++ b/MikroTikAPI/nbproject/build-impl.xml @@ -0,0 +1,1413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MikroTikAPI/nbproject/genfiles.properties b/MikroTikAPI/nbproject/genfiles.properties new file mode 100644 index 0000000..67c8bd8 --- /dev/null +++ b/MikroTikAPI/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=4d236977 +build.xml.script.CRC32=a2932288 +build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=4d236977 +nbproject/build-impl.xml.script.CRC32=06e81169 +nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 diff --git a/MikroTikAPI/nbproject/private/config.properties b/MikroTikAPI/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/MikroTikAPI/nbproject/private/private.properties b/MikroTikAPI/nbproject/private/private.properties new file mode 100644 index 0000000..8df0121 --- /dev/null +++ b/MikroTikAPI/nbproject/private/private.properties @@ -0,0 +1,6 @@ +compile.on.save=true +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=/home/mh/.netbeans/8.0.2/build.properties diff --git a/MikroTikAPI/nbproject/private/private.xml b/MikroTikAPI/nbproject/private/private.xml new file mode 100644 index 0000000..6b5984a --- /dev/null +++ b/MikroTikAPI/nbproject/private/private.xml @@ -0,0 +1,11 @@ + + + + + + file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java + file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/sample/TestApp.java + file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/OPs.java + + + diff --git a/MikroTikAPI/nbproject/project.properties b/MikroTikAPI/nbproject/project.properties new file mode 100644 index 0000000..a4c8f2e --- /dev/null +++ b/MikroTikAPI/nbproject/project.properties @@ -0,0 +1,74 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=MikroTikAPI +application.vendor=mh +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/MikroTikAPI.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +includes=** +jar.compress=false +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=com.eightOceans.mikroTik.sample.TestApp +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=true +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/MikroTikAPI/nbproject/project.xml b/MikroTikAPI/nbproject/project.xml new file mode 100644 index 0000000..53a5132 --- /dev/null +++ b/MikroTikAPI/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2seproject + + + MikroTikAPI + + + + + + + + + diff --git a/MikroTikAPI/src/README.txt b/MikroTikAPI/src/README.txt new file mode 100644 index 0000000..f0b1787 --- /dev/null +++ b/MikroTikAPI/src/README.txt @@ -0,0 +1,14 @@ +This lib contains two APIs + +- com.mikroTik.wiki -> This is the java lib from the + mikrotik wiki by "janisk". I have extended it for support + with SSL-Support (weak and fully validated) and changed the + class/visiblity a bit for use in the second api + +- com.eightOceans.mikroTik -> This a API-wrapper around the wiki-api + which utilied Futures. There is also a TestApp, which uses a state + based App-Flow, which I uses as template for several private utilities. + +* Work in progress * + +MH diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/ApiConnection.java b/MikroTikAPI/src/com/eightOceans/mikroTik/ApiConnection.java new file mode 100644 index 0000000..c2fd085 --- /dev/null +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/ApiConnection.java @@ -0,0 +1,292 @@ +package com.eightOceans.mikroTik; + +import com.mikroTik.wiki.ApiConn; +import com.mikroTik.wiki.Hasher; +import com.mikroTik.wiki.ReadCommand; +import com.mikroTik.wiki.WriteCommand; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + + +/** + * + * @author mh TODO: Improve listen/getData/readin + */ +public class ApiConnection { + + + private Socket sock; + private boolean isAuthenticated; + + // Stuff from old API + private DataOutputStream out = null; + private DataInputStream in = null; + private String ipAddress; + private ReadCommand readCommand = null; + private WriteCommand writeCommand = null; + private Thread listener = null; + private LinkedBlockingQueue queue = new LinkedBlockingQueue(40); + + private final ExecutorService pool = Executors.newCachedThreadPool();//newFixedThreadPool(10); + private static final TrustManager[] ALL_TRUSTING_TRUST_MANAGER = new TrustManager[]{ + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } + }; + + // private static final HostnameVerifier ALL_TRUSTING_HOSTNAME_VERIFIER = new HostnameVerifier() { +// public boolean verify(String hostname, SSLSession session) { +// return true; +// } +// }; + + private void ensureSockClosed() { + try { + if (sock != null) { + try { + sock.close(); + } catch (IOException ioex) { + + } + } + } finally { + sock = null; + isAuthenticated = false; + } + } + + + private String getData() throws InterruptedException { + String s = (String) queue.take(); + return s; + } + + + 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(); + + } + } + + + public Future close() { + return pool.submit(new Callable() { + + @Override + public Result call() throws Exception { + ensureSockClosed(); + Result res = new Result(); + res.Success = true; + res.ResultMessage = "OK"; + return res; + } + }); + } + + + public boolean isAuthenticated() { + return isAuthenticated; + } + + + public boolean isConnected() { + return sock != null; + } + + + public Future login(String user, String password) { + return pool.submit(new Callable() { + + @Override + public Result call() throws Exception { + + String errMsg = null; + String r = sendCommand("/login"); + String s = "a"; + try { + s = getData(); + + 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=" + user + "\n=response=00" + chal; + s = sendCommand(m); + s = getData(); + if (s.contains("!done")) { + if (s.contains("!trap")) { + errMsg = "Error during login"; + } + } else { + errMsg = "Error during login"; + } + } + } else { + errMsg = "Unexpected response from server"; + } + + } catch (/*InterruptedException*/RuntimeException ex) { + Logger.getLogger(ApiConn.class.getName()).log(Level.SEVERE, null, ex); + errMsg = "Communication interrupted"; + } + + if (errMsg == null) { + isAuthenticated = true; + Result res = new Result(); + res.Success = true; + res.ResultMessage = "Login successful"; + return res; + } else { + isAuthenticated = false; + Result res = new Result(); + res.Success = false; + res.ResultMessage = errMsg; + return res; + } + } + + } + ); + } + + + public Future open(String host, int port, SSLMode sslMode) { + return pool.submit(new Callable() { + + @Override + public Result call() throws Exception { + + ensureSockClosed(); + + String message = "(none)"; + try { + InetAddress ia = InetAddress.getByName(host); + if (ia.isReachable(1000)) { + + SSLSocketFactory sslSockFact = null; + if (sslMode == SSLMode.None) { + sock = new Socket(ia, port); + } else if (sslMode == SSLMode.Weak) { + try { + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, ALL_TRUSTING_TRUST_MANAGER, new java.security.SecureRandom()); + sslSockFact = sc.getSocketFactory(); + } catch (NoSuchAlgorithmException | KeyManagementException nex) { + message = "Internal error in SSL-Module: " + nex.getMessage(); + } + } else {// if (sslMode == SSLMode.Strict) { + sslSockFact = (SSLSocketFactory) SSLSocketFactory.getDefault(); + + } + if (sslSockFact != null) { + SSLSocket socket = (SSLSocket) sslSockFact.createSocket(ia, port); + sock = socket; + } + message = "Connected"; + + in = new DataInputStream(sock.getInputStream()); + out = new DataOutputStream(sock.getOutputStream()); + readCommand = new ReadCommand(in, queue); + writeCommand = new WriteCommand(out); + listen(); + + } else { + message = "Not reachable"; + } + } catch (UnknownHostException ex) { + ensureSockClosed(); + message = ex.getMessage(); + } catch (IOException ex) { + ensureSockClosed(); + message = ex.getMessage(); + } + Result res = new Result(); + res.Success = (sock != null); + res.ResultMessage = message; + return res; + } + }); + } + + + /** + * 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(); + } + + + public Future readData() { + return pool.submit(new Callable() { + + @Override + public ReadDataResult call() throws Exception { + return readDataSync(); + } + }); + } + + + public ReadDataResult readDataSync() { + try { + String g = getData(); + ReadDataResult res = new ReadDataResult(); + res.Success = true; + res.Data = g; + return res; + } catch (Throwable th) { + ReadDataResult res = new ReadDataResult(); + res.Success = false; + res.ResultMessage = "Read failed: " + th.getMessage(); + return res; + } + } + + +} diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/MtSentence.java b/MikroTikAPI/src/com/eightOceans/mikroTik/MtSentence.java new file mode 100644 index 0000000..83a6afa --- /dev/null +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/MtSentence.java @@ -0,0 +1,74 @@ +package com.eightOceans.mikroTik; + +import java.util.HashMap; +import java.util.Map; + + +/** + * + * @author mh + */ +public class MtSentence { + + private boolean isDone; + private boolean isTrap; + private Map words; + + + public static MtSentence from(String rawString) { + MtSentence s = new MtSentence(); + String[] words = rawString.split("\n"); + + Map newWords = new HashMap<>(); + for (String w : words) { + if (w.contains("!done")) { + s.isDone = true; + break; + } else if (w.contains("!trap")) { + s.isTrap = true; + break; + } else if (w.contains("!re")) { + + } else if (w.startsWith("=")) { + String[] p = w.substring(1).split("="); + if (p.length == 2) { + newWords.put(p[0], p[1]); + } else { + newWords.put(w, null); + } + } else { + throw new UnsupportedOperationException("Unsupported word in rawString: " + w); + } + + + } + if ((!s.isDone) && (!s.isTrap)) { + s.words = newWords; + } + return s; + } + + + protected MtSentence() { + words = new HashMap<>(); + } + + + public boolean isDone() { + return isDone; + } + + + public boolean isTrap() { + return isTrap; + } + + + /** + * + * @return [KEY]=[VALUE] if word was key=value style or [KEY]=null if word does not contain a "=" + */ + public Map getWordAsMap() { + return words; + } +} diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/ReadDataResult.java b/MikroTikAPI/src/com/eightOceans/mikroTik/ReadDataResult.java new file mode 100644 index 0000000..8e5f914 --- /dev/null +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/ReadDataResult.java @@ -0,0 +1,12 @@ +package com.eightOceans.mikroTik; + + +/** + * + * @author mh + */ +public class ReadDataResult extends Result { + + public String Data; + +} diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/Result.java b/MikroTikAPI/src/com/eightOceans/mikroTik/Result.java new file mode 100644 index 0000000..8e83dae --- /dev/null +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/Result.java @@ -0,0 +1,14 @@ +package com.eightOceans.mikroTik; + + +/** + * + * @author mh + */ +public class Result { + + public boolean Success; + public String ResultMessage; + public T Result; + +} diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/SSLMode.java b/MikroTikAPI/src/com/eightOceans/mikroTik/SSLMode.java new file mode 100644 index 0000000..ba9b628 --- /dev/null +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/SSLMode.java @@ -0,0 +1,21 @@ +package com.eightOceans.mikroTik; + +/** + * + * @author mh + */ +public enum SSLMode { + + /** + * No encryption + */ + None, + /** + * SSL without certificate check + */ + Weak, + /** + * SSL verifiying for valid certificate + */ + Strict +} diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/Utils.java b/MikroTikAPI/src/com/eightOceans/mikroTik/Utils.java new file mode 100644 index 0000000..1d951fb --- /dev/null +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/Utils.java @@ -0,0 +1,95 @@ +package com.eightOceans.mikroTik; + +import java.util.Random; + + +/** + * + * @author mh + */ +public class Utils { + + public static String createPrivateWifiKey() { + String dk = "23a6c7f8abadffdFF01cd02DFfd022dacbdf"; // Sample for length + StringBuilder sb = new StringBuilder(); + Random rnd = new Random(); + for (int i = 0; i < dk.length() / 2; i++) { + int r = rnd.nextInt(255); + sb.append(String.format("%02x", r)); + + } + return sb.toString(); + } + + + public static String createPresharedWifiKey(int minLen, int maxLen, int minSpecial, int maxSpecial) { + StringBuilder sb = new StringBuilder(); + Random rnd = new Random(); + int special = 0; + boolean firstPass = true; + while (special < minSpecial) { + + if (firstPass) { + for (int i = 0; i < maxLen; i++) { + int r = 0; + char c = ' '; + while (true) { + c = getPasswordChar(rnd); + if (!Character.isAlphabetic(c)) { + special++; + if (special > maxSpecial) { + continue; + } + } + break; + } + sb.append(c); + if (sb.length() >= minLen) { + r = rnd.nextInt(maxLen - minLen+1); + if (r == 0) { + break; + } + } + } + } else { // Second or more passes, merge required num of special chars + char c = ' '; + for (int j = 0; j < sb.length(); j++) { + if (!Character.isAlphabetic(sb.charAt(j))) { + continue; + } + c = getPasswordChar(rnd); + if (Character.isAlphabetic(c)) { + sb.setCharAt(j, c); + special++; + } + if (special > maxSpecial) { + break; + } + } + } + firstPass = false; + } + + + return sb.toString(); + } + + + /** + * Create user typeable password char. Excludes some hard to enter + * characters + * + * @param rnd + * @return + */ + private static Character getPasswordChar(Random rnd) { + char c = ' '; + + do { + int r = 33 + rnd.nextInt(126 - 33); + c = String.format("%c", r).charAt(0); + } while ((c == '\'') || (c == '`') || (c == '|') || (c == '\\')); + return c; + } + +} diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/ops/AccessListEntry.java b/MikroTikAPI/src/com/eightOceans/mikroTik/ops/AccessListEntry.java new file mode 100644 index 0000000..371dcc4 --- /dev/null +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/ops/AccessListEntry.java @@ -0,0 +1,36 @@ +package com.eightOceans.mikroTik.ops; + +import com.eightOceans.mikroTik.Utils; + + +/** + * + * @author mh + */ +public class AccessListEntry { + + public static AccessListEntry createWithDefault() { + AccessListEntry e = new AccessListEntry(); + e.Authentication = true; + e.Forwarding = true; + e.MinSigStrength = -120; + e.MaxSigStrength = 120; + e.Algo = "aes-ccm"; + e.PrivateKey = Utils.createPrivateWifiKey(); + e.PreSharedKey = Utils.createPresharedWifiKey(12,14,1,2); + return e; + + } + + public String Id; + public String MacAddress; + public int MinSigStrength; + public int MaxSigStrength; + public boolean Forwarding; + public boolean Authentication; + public String Algo; + public String PrivateKey; + public String PreSharedKey; + public String ManagementProtectionKey; + public boolean Disabled; +} diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/ops/OPs.java b/MikroTikAPI/src/com/eightOceans/mikroTik/ops/OPs.java new file mode 100644 index 0000000..d084b76 --- /dev/null +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/ops/OPs.java @@ -0,0 +1,30 @@ +package com.eightOceans.mikroTik.ops; + +import com.eightOceans.mikroTik.ApiConnection; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + + +/** + * + * @author mh + */ +public class OPs { + + private Wireless wireless; + private ApiConnection conn; + private final ExecutorService pool = Executors.newCachedThreadPool();// + + + public OPs(ApiConnection conn) { + this.conn = conn; + } + + + public Wireless getWireless() { + if (wireless == null) { + wireless = new Wireless(pool, conn); + } + return wireless; + } +} diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java b/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java new file mode 100644 index 0000000..2915b80 --- /dev/null +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java @@ -0,0 +1,125 @@ +package com.eightOceans.mikroTik.ops; + +import com.eightOceans.mikroTik.ApiConnection; +import com.eightOceans.mikroTik.MtSentence; +import com.eightOceans.mikroTik.ReadDataResult; +import com.eightOceans.mikroTik.Result; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + + +/** + * + * @author mh + */ +public class Wireless { + + private ApiConnection conn; + private ExecutorService pool; + + + protected Wireless(ExecutorService pool, ApiConnection conn) { + this.conn = conn; + this.pool = pool; + } + + + public Future createOrUpdate(AccessListEntry e) { + return null; + } + + + public Future>> getAccessListEntries() { + return pool.submit(new Callable>>() { + + @Override + public Result> call() throws Exception { + Result> res = new Result<>(); + List resLst = new ArrayList<>(); + + conn.sendCommand("/interface/wireless/access-list/getall"); + + while (true) { + ReadDataResult r = conn.readDataSync(); + if (!r.Success) { + res.Success = false; + res.ResultMessage = "Data read failed: " + r.ResultMessage; + resLst = null; // Indicate failures + break; + } else { + MtSentence ms = MtSentence.from(r.Data); + if (ms.isTrap()) { + res.Success = false; + res.ResultMessage = "Data read failed, remote sent trap"; + resLst = null; // Indicate failures + break; + } else if (ms.isDone()) { + break; + } else { + /* + .id=*1B + =mac-address=00:00:00:00:00:01 + =interface=all + =signal-range=-120..120 + =authentication=true + =forwarding=true + =ap-tx-limit=0 + =client-tx-limit=0 + =private-algo=aes-ccm + =private-key=23a6c7f8abadffdFF01cd02DFfd022dacbdf + =private-pre-shared-key=asdasdadsasdasdasdasdasd + =management-protection-key= + =disabled=false + =comment=#GEND0001# Vla + */ + AccessListEntry ale = AccessListEntry.createWithDefault(); + ale.Algo = ms.getWordAsMap().get("private-algo"); + ale.Authentication = "true".equals(ms.getWordAsMap().get("authentication")); + ale.Disabled = "true".equals(ms.getWordAsMap().get("disabled")); + ale.Forwarding = "true".equals(ms.getWordAsMap().get("forwarding")); + ale.Id = ms.getWordAsMap().get(".id"); + ale.MacAddress = ms.getWordAsMap().get("mac-address"); + ale.ManagementProtectionKey = ms.getWordAsMap().get("management-protection-key"); + String sigRange = ms.getWordAsMap().get("signal-range"); + if (sigRange != null) { + String[] parts = sigRange.split("\\.\\."); + try { + if (parts.length == 2) { + int min = 0; + int max = 0; + min = Integer.parseInt(parts[0]); + max = Integer.parseInt(parts[1]); + ale.MaxSigStrength = max; + ale.MinSigStrength = min; + } + } catch (NumberFormatException nex) { + res.Success = false; + res.ResultMessage = "Remote returns invalid singal sength"; + resLst = null; + break; + } + } + ale.PreSharedKey = ms.getWordAsMap().get("private-pre-shared-key"); + ale.PrivateKey = ms.getWordAsMap().get("private-key"); + resLst.add(ale); + } + } + } + + if (resLst != null) { // != null == no failure + resLst.add(AccessListEntry.createWithDefault()); + res.Success = true; + res.Result = resLst; + } + + return res; + } + } + ); + } + + +} diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/sample/TestApp.java b/MikroTikAPI/src/com/eightOceans/mikroTik/sample/TestApp.java new file mode 100644 index 0000000..2dfff25 --- /dev/null +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/sample/TestApp.java @@ -0,0 +1,143 @@ +package com.eightOceans.mikroTik.sample; + +import com.eightOceans.mikroTik.ApiConnection; +import com.eightOceans.mikroTik.ReadDataResult; +import com.eightOceans.mikroTik.Result; +import com.eightOceans.mikroTik.SSLMode; +import com.eightOceans.mikroTik.ops.AccessListEntry; +import com.eightOceans.mikroTik.ops.OPs; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + + +/** + * + * @author mh + */ +public class TestApp { + + private static String user = "admin"; + private static String pass = ""; + private static String host = "192.168.61.254"; + private static int port = 8729; + + + /** + * + * @param + * @param f + * @param timeout + * @param startOp + * @param resOp + * @return On sucess the Result of the Future, on failure Result of the + * Future or a basic self generated Result-instance. + */ + private static T run(Future f, long timeout, String opInProgressDescr) { + if (opInProgressDescr != null) { + System.out.println("* " + opInProgressDescr + " ..."); + } + boolean succ; + String msg; + T res = null; + try { + res = f.get(timeout, TimeUnit.SECONDS); + msg = res.ResultMessage; + succ = res.Success; + } catch (InterruptedException | ExecutionException | TimeoutException iex) { + succ = false; + msg = "Operation interrupted/timeout:" + iex.getMessage(); + } + if (msg != null) { + if (succ) { + System.out.println("- Operation succeeded: " + msg + "."); + } else { + System.out.println("! Operation FAILED: " + msg + "!"); + } + } + if (res == null) { + res = (T) new Result(); + res.Success = succ; + res.ResultMessage = msg; + } + return res; + } + + + enum NextOp { + + CONNECT, + LOGIN, + TEST1, + TEST2, + EXIT, + UNDEF + + }; + + + public static void main(String[] args) { + System.out.println("- Starting test app"); + ApiConnection conn = new ApiConnection(); + + NextOp op = NextOp.CONNECT; + while (true) { + + NextOp nextOp = NextOp.EXIT; + + switch (op) { + case CONNECT: + if (run(conn.open(host, port, SSLMode.Weak), 7, "Establishing connection").Success) { + nextOp = NextOp.LOGIN; + } + break; + case LOGIN: + if (run(conn.login(user, pass), 7, "Loggin in").Success) { + nextOp = NextOp.TEST2; + } + break; + case TEST1: + conn.sendCommand("/interface/getall"); + String d = ""; + while ((d == null || (d.indexOf("!done") == -1))) { + Result res = run(conn.readData(), 7, null); + if (res.Success) { + d = ((ReadDataResult) res).Data; + System.out.println(" Got: " + d); + } else { + break; + } + } + System.out.println(" Reply complete"); + nextOp = NextOp.TEST2; + break; + case TEST2: + try { + OPs ops = new OPs(conn); + Result> r = ops.getWireless().getAccessListEntries().get(); + System.out.println(r.Result.get(0).PreSharedKey); + System.out.println(r.Result.get(0).PrivateKey); + System.out.println("XXX"); + } catch (Throwable th) { + th.printStackTrace(); + } + + break; + case EXIT: + run(conn.close(), 7, "Closing connection"); + System.out.println("Test app done"); +// +// for (int i = 0; i < 100; i++) { +// System.out.println(Utils.createPresharedWifiKey(12, 13, 1, 2)); +// } + System.exit(0); + } + op = nextOp; + } + + + } + +} diff --git a/MikroTikAPI/src/com/mikroTik/wiki/ApiConn.java b/MikroTikAPI/src/com/mikroTik/wiki/ApiConn.java new file mode 100644 index 0000000..612f8b0 --- /dev/null +++ b/MikroTikAPI/src/com/mikroTik/wiki/ApiConn.java @@ -0,0 +1,295 @@ +package com.mikroTik.wiki; + +// *** Based on Sources from the MikroTik wiki - Free to use license +// *** Orig author apparently "janisk" + +/* + * This contains connection. Everything should be here, + * should operate with this class only + */ +import java.io.*; +import java.net.*; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +/** + * + * @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); + private SSLMode sslMode; + + public enum SSLMode { + + /** + * No encryption + */ + None, + /** + * SSL without certificate check + */ + Weak, + /** + * SSL verifiying for valid certificate + */ + Strict + } + + /** + * 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, SSLMode sslMode) { + this.sslMode = sslMode; + 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"; + } + + private static final TrustManager[] ALL_TRUSTING_TRUST_MANAGER = new TrustManager[]{ + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } + }; + +// private static final HostnameVerifier ALL_TRUSTING_HOSTNAME_VERIFIER = new HostnameVerifier() { +// public boolean verify(String hostname, SSLSession session) { +// return true; +// } +// }; + @Override + public void run() { + try { + InetAddress ia = InetAddress.getByName(ipAddress); + if (ia.isReachable(1000)) { + + SSLSocketFactory sslSockFact = null; + if (sslMode == SSLMode.None) { + sock = new Socket(ipAddress, ipPort); + } else if (sslMode == SSLMode.Weak) { + try { + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, ALL_TRUSTING_TRUST_MANAGER, new java.security.SecureRandom()); + sslSockFact = sc.getSocketFactory(); + } catch (NoSuchAlgorithmException | KeyManagementException nex) { + throw new RuntimeException(nex); + } + } else {// if (sslMode == SSLMode.Strict) { + sslSockFact = (SSLSocketFactory) SSLSocketFactory.getDefault(); + + } + if (sslSockFact != null) { + SSLSocket socket = (SSLSocket) sslSockFact.createSocket(ipAddress, ipPort); + sock = socket; + } + + //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(); + } + } +} diff --git a/MikroTikAPI/src/com/mikroTik/wiki/DataReceiver.java b/MikroTikAPI/src/com/mikroTik/wiki/DataReceiver.java new file mode 100644 index 0000000..618c6da --- /dev/null +++ b/MikroTikAPI/src/com/mikroTik/wiki/DataReceiver.java @@ -0,0 +1,34 @@ +package com.mikroTik.wiki; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author janisk + */ +public class DataReceiver extends Thread { + + private ApiConn aConn = null; + + public DataReceiver(ApiConn aConn) { + this.aConn = aConn; + } + + @Override + public void run() { + String s = ""; + while (true) { + try { + s = aConn.getData(); + if (s != null) { + System.out.println(s); + if (s.contains("!done")) { + } + } + } catch (InterruptedException ex) { + Logger.getGlobal().log(Level.SEVERE, null, ex); + } + } + } +} diff --git a/MikroTikAPI/src/com/mikroTik/wiki/Hasher.java b/MikroTikAPI/src/com/mikroTik/wiki/Hasher.java new file mode 100644 index 0000000..7f6f58d --- /dev/null +++ b/MikroTikAPI/src/com/mikroTik/wiki/Hasher.java @@ -0,0 +1,58 @@ +package com.mikroTik.wiki; + +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; + } +} diff --git a/MikroTikAPI/src/com/mikroTik/wiki/ReadCommand.java b/MikroTikAPI/src/com/mikroTik/wiki/ReadCommand.java new file mode 100644 index 0000000..d90ce52 --- /dev/null +++ b/MikroTikAPI/src/com/mikroTik/wiki/ReadCommand.java @@ -0,0 +1,139 @@ +package com.mikroTik.wiki; + + +/* + * 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) { + Logger.getLogger(ReadCommand.class.getName()).log(Level.SEVERE, null, 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"; // MH: Dont'add newline BEFORE word + byte[] bb = new byte[sk]; + // MH: Use while around in.read -> otherwise messasges bay be garbled by partial reads + int got = 0; + while (got < sk) { + try { + got += in.read(bb, got, sk - got); + } catch (IOException ex) { + got = 0; + ex.printStackTrace(); + return; + } + } + if (got > 0) { + s += new String(bb); + s += "\n"; // MH: Add newline AFTER word (here!) + } + } else if (b == -1) { + System.out.println("Error, it should not happen ever, or connected to wrong port"); + } else { + try { + queue.put(s + "\n"); + } catch (InterruptedException ex) { + ex.printStackTrace(); + System.out.println("exiting reader"); + return; + } + s = ""; + } + } + } +} diff --git a/MikroTikAPI/src/com/mikroTik/wiki/WriteCommand.java b/MikroTikAPI/src/com/mikroTik/wiki/WriteCommand.java new file mode 100644 index 0000000..187ee26 --- /dev/null +++ b/MikroTikAPI/src/com/mikroTik/wiki/WriteCommand.java @@ -0,0 +1,115 @@ +package com.mikroTik.wiki; + + +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(); + } + + + public WriteCommand(DataOutputStream out) { + this.out = out; + } + + + public 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(); + } + + + public 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"; + } + } +} diff --git a/MikroTikAPI/src/com/mikroTik/wiki/sample/TestApp.java b/MikroTikAPI/src/com/mikroTik/wiki/sample/TestApp.java new file mode 100644 index 0000000..c5c788b --- /dev/null +++ b/MikroTikAPI/src/com/mikroTik/wiki/sample/TestApp.java @@ -0,0 +1,35 @@ +package com.mikroTik.wiki.sample; + +import com.mikroTik.wiki.DataReceiver; +import com.mikroTik.wiki.ApiConn; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author mh + */ +public class TestApp { + + static String password = "XXXX"; + + public static void main(String[] args) { + ApiConn conn = new ApiConn("192.168.61.254", 8729, ApiConn.SSLMode.Weak); + if (!conn.isConnected()) { + conn.start(); + try { + conn.join(2000); + if (conn.isConnected()) { + conn.login("admin", new String(password).toCharArray()); + conn.sendCommand("/ip/address/print"); + DataReceiver dataRec = new DataReceiver(conn); + dataRec.start(); + } + } catch (InterruptedException ex) { + Logger.getGlobal().log(Level.SEVERE, null, ex); + return; + } + } + System.out.println("Done"); + } +} diff --git a/MtWifiUserConfig/.directory b/MtWifiUserConfig/.directory new file mode 100644 index 0000000..7a345e0 --- /dev/null +++ b/MtWifiUserConfig/.directory @@ -0,0 +1,3 @@ +[Dolphin] +Timestamp=2015,1,10,10,15,48 +Version=3 diff --git a/MtWifiUserConfig/build.xml b/MtWifiUserConfig/build.xml new file mode 100644 index 0000000..bbedc7d --- /dev/null +++ b/MtWifiUserConfig/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project MtWifiUserConfig. + + + diff --git a/MtWifiUserConfig/config.properties b/MtWifiUserConfig/config.properties new file mode 100644 index 0000000..a176f35 --- /dev/null +++ b/MtWifiUserConfig/config.properties @@ -0,0 +1,9 @@ +AP_1=192.168.61.220 +AP_1_PORT=8729 +AP_1_USER=rcfg +AP_1_PWPREF=0a + +DEV1=MK Iphone 7 +DEV1_KEY=saldk9012LLada +DEV1_MINDB=-70 + diff --git a/MtWifiUserConfig/manifest.mf b/MtWifiUserConfig/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/MtWifiUserConfig/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/MtWifiUserConfig/nbproject/build-impl.xml b/MtWifiUserConfig/nbproject/build-impl.xml new file mode 100644 index 0000000..39866da --- /dev/null +++ b/MtWifiUserConfig/nbproject/build-impl.xml @@ -0,0 +1,1429 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MtWifiUserConfig/nbproject/genfiles.properties b/MtWifiUserConfig/nbproject/genfiles.properties new file mode 100644 index 0000000..509c6e3 --- /dev/null +++ b/MtWifiUserConfig/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=d3fd163a +build.xml.script.CRC32=d782f8ff +build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=d3fd163a +nbproject/build-impl.xml.script.CRC32=0d613bb8 +nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 diff --git a/MtWifiUserConfig/nbproject/private/config.properties b/MtWifiUserConfig/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/MtWifiUserConfig/nbproject/private/private.properties b/MtWifiUserConfig/nbproject/private/private.properties new file mode 100644 index 0000000..2f0566c --- /dev/null +++ b/MtWifiUserConfig/nbproject/private/private.properties @@ -0,0 +1,7 @@ +application.args=0a0a +compile.on.save=true +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=/home/mh/.netbeans/8.0.2/build.properties diff --git a/MtWifiUserConfig/nbproject/private/private.xml b/MtWifiUserConfig/nbproject/private/private.xml new file mode 100644 index 0000000..752ed80 --- /dev/null +++ b/MtWifiUserConfig/nbproject/private/private.xml @@ -0,0 +1,11 @@ + + + + + + file:/home/mh/Dvp/prj/MtUtils/MtWifiUserConfig/config.properties + file:/home/mh/Dvp/prj/MtUtils/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/Main.java + file:/home/mh/Dvp/prj/MtUtils/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/ApDef.java + + + diff --git a/MtWifiUserConfig/nbproject/project.properties b/MtWifiUserConfig/nbproject/project.properties new file mode 100644 index 0000000..7a79759 --- /dev/null +++ b/MtWifiUserConfig/nbproject/project.properties @@ -0,0 +1,93 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=MtWifiUserConfig +application.vendor=mh +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/MtWifiUserConfig.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +includes=** +jar.archive.disabled=${jnlp.enabled} +jar.compress=false +jar.index=${jnlp.enabled} +javac.classpath=\ + ${reference.MikroTikAPI.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=no.codebase +jnlp.descriptor=application +jnlp.enabled=false +jnlp.mixed.code=default +jnlp.offline-allowed=false +jnlp.signed=false +jnlp.signing= +jnlp.signing.alias= +jnlp.signing.keystore= +main.class=com.eightOceans.mtWifiUserConfig.Main +# Optional override of default Codebase manifest attribute, use to prevent RIAs from being repurposed +manifest.custom.codebase= +# Optional override of default Permissions manifest attribute (supported values: sandbox, all-permissions) +manifest.custom.permissions= +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +project.MikroTikAPI=../MikroTikAPI +reference.MikroTikAPI.jar=${project.MikroTikAPI}/dist/MikroTikAPI.jar +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/MtWifiUserConfig/nbproject/project.xml b/MtWifiUserConfig/nbproject/project.xml new file mode 100644 index 0000000..c7e9545 --- /dev/null +++ b/MtWifiUserConfig/nbproject/project.xml @@ -0,0 +1,25 @@ + + + org.netbeans.modules.java.j2seproject + + + MtWifiUserConfig + + + + + + + + + + MikroTikAPI + jar + + jar + clean + jar + + + + diff --git a/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/ApDef.java b/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/ApDef.java new file mode 100644 index 0000000..d14d3dd --- /dev/null +++ b/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/ApDef.java @@ -0,0 +1,15 @@ +package com.eightOceans.mtWifiUserConfig; + + +/** + * + * @author mh + */ +public class ApDef { + + public int index; + public String host; + public Integer port; + public String user; + public String pwPrefix; +} diff --git a/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/DevDef.java b/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/DevDef.java new file mode 100644 index 0000000..d460c1f --- /dev/null +++ b/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/DevDef.java @@ -0,0 +1,14 @@ + +package com.eightOceans.mtWifiUserConfig; + + +/** + * + * @author mh + */ +public class DevDef { + public int index; + public String name; + public String preshareKey; + public int minDbAllowed; +} diff --git a/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/Main.java b/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/Main.java new file mode 100644 index 0000000..15f9c3c --- /dev/null +++ b/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/Main.java @@ -0,0 +1,186 @@ +package com.eightOceans.mtWifiUserConfig; + +import com.eightOceans.mikroTik.ApiConnection; +import com.eightOceans.mikroTik.Result; +import com.eightOceans.mikroTik.SSLMode; + +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * + * @author mh + */ +public class Main { + + private final static String VERS = "0.0.1"; + + private static String _currOp = ""; + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + + List aps = new ArrayList<>(); + List devs = new ArrayList<>(); + + try { + System.out.println("MTWifiUserConfig " + VERS); + _currOp = "Loading config"; + Properties props = loadProps(); + + // load APs + int i = 0; + while (true) { + i++; + _currOp = "Checking AP " + i + " config"; + ApDef ap = new ApDef(); + ap.index = i; + ap.host = props.getProperty("AP_" + i, null); + if (ap.host == null) { + break; + } + ap.port = Integer.parseInt(props.getProperty("AP_" + i + "_PORT", null)); + ap.user = props.getProperty("AP_" + i + "_USER", null); + ap.pwPrefix = props.getProperty("AP_" + i + "_PWPREF", null); + aps.add(ap); + } + + // load + i = 0; + while (true) { + i++; + _currOp = "Checking Device " + i + " config"; + DevDef dev = new DevDef(); + dev.index = i; + dev.name = props.getProperty("DEV_" + i, null); + if (dev.name == null) { + break; + } + dev.preshareKey = props.getProperty("DEV_" + i + "_KEY", null); + if ((dev.preshareKey == null) || (dev.preshareKey.length() < 8)) { + throw new RuntimeException("Preshared key too short!"); + } + String db = props.getProperty("DEV_" + i + "_MINDB", null); + if (db != null) { + dev.minDbAllowed = Integer.parseInt(db); + } else { + dev.minDbAllowed = -87; + } + } + + _currOp = "Getting pw"; + String pw = null; + + System.out.print("Enter Password: "); + if (System.console() != null) { + char[] pwChars = System.console().readPassword(); + pw = new String(pwChars); + } else { + pw = args[0]; + } + System.out.println(); + + System.out.println("- Sending config to APs ..."); + for (ApDef ap : aps) { + sendDevsToAp(ap, devs, pw); + } + + } catch (Exception ex) { + System.out.println("! While " + _currOp + ": " + ex.getMessage()); + } + + _currOp = "Done"; + System.out.println("* Done"); + } + + private static Properties loadProps() { + try { + FileInputStream fis = new FileInputStream("config.properties"); + Properties p = new Properties(); + p.load(fis); + return p; + } catch (Exception ex) { + throw new RuntimeException("Property load failed:" + ex.getMessage()); + } + } + + public enum CommState { + + CONNECT, + LOGIN, + FAILED, + EXIT, + DONE + } + + private static void setCurrOp(String msg) { + _currOp = msg; + System.out.println(" ... " + _currOp); + } + + private static void sendDevsToAp(ApDef ap, List devs, String remPw) { + + int to = 10; + String errMsg = null; + ApiConnection conn = new ApiConnection(); + + CommState cs = CommState.CONNECT; + + while (cs != CommState.DONE) { + try { + Result r; + switch (cs) { + case CONNECT: + setCurrOp("Connecting to ap " + ap.host); + r = conn.open(ap.host, ap.port, SSLMode.Weak).get(to, TimeUnit.SECONDS); + if (r.Success) { + cs = CommState.LOGIN; + } else { + errMsg = "Failed: " + r.ResultMessage; + cs = CommState.FAILED; + } + break; + case LOGIN: + setCurrOp("Login to " + ap.host); + r = conn.login(ap.user, ap.pwPrefix + remPw).get(to, TimeUnit.SECONDS); + if (r.Success) { + cs = cs.EXIT; + } else { + errMsg = "Login Failed: " + r.ResultMessage; + cs = CommState.FAILED; + } + break; + case FAILED: + System.out.println(" ! " + (errMsg != null ? errMsg : (_currOp + " Failed")) + "! Skipping this AP"); + cs = CommState.EXIT; + break; + case EXIT: + default: + setCurrOp("Closing conn to " + ap.host); + cs = CommState.DONE; + conn.close().get(to, TimeUnit.SECONDS); + } + } catch (TimeoutException tex) { + errMsg = "Timeout occurred"; + cs = CommState.FAILED; + } catch (ExecutionException ee) { + throw new RuntimeException(ee); + } catch (InterruptedException iex) { + errMsg = "Interruption occuroed"; + cs = CommState.FAILED; + } + } + + for (DevDef dev : devs) { + } + + } + +}