Added anonymous TLS (SSL) support

This commit is contained in:
Gideon le Grange 2013-12-30 08:11:05 +02:00
parent e3569a49af
commit fd69b85755
4 changed files with 87 additions and 29 deletions

View File

@ -34,6 +34,8 @@
<version>2.3.2</version> <version>2.3.2</version>
<configuration> <configuration>
<showDeprecation>true</showDeprecation> <showDeprecation>true</showDeprecation>
<source>1.5</source>
<target>1.5</target>
</configuration> </configuration>
</plugin> </plugin>

View File

@ -7,7 +7,7 @@ package examples;
public class Config { public class Config {
public static final String HOST = "10.0.0.3"; public static final String HOST = "10.0.1.3";
public static final String USERNAME = "admin"; public static final String USERNAME = "admin";
public static final String PASSWORD = ""; public static final String PASSWORD = "";

View File

@ -12,8 +12,26 @@ import me.legrange.mikrotik.impl.ApiConnectionImpl;
*/ */
public abstract class ApiConnection { public abstract class ApiConnection {
/** default TCP port used by Mikrotik API */ /**
public static final int DEFAULT_PORT = 8728; * Create a new API connection to the give device on the supplied port, using anonymous TLS for encryption.
* @param host The host to which to connect.
* @param port The TCP port to use.
* @return The ApiConnection
*/
public static ApiConnection connectTLS(String host, int port) throws MikrotikApiException {
return ApiConnectionImpl.connect(host, port, true);
}
/**
* Create a new API connection to the give device on the default API port, using anonymous TLS for encryption.
* @param host The host to which to connect.
* @return The ApiConnection
*/
public static ApiConnection connectTLS(String host) throws MikrotikApiException {
return ApiConnectionImpl.connect(host, DEFAULT_TLS_PORT, true);
}
/** /**
* Create a new API connection to the give device on the supplied port * Create a new API connection to the give device on the supplied port
@ -22,11 +40,11 @@ public abstract class ApiConnection {
* @return The ApiConnection * @return The ApiConnection
*/ */
public static ApiConnection connect(String host, int port) throws MikrotikApiException { public static ApiConnection connect(String host, int port) throws MikrotikApiException {
return ApiConnectionImpl.connect(host, port); return ApiConnectionImpl.connect(host, port, false);
} }
/** /**
* Create a new API connection to the give device on the default API port.. * Create a new API connection to the give device on the default API port.
* @param host The host to which to connect. * @param host The host to which to connect.
* @return The ApiConnection * @return The ApiConnection
*/ */
@ -72,4 +90,9 @@ public abstract class ApiConnection {
/** cancel a command */ /** cancel a command */
public abstract void cancel(String tag) throws MikrotikApiException; public abstract void cancel(String tag) throws MikrotikApiException;
/** default TCP port used by Mikrotik API */
private static final int DEFAULT_PORT = 8728;
/** default TCP TLS port used by Mikrotik API */
private static final int DEFAULT_TLS_PORT = 8729;
} }

View File

@ -8,16 +8,21 @@ import java.io.UnsupportedEncodingException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.Provider;
import java.security.Security;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
//import org.bouncycastle.jce.provider.BouncyCastleProvider;
/** /**
* The Mikrotik API connection implementation. This is the class used to connect to a remote * The Mikrotik API connection implementation. This is the class used to connect
* Mikrotik and send commands to it. * to a remote Mikrotik and send commands to it.
* *
* @author GideonLeGrange * @author GideonLeGrange
*/ */
@ -25,13 +30,14 @@ public final class ApiConnectionImpl extends ApiConnection {
/** /**
* Create a new API connection to the give device on the supplied port * Create a new API connection to the give device on the supplied port
*
* @param host The host to which to connect. * @param host The host to which to connect.
* @param port The TCP port to use. * @param port The TCP port to use.
* @return The ApiConnection * @return The ApiConnection
*/ */
public static ApiConnection connect(String host, int port) throws ApiConnectionException { public static ApiConnection connect(String host, int port, boolean secure) throws ApiConnectionException {
ApiConnectionImpl con = new ApiConnectionImpl(); ApiConnectionImpl con = new ApiConnectionImpl();
con.open(host, port); con.open(host, port, secure);
return con; return con;
} }
@ -75,7 +81,9 @@ public final class ApiConnectionImpl extends ApiConnection {
execute("/login name=" + username + " response=00" + chal); execute("/login name=" + username + " response=00" + chal);
} }
/** execute a command and return a list of results. /**
* execute a command and return a list of results.
*
* @param cmd Command to execute * @param cmd Command to execute
* @return The list of results * @return The list of results
*/ */
@ -83,7 +91,8 @@ public final class ApiConnectionImpl extends ApiConnection {
return execute(Parser.parse(cmd)); return execute(Parser.parse(cmd));
} }
/** execute a command and attach a result listener to receive it's results. /**
* execute a command and attach a result listener to receive it's results.
* *
* @param cmd Command to execute * @param cmd Command to execute
* @param lis ResultListener that will receive the results * @param lis ResultListener that will receive the results
@ -94,9 +103,11 @@ public final class ApiConnectionImpl extends ApiConnection {
return execute(Parser.parse(cmd), lis); return execute(Parser.parse(cmd), lis);
} }
/** cancel a command */ /**
* cancel a command
*/
public void cancel(String tag) throws MikrotikApiException { public void cancel(String tag) throws MikrotikApiException {
execute(String.format("/cancel tag=%s", tag)) ; execute(String.format("/cancel tag=%s", tag));
} }
private List<Map<String, String>> execute(Command cmd) throws MikrotikApiException { private List<Map<String, String>> execute(Command cmd) throws MikrotikApiException {
@ -105,7 +116,6 @@ public final class ApiConnectionImpl extends ApiConnection {
return l.getResults(); return l.getResults();
} }
private String execute(Command cmd, ResultListener lis) throws MikrotikApiException { private String execute(Command cmd, ResultListener lis) throws MikrotikApiException {
String tag = nextTag(); String tag = nextTag();
cmd.setTag(tag); cmd.setTag(tag);
@ -124,13 +134,24 @@ public final class ApiConnectionImpl extends ApiConnection {
} }
/** /**
* Start the API. Connects to the Mikrotik * Start the API. Connects to the Mikrotik without using encryption
*/ */
private void open(String host, int port) throws ApiConnectionException { private void open(String host, int port) throws ApiConnectionException {
open(host, port, false);
}
/**
* Start the API. Connects to the Mikrotik
*/
private void open(String host, int port, boolean secure) throws ApiConnectionException {
try { try {
InetAddress ia = InetAddress.getByName(host); InetAddress ia = InetAddress.getByName(host);
if (ia.isReachable(1000)) { if (ia.isReachable(1000)) {
if (secure) {
sock = openSSLSocket(ia, port);
} else {
sock = new Socket(ia, port); sock = new Socket(ia, port);
}
in = new DataInputStream(sock.getInputStream()); in = new DataInputStream(sock.getInputStream());
out = new DataOutputStream(sock.getOutputStream()); out = new DataOutputStream(sock.getOutputStream());
connected = true; connected = true;
@ -152,6 +173,21 @@ public final class ApiConnectionImpl extends ApiConnection {
} }
} }
/**
* open and configure a SSL socket.
*/
private Socket openSSLSocket(InetAddress ia, int port) throws IOException {
SSLSocket ssl = (SSLSocket) SSLSocketFactory.getDefault().createSocket(ia, port);
List<String> cs = new LinkedList<String>();
// not happy with this code. Without it, SSL throws a "Remote host closed connection during handshake" error
// caused by a "SSL peer shut down incorrectly" error
for (String s : ssl.getSupportedCipherSuites()) {
if (s.startsWith("TLS_DH_anon")) cs.add(s);
}
ssl.setEnabledCipherSuites(cs.toArray(new String[]{}));
return ssl;
}
private char[] makePass(String pass) { private char[] makePass(String pass) {
if (true) { if (true) {
return pass.toCharArray(); return pass.toCharArray();
@ -166,7 +202,6 @@ public final class ApiConnectionImpl extends ApiConnection {
_tag++; _tag++;
return Integer.toHexString(_tag); return Integer.toHexString(_tag);
} }
private static final int DEFAULT_PORT = 8728; private static final int DEFAULT_PORT = 8728;
private Socket sock = null; private Socket sock = null;
private DataOutputStream out = null; private DataOutputStream out = null;
@ -256,9 +291,8 @@ public final class ApiConnectionImpl extends ApiConnection {
ResponseListener rl = (ResponseListener) l; ResponseListener rl = (ResponseListener) l;
if (res instanceof Done) { if (res instanceof Done) {
if (rl instanceof SyncListener) { if (rl instanceof SyncListener) {
((SyncListener)rl).completed((Done)res); ((SyncListener) rl).completed((Done) res);
} } else {
else {
rl.completed(); rl.completed();
} }
} else if (res instanceof Error) { } else if (res instanceof Error) {
@ -441,7 +475,6 @@ public final class ApiConnectionImpl extends ApiConnection {
} }
return results; return results;
} }
private List<Map<String, String>> results = new LinkedList<Map<String, String>>(); private List<Map<String, String>> results = new LinkedList<Map<String, String>>();
private MikrotikApiException err; private MikrotikApiException err;
} }