API rust
From MikroTik Wiki
About author and licensing of the code
API example in RUST by Jason Cardinal This code is licensed under GPL v3
link to license: GPLv3
It uses blocking i/o as RUST does not really have 100% working non-glocking solution to ensure no data loss.
Library
routeros.rs
#![crate_name = "router_os"]
#![crate_type = "lib"]
extern crate crypto;
use crypto::md5::Md5;
use crypto::digest::Digest;
use std::collections::BTreeMap;
use std::io::prelude::*;
use std::net::TcpStream;
fn hex_binascii<'a>(hexstr: String) -> Result<Vec<u8>, &'a str> {
if hexstr.len() % 2 != 0 {
Err("Odd number of characters")
}else {
let mut result: Vec<u8> = Vec::new();
let mut i = 0;
let c_hexstr: Vec<char> = hexstr.chars().collect();
while i < c_hexstr.len()-1 {
let top = c_hexstr[i].to_digit(16).unwrap() as u8;
let bottom = c_hexstr[i+1].to_digit(16).unwrap() as u8;
let r = (top << 4) + bottom;
result.push(r);
i += 2;
}
Ok(result)
}
}
pub struct ApiRos<'a> {
stream: &'a mut TcpStream,
}
impl<'a> ApiRos<'a> {
pub fn new(s: &'a mut TcpStream) -> ApiRos<'a> {
ApiRos {
stream: s
}
}
pub fn try_read(&mut self) -> bool {
if self.stream.read(&mut [0]).unwrap() > 0 {
true
}else {
false
}
}
fn write_str(&mut self, str_buff: &[u8]) {
match self.stream.write(str_buff) {
Ok(_) => {}
Err(e) => { panic!("connection closed by remote end, {}", e); }
}
}
fn read_str(&mut self, length: usize) -> String {
let mut buff: Vec<u8> = Vec::new();
for _ in 0 .. length {
let mut tmp_buff: [u8; 1] = [0];
match self.stream.read(&mut tmp_buff) {
Ok(_) => {}
Err(e) => { panic!("connection closed by remote end, {}", e); }
}
buff.push(tmp_buff[0]);
}
String::from_utf8(buff).unwrap()
}
fn write_len(&mut self, len: u32) {
if len < 0x80 {
self.write_str(&[len as u8]);
} else if len < 0x4000 {
let l = len | 0x8000;
self.write_str( &[((l >> 8) & 0xFF) as u8] );
self.write_str( &[(l & 0xFF) as u8] );
} else if len < 0x200000 {
let l = len | 0xC00000;
self.write_str( &[((l >> 16) & 0xFF) as u8] );
self.write_str( &[((l >> 8) & 0xFF) as u8] );
self.write_str( &[(l & 0xFF) as u8] );
} else if len < 0x10000000 {
let l = len | 0xE0000000;
self.write_str( &[((l >> 16) & 0xFF) as u8] );
self.write_str( &[((l >> 24) & 0xFF) as u8] );
self.write_str( &[((l >> 8) & 0xFF) as u8] );
self.write_str( &[(l & 0xFF) as u8] );
} else {
self.write_str( &[(0xF0) as u8] );
self.write_str( &[((len >> 24) & 0xFF) as u8] );
self.write_str( &[((len >> 16) & 0xFF) as u8] );
self.write_str( &[((len >> 8) & 0xFF) as u8] );
self.write_str( &[(len & 0xFF) as u8] );
}
}
fn read_len(&mut self) -> u32 {
let mut c: u32 = self.read_str(1).as_bytes()[0] as u32;
if c & 0x80 == 0x00 {
} else if c & 0xC0 == 0x80 {
c &= !0xC0;
c <<= 8;
c += self.read_str(1).as_bytes()[0] as u32;
} else if c & 0xE0 == 0xC0 {
c &= !0xE0;
c <<= 8;
c += self.read_str(1).as_bytes()[0] as u32;
c <<= 8;
c += self.read_str(1).as_bytes()[0] as u32;
} else if c & 0xF0 == 0xE0 {
c &= !0xF0;
c <<= 8;
c += self.read_str(1).as_bytes()[0] as u32;
c <<= 8;
c += self.read_str(1).as_bytes()[0] as u32;
c <<= 8;
c += self.read_str(1).as_bytes()[0] as u32;
} else if c & 0xF8 == 0xF0 {
c = self.read_str(1).as_bytes()[0] as u32;
c <<= 8;
c += self.read_str(1).as_bytes()[0] as u32;
c <<= 8;
c += self.read_str(1).as_bytes()[0] as u32;
c += self.read_str(1).as_bytes()[0] as u32;
c <<= 8;
}
c
}
fn read_word(&mut self) -> String {
let len = self.read_len();
let ret = self.read_str(len as usize);
if len > 0 { println!(">>> {}", ret); }
ret
}
fn write_word(&mut self, w: String) {
if w.len() > 0 { println!("<<< {}", w); }
self.write_len(w.len() as u32);
self.write_str(&w.as_bytes());
}
pub fn write_sentence(&mut self, words: Vec<String>) -> u32{
let mut ret: u32 = 0;
for w in words {
self.write_word(w);
ret += 1;
}
self.write_word("".to_string());
ret
}
pub fn read_sentence(&mut self) -> Vec<String> {
let mut r: Vec<String> = Vec::new();
loop {
let w = self.read_word();
if &w[..] == "" {
return r;
}
r.push(w);
}
}
fn talk(&mut self, words: Vec<String>) -> Vec<(String, BTreeMap<String, String>)>{
if self.write_sentence(words) == 0 {
return vec![];
}
let mut r: Vec<(String, BTreeMap<String, String>)> = Vec::new();
loop {
let i: Vec<String> = self.read_sentence();
if i.len() == 0 {
continue;
}
let reply: String = i[0].clone();
let mut attrs: BTreeMap<String, String> = BTreeMap::new();
for w in &i[1..] {
match w[1..].find("=") {
Some(n) => { attrs.insert( w[..n+1].to_string() ,
w[(n+2)..].to_string() ); }
None => { attrs.insert(w.clone(), "".to_string()); }
};
}
r.push((reply.clone(), attrs));
if reply == "!done" {
return r;
}
}
}
pub fn login(&mut self, username: String, pwd: String) {
let mut chal: Vec<u8> = Vec::new();
for (_ /*reply*/, attrs) in self.talk(vec![r"/login".to_string()]) {
chal = hex_binascii(attrs["=ret"].clone()).unwrap();
}
let mut md = Md5::new();
md.input(&[0]);
md.input(pwd.as_bytes());
md.input(&chal[..]);
self.talk(vec![r"/login".to_string(), format!("=name={}", username),
format!("=response=00{}",md.result_str())]);
}
}
Example use of the library
example.rs
extern crate router_os;
use router_os::ApiRos;
use std::net::TcpStream;
use std::io::BufRead;
use std::io;
fn get_line() -> String {
let stdin_u = io::stdin();
let mut stdin = stdin_u.lock();
let mut line = String::new();
stdin.read_line(&mut line).unwrap();
line.pop();
return line;
}
fn main() {
let mut stream = TcpStream::connect("192.168.1.1:8728").unwrap();
let mut apiros = ApiRos::new(&mut stream);
apiros.login("username".to_string(), "password".to_string());
let mut input_sentence: Vec<String> = Vec::new();
let mut has_written = false;
let mut was_command = false;
println!("Type '#quit#' to exit program");
'main_loop: loop {
if has_written {
'reply_loop: loop {
let replies = apiros.read_sentence();
if replies.len() == 0 {
continue;
}
if replies[0] == "!done" {
has_written = false;
break 'reply_loop;
}
}
}else {
let input = get_line();
if &input[..] == "#quit#" {
break 'main_loop;
}
if &input[..] == "" && was_command {
apiros.write_sentence(input_sentence.clone());
input_sentence.clear();
was_command = false;
has_written = true;
}else {
input_sentence.push(input);
was_command = true;
has_written = false;
}
}
}
println!("Goodbye!");
}