Вывожу свои скрипты для работы с микротиком через API.
Библиотека mikrotik. Оригинальный автор классов - некто Яниск, я же переделал их на BeanShell и добавил пару классов для удобства.
Код:
import java.io.*;
import java.net.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays.copyOf;
/**
*
* @orginal 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;
int i;
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 (i = 0; i < s.length(); i++) {
defaultBytes[i] = (byte) (0xFF & s.charAt(i));
}
algorithm.reset();
algorithm.update(defaultBytes);
messageDigest = algorithm.digest();
StringBuffer hexString = new StringBuffer();
for (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 string 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;
}
}
public class ReadCommand implements Runnable {
private DataInputStream in = null;
private 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;
}
public void run() {
byte b = 0;
String s = "";
char ch;
int sk, i, a = 0;
while (true) {
//int sk = 0;
try {
a = in.readByte();
} catch (IOException ex) {
return;
}
if (a != 0 && a > 0) {
if (a < 0x80) {
sk = a;
} else {
if (a < 0xC0) {
a = a << 8;
try {
a += in.readByte();
} catch (IOException ex) {
return;
}
sk = a ^ 0x8000;
} else {
if (a < 0xE0) {
try {
for (i = 0; i < 2; i++) {
a = a << 8;
a += in.readByte();
}
} catch (IOException ex) {
Logger.getLogger(ReadCommand.class.getName()).log(Level.SEVERE, null, ex);
return;
}
sk = a ^ 0xC00000;
} else {
if (a < 0xF0) {
try {
for (i = 0; i < 3; i++) {
a = a << 8;
a += in.readByte();
}
} catch (IOException ex) {
Logger.getLogger(ReadCommand.class.getName()).log(Level.SEVERE, null, ex);
return;
}
sk = a ^ 3758096384L;
} else {
if (a < 0xF8) {
try {
a = 0;
for (i = 0; i < 5; i++) {
a = a << 8;
a += in.readByte();
}
} catch (IOException ex) {
Logger.getLogger(ReadCommand.class.getName()).log(Level.SEVERE, null, ex);
return;
}
} else {
}
}
}
}
}
s += "\n";
byte[] bb = new byte[sk];
try {
a = in.read(bb, 0, sk);
} catch (IOException ex) {
a = 0;
ex.printStackTrace();
return;
}
if (a > 0) {
s += new String(bb);
}
} else if (b == -1) {
System.out.println("Error, it should not happen ever, or connected to wrong port");
} else {
try {
queue.put(s);
} catch (InterruptedException ex) {
ex.printStackTrace();
System.out.println("exiting reader");
return;
}
s = "";
}
}
}
}
public class WriteCommand {
private byte[] len = {0};
private DataOutputStream out = null;
private String command = "";
WriteCommand(DataOutputStream out, String command) {
this.out = out;
this.command = command.replaceAll("\n", "").trim();
}
WriteCommand(DataOutputStream out) {
this.out = out;
}
public void 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() | 3758096384L);
} else {
i = Integer.reverseBytes(command.length());
}
s = Integer.toHexString(i);
if (s.length() < 2) {
return new byte[]{i.byteValue()};
} else {
for (int j = 0; j < s.length(); j += 2) {
ret += (char) Integer.parseInt(s.substring(j, j + 2), 16) != 0 ? (char) Integer.parseInt(s.substring(j, j + 2), 16) : "";
}
}
//char[] ch = ret.toCharArray();
return ret.getBytes();
}
String runCommand() {
int i, a, j;
int counter;
byte[] ret = new byte[0];
try {
if (!command.contains("\n")) {
byte[] b = writeLen(command);
int retLen = b.length + command.length() + 1;
ret = (byte[])Arrays.copyOf(ret, retLen);
for (i = 0; i < b.length; i++) {
ret[i] = b[i];
}
//System.arraycopy(b, 0, ret, 0, b.length);
//i = b.length;
for (byte c : command.getBytes()) { //"US-ASCII"
ret[i] = c;
i = i + 1;
}
} else {
String[] str = command.split("\n");
i = 1;
int[] iTmp = new int[str.length];
for (a = 0; a < str.length; a++) {
iTmp[a] = writeLen(str[a]).length + str[a].length();
}
for (int b : iTmp) {
i += b;
}
ret = (byte[])Arrays.copyOf(ret, i);
counter = 0;
for (a = 0; a < str.length; a++) {
byte[] b = writeLen(str[a]);
for (j = 0; j < b.length; j++) {
ret[counter] = b[j];
counter = counter + 1;
}
for (byte c : str[a].getBytes()) { //"US-ASCII"
ret[counter] = c;
counter = counter + 1;
}
}
}
/*for(i = 0; i < ret.length; i++) {
print(i.toString() + " : " + ret[i].toString());
}*/
out.write(ret, 0, ret.length);
return "Sent successfully";
} catch (IOException ex) {
Logger.getLogger(WriteCommand.class.getName()).log(Level.SEVERE, null, ex);
return "failed";
}
}
}
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;
private LinkedBlockingQueue queue = new LinkedBlockingQueue(40);
/**
* Constructor of the connection class
* @param ipAddress - IP address of the router you want to conenct to
* @param ipPort - port used for connection, ROS default is 8728
*/
public ApiConn(String ipAddress, int ipPort) {
this.ipAddress = ipAddress;
this.ipPort = ipPort;
//this.setName("settings");
}
/**
* State of connection
* @return - if connection is established to router it returns true.
*/
public boolean isConnected() {
return connected;
}
public void disconnect() throws IOException{
listener.interrupt();
sock.close();
}
public 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) {
writeCommand.setCommand(s);
return writeCommand.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, String password) {
this.sendCommand("/login");
String s = "a";
String[] tmp;
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) {
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") + 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";
}
public void run() {
try {
InetAddress ia = InetAddress.getByName(ipAddress);
if (ia.isReachable(1000)) {
sock = new Socket(ipAddress, ipPort);
in = new DataInputStream(sock.getInputStream());
out = new DataOutputStream(sock.getOutputStream());
connected = true;
readCommand = new ReadCommand(in, queue);
writeCommand = new WriteCommand(out);
this.listen();
message = "Connected";
} else {
message = "Not reachable";
}
} catch (UnknownHostException ex) {
connected = false;
message = ex.getMessage();
ex.printStackTrace();
} catch (IOException ex) {
connected = false;
message = ex.getMessage();
ex.printStackTrace();
}
}
}
public class ApiAnswer {
private String[] Text;
public ApiAnswer() {};
public ApiAnswer(String data) {
Text = data.split("\n");
}
public change(String data) {
Text = data.split("\n");
}
public String getParam(String param) {
String[] tmp;
String line;
for(line: Text) {
if (line.contains(param)) {
tmp = line.split(param);
if (tmp.length > 1) {
return tmp[tmp.length - 1];
}
else return "";
}
}
return "";
}
public boolean isError() {
String line;
for (line: Text) {
if (line.contains("!trap"))
return true;
}
return false;
}
public boolean isDone() {
String line;
for (line: Text) {
if (line.contains("!done"))
return true;
}
return false;
}
public String[] getText() {
return Text;
}
}
public class ApiCommand {
private String Text;
public ApiCommand() {
Text = "";
}
public void clear() {
Text = "";
}
public void add(String line) {
if(Text.length() == 0) {
Text = line;
}
else {
Text = Text + "\n" + line;
}
}
public String getText() {
return Text;
}
}
И мой пример ее использования. Скрипт для шлюза Микротик с использованием выборки типа правил из вики.
Код:
import java.util.*;
import java.util.regex.*;
import java.text.*;
import java.sql.*;
import ru.bitel.bgbilling.common.*;
import bitel.billing.common.module.ipn.*;
import bitel.billing.server.contract.bean.*;
import bitel.billing.server.ipn.bean.*;
import bitel.billing.server.ipn.*;
import bitel.billing.server.util.*;
//import bitel.billing.common.KernelConst;
includeBGBS("bgbs://ru.bitel.bgbilling.kernel.script.common.bean.ScriptLibrary/mikrotik");
private getNewRule(status)
{
SERVICE_INTERNET = 27;
SERVICE_INTRANET = 29;
// if (status.ruleType == null){
// return;
// };
H = new Hashtable();
H.put("operation", "get_ipn_rule_id");
H.put("cid", status.contractId);
H.put("mid", status.mid);
H.put("sid", SERVICE_INTERNET);
H.put("time", new GregorianCalendar());
ParseTarif(H);
rule_id = H.get("rule_id");
//rule_id = 1;
if (rule_id != null)
{
rule_type_manager = new RuleTypeManager(con,status.mid);
status.ruleType = rule_type_manager.getType(Integer.parseInt(rule_id));
return status;
}
else return null;
}
private void ParseTarif(H)
{
H.put("is_found", false);
cid = H.get("cid");
if (cid == null)
{
return;
};
time = H.get("time");
if (time == null)
{
return;
};
formatter = new SimpleDateFormat("yyyy-MM-dd");
tarif_date = formatter.format(time.getTime()).toString();
query = "SELECT contract_tree_link.tree_id "+
"FROM contract_tree_link "+
"WHERE "+
"(contract_tree_link.date1 is NULL OR contract_tree_link.date1<='"+tarif_date+"') AND "+
"(contract_tree_link.date2 is NULL OR contract_tree_link.date2>='"+tarif_date+"') AND "+
"contract_tree_link.cid='"+cid+"' "+
"UNION "+
"SELECT tariff_tree_link.tree_id "+
"FROM contract_tariff "+
" INNER JOIN tariff_tree_link "+
" ON (contract_tariff.tpid=tariff_tree_link.tpid) "+
"WHERE (contract_tariff.date1 is NULL OR contract_tariff.date1<='"+tarif_date+"') AND "+
"(contract_tariff.date2 is NULL OR contract_tariff.date2>='"+tarif_date+"') AND "+
"contract_tariff.cid='"+cid+"'";
re = con.prepareStatement(query);
rs = re.executeQuery();
while (rs.next())
{
tree_id = rs.getString(1);
ParseMTree(tree_id,H);
};
}
private void ParseMTree(tree_id,H)
{
is_found = H.get("is_found");
if (is_found == null)
{
return;
};
if (is_found)
{
return;
};
mid = H.get("mid");
if (mid == null)
{
return;
};
query = "SELECT mtree_node.id "+
"FROM module_tariff_tree "+
" INNER JOIN mtree_node "+
" ON (module_tariff_tree.id=mtree_node.mtree_id) "+
"WHERE mtree_node.type='root' AND module_tariff_tree.tree_id='"+tree_id+"' AND module_tariff_tree.mid='"+mid+"'";
re = con.prepareStatement(query);
rs = re.executeQuery();
while (rs.next())
{
node_id = rs.getString(1);
ParseNode(node_id,H);
};
}
private void ParseNode(node_id,H)
{
is_found = H.get("is_found");
if (is_found == null)
{
return;
};
if (is_found)
{
return;
};
sid = H.get("sid");
if (sid == null)
{
return;
};
operation = H.get("operation");
if (operation == null)
{
return;
};
time = H.get("time");
if (time == null)
{
return;
};
query = "SELECT id, type, data FROM mtree_node WHERE parent_node='"+node_id+"'";
re = con.prepareStatement(query);
rs = re.executeQuery();
while (rs.next())
{
id = rs.getString(1);
type = rs.getString(2);
data = rs.getString(3);
if (type.equals("service") && sid != Integer.parseInt(data))
{
continue;
};
if (type.equals("ipn_rule") && operation.equals("get_ipn_rule_id"))
{
datas = data.split ("&");
H.put("rule_id",datas[1]);
H.put("is_found",true);
};
if (type.equals("period"))
{
datas = data.split ("%");
date1 = null;
date2 = null;
for (i=0; i<datas.length; i++)
{
date_arr = datas[i].split("&");
if (date_arr.length != 2)
{
continue;
};
formatter = new SimpleDateFormat("dd.MM.yyy");
date = formatter.parse(date_arr[1]);
if (date_arr[0].equals("date1"))
{
date1 = date;
};
if (date_arr[0].equals("date2"))
{
date2 = date;
};
};
if (date1 != null && date1.compareTo(time.getTime()) != -1){
continue;
};
if (date2 != null && date2.compareTo(time.getTime()) != 1){
continue;
};
};
ParseNode(id,H);
};
};
protected void doSync()
{
host = gate.getHost();
port = gate.getPort();
gateSetup = new DefaultServerSetup( gate.getConfig(), "\r\n" );
login = gateSetup.getStringValue( "login", "root" );
timeout = gateSetup.getIntValue( "timeout", 2000 );
pswd = gate.getKeyword();
ApiAnswer answer = new ApiAnswer();
ApiCommand command = new ApiCommand();
String str, lastRes, idList;
boolean newIdList;
String commandMark = "#";
ApiConn conn = new ApiConn(host, port);
conn.run();
if (conn.isConnected()) {
print(conn.login(login, pswd));
if ( log.isDebugEnabled() )
{
log.info( " gate: " + host + ":" + port + " login: " + login + " pswd: " + pswd );
}
session = null;
try
{
command_list = null;
for ( UserStatus status : statusList )
{
cid = status.contractId;
status = getNewRule(status);
ruleType = status.ruleType;
if (ruleType != null)
{
rule_id = ruleType.getId();
}
else {
rule_id = 0;
};
rules = null;
if ( status.status == IPNContractStatus.STATUS_OPEN )
{
//открываем
rules = getOpenRules( status );
log.info("OpenRules !!"+cid+"!!");
}
else {
if ( status.status == IPNContractStatus.STATUS_REMOVED )
{
// удаляем
rules = getDeleteRules( status );
log.info("DeleteRules !!"+cid+"!!");
}
else
{
// закрываем
rules = getCloseRules( status );
log.info("CloseRules !!"+cid+"!!");
};
};
if (rules != null)
{
lastRes = "";
idList = "";
for ( String rule : rules )
if (rule.length() > 0) {
if((rule.indexOf(commandMark) == 0)&&(command.getText().length() > 0)) {
print(command.getText());
print(conn.sendCommand(command.getText()));
print("*****");
newIdList = true;
do {
str = conn.getData();
print(str);
answer.change(str);
command.clear();
if(answer.isError()) {
print("error");
//str = conn.getData();
//print(str);
}
if(answer.getParam("=ret=").length() > 0) {
lastRes = answer.getParam("=ret=");
//print("result=" + lastRes);
}
if(answer.getParam("=.id=").length() > 0) {
if (newIdList) {
idList = answer.getParam("=.id=");
newIdList = false;
}
else {
idList = idList + "," + answer.getParam("=.id=");
}
}
} while (!answer.isDone());
print("\n");
}
else {
str = rule;
str = str.replaceAll("\\{RESULT\\}", lastRes);
str = str.replaceAll("\\{LIST\\}", idList);
command.add(str);
};
}
}
else print("no rules");
}
}
catch( Exception e )
{
throw new RuntimeException( e );
}
finally
{
if ( conn.isConnected() )
{
conn.disconnect();
}
}
}
else throw new RuntimeException("Cannot connect to router");
}
private getOpenRules( status )
{
return getRules( status, "\\[OPEN\\](.*)\\[/OPEN\\]" );
}
private getCloseRules( status )
{
return getRules( status, "\\[CLOSE\\](.*)\\[/CLOSE\\]" );
}
private getDeleteRules( status )
{
return getRules( status, "\\[DELETE\\](.*)\\[/DELETE\\]" );
}
private getRules( status, template )
{
// пользовательское правило, без типа - то все оставляем как есть
String ruleText = ManadUtils.getRule( status.gateType, status.ruleType );
//получаем списки адресов для данного клиента на текущий момент
now = new java.util.GregorianCalendar();
ARM = new AddressRangeManager( con, status.mid);
addressList = ARM.getContractAddressRange(status.contractId, now, 0);
for (addressRange: addressList) {
pool = addressRange.toShortString(); //приводим к нужному формату
}
pool = pool.replace(" ", "");
//получаем номер влана для клиента
VLANID_PID = 9;
CPM = new ContractParameterManager( con );
vlanid = CPM.getStringParam(status.contractId, VLANID_PID);
// типизированное правило
if ( status.ruleType != null )
{
rule = generateRule( pool, status.gateType, status.ruleType, status.contractId, vlanid );
}
else
{
rule = status.rule.getRuleText();
}
pattern = Pattern.compile( template, Pattern.DOTALL );
m = pattern.matcher( rule );
if ( m.find() )
{
rule = m.group( 1 );
}
rule.replaceAll( "\r", "" );
parts = rule.split( "\n" ); //приводим к нужному формату
result = new ArrayList();
for ( String part : parts )
{
if ( !Utils.isEmptyString( part ))
{
result.add( part );
}
}
return result;
// return rule.split( "\n" );
}
private generateRule( pool, gateType, ruleType, cid, vlanid )
{
String rule;
String ruleTypeData = ruleType.getData();
ruleTypeData.replaceAll("\r", "");
templates = ruleTypeData.split("\n");
ruleText = ManadUtils.getRule( gateType, ruleType );
replacements = new HashMap();
replacements.put( "\\{CID\\}", String.valueOf( cid ) );
replacements.put( "\\{VID\\}", String.valueOf( vlanid ) );
replacements.put( "\\{POOL\\}", pool );
//делаем подстановку макросов из текущего типа правила.
for(String template : templates) {
splitter = template.indexOf("=");
key = template.substring(0, splitter - 1); //имя макроса
value = template.substring(splitter + 1, template.length() - 1); //значение
replacements.put("$\\{"+key+"\\}", value);
};
addresses = "";
rule = ManadUtils.generateRule( ruleText, addresses, replacements, ruleType );
return rule;
}
Мне пришлось эмулировать работу команды find в виде отдельных макросов подстановки: {RESULT} - результат выполнения последней команды; {LIST} - список элементов, выданный последней командой.
Другие макросы: {POOL} - выданный клиенту пул адресов, {VID} - введенный вручную номер влана, {CID} - номер договора.
Команды для скриптового шлюза:
Код:
[DEFAULT]
[OPEN]
/ip/firewall/address-list/print
?=address={POOL}
?=list=closed_clients
#
/ip/firewall/address-list/remove
=numbers={LIST}
#
/queue/simple/add
=name=vlan{VID}
=interface=vlan{VID}
=limit-at=${upload}/${download}
=max-limit=${upload}/${download}
=disabled=no
#
[/OPEN]
[CLOSE]
/ip/firewall/address-list/add
=address={POOL}
=list=closed_clients
=comment=!!{CID}!!
#
/queue/simple/remove
=numbers=vlan{VID}
#
[/CLOSE]
[DELETE]
/ip/firewall/address-list/add
=address={POOL}
=list=closed_clients
=comment=!!{CID}!!
#
/queue/simple/remove
=numbers=vlan{VID}
#
[/DELETE]
[/DEFAULT]
Все команды отлично работают в версии биллинга 4.6 и должны работать в 5.0.