This commit is contained in:
Michael Hoess 2015-08-18 21:54:15 +02:00
commit 63f74c67ba
39 changed files with 5008 additions and 0 deletions

73
MikroTikAPI/build.xml Normal file
View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="MikroTikAPI" default="default" basedir=".">
<description>Builds, tests, and runs the project MikroTikAPI.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="MikroTikAPI-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
<group>
<file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java</file>
<file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/sample/TestApp.java</file>
<file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/OPs.java</file>
</group>
</open-files>
</project-private>

View File

@ -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

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>MikroTikAPI</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
</configuration>
</project>

View File

@ -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

View File

@ -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<Result> close() {
return pool.submit(new Callable<Result>() {
@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<Result> login(String user, String password) {
return pool.submit(new Callable<Result>() {
@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<Result> open(String host, int port, SSLMode sslMode) {
return pool.submit(new Callable<Result>() {
@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<ReadDataResult> readData() {
return pool.submit(new Callable<ReadDataResult>() {
@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;
}
}
}

View File

@ -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<String, String> words;
public static MtSentence from(String rawString) {
MtSentence s = new MtSentence();
String[] words = rawString.split("\n");
Map<String, String> 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<String, String> getWordAsMap() {
return words;
}
}

View File

@ -0,0 +1,12 @@
package com.eightOceans.mikroTik;
/**
*
* @author mh
*/
public class ReadDataResult extends Result {
public String Data;
}

View File

@ -0,0 +1,14 @@
package com.eightOceans.mikroTik;
/**
*
* @author mh
*/
public class Result<T> {
public boolean Success;
public String ResultMessage;
public T Result;
}

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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<Result> createOrUpdate(AccessListEntry e) {
return null;
}
public Future<Result<List<AccessListEntry>>> getAccessListEntries() {
return pool.submit(new Callable<Result<List<AccessListEntry>>>() {
@Override
public Result<List<AccessListEntry>> call() throws Exception {
Result<List<AccessListEntry>> res = new Result<>();
List<AccessListEntry> 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;
}
}
);
}
}

View File

@ -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 <T>
* @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 extends Result> T run(Future<T> 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<List<AccessListEntry>> 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;
}
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}

View File

@ -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 = "";
}
}
}
}

View File

@ -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";
}
}
}

View File

@ -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");
}
}

View File

@ -0,0 +1,3 @@
[Dolphin]
Timestamp=2015,1,10,10,15,48
Version=3

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="MtWifiUserConfig" default="default" basedir=".">
<description>Builds, tests, and runs the project MtWifiUserConfig.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="MtWifiUserConfig-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>

View File

@ -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

View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
<group>
<file>file:/home/mh/Dvp/prj/MtUtils/MtWifiUserConfig/config.properties</file>
<file>file:/home/mh/Dvp/prj/MtUtils/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/Main.java</file>
<file>file:/home/mh/Dvp/prj/MtUtils/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/ApDef.java</file>
</group>
</open-files>
</project-private>

View File

@ -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

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>MtWifiUserConfig</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
<references xmlns="http://www.netbeans.org/ns/ant-project-references/1">
<reference>
<foreign-project>MikroTikAPI</foreign-project>
<artifact-type>jar</artifact-type>
<script>build.xml</script>
<target>jar</target>
<clean-target>clean</clean-target>
<id>jar</id>
</reference>
</references>
</configuration>
</project>

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<ApDef> aps = new ArrayList<>();
List<DevDef> 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<DevDef> 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) {
}
}
}