API rust
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!"); }