API rust

From MikroTik Wiki
Jump to navigation Jump to search

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