Skip to content

Commit a1807ca

Browse files
authored
Merge pull request #7 from sklv/dev-update
Add some basic commands. Implement multi keyword GETINFO.
2 parents 7ca8693 + 7ce8c23 commit a1807ca

File tree

1 file changed

+112
-12
lines changed

1 file changed

+112
-12
lines changed

src/control.rs

+112-12
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#![forbid(unsafe_code)]
22
use std::net::ToSocketAddrs;
33
use std::num;
4-
use std::net::{TcpStream, Shutdown};
4+
use std::net::{IpAddr, TcpStream, Shutdown};
55
use std::io;
66
use std::io::{Read, Write};
77
// use std::str;
88
use std::io::{BufReader, BufRead, BufWriter};
9-
// use std::option::Option;
10-
// use std::collections::HashMap;
9+
use std::option::Option;
10+
use std::collections::HashMap;
1111
use std::fs::File;
1212
use std::fmt;
1313

@@ -569,11 +569,9 @@ impl<T: Read + Write> Controller<T> {
569569
Ok(res)
570570
}
571571

572-
// So far we only support one keyword.
573-
// TODO: Supporting multiple keywords would imply returning a dictionary.
572+
// GETINFO
574573
// The output is not parsed (you are on your own), it's just a string containing the return
575574
// value (the 'keyword=' part is stripped).
576-
// GETINFO
577575
pub fn cmd_getinfo(&mut self, info_key: &str) -> Result<String, Error> {
578576
let reply = self.raw_cmd(format!("GETINFO {}", info_key).as_str())?;
579577
let reply_line = &reply.lines[0];
@@ -587,6 +585,38 @@ impl<T: Read + Write> Controller<T> {
587585
}
588586
}
589587

588+
pub fn cmd_getinfos(&mut self, args: &[&str]) -> Result<HashMap<String,Vec<String>>, Error> {
589+
let mut req = String::from("GETINFO");
590+
let mut res: HashMap<String, Vec<String>> = HashMap::new();
591+
592+
for arg in args.iter() {
593+
req.push_str(&format!(" {}", arg));
594+
}
595+
596+
for line in self.raw_cmd(&req)?.lines.iter() {
597+
if line.reply == "OK" {
598+
return Ok(res);
599+
}
600+
601+
let key_value_parts: Vec<&str> = line.reply.splitn(2, '=').collect();
602+
let key = key_value_parts[0].to_string();
603+
604+
let mut value: Vec<String> = match res.get(&key) {
605+
Some(val) => val.to_owned(),
606+
None => Vec::new()
607+
};
608+
609+
if key_value_parts.len() > 1 {
610+
value.push(key_value_parts[1].to_string());
611+
}
612+
613+
res.insert(key, value);
614+
}
615+
616+
// OK not returned
617+
return Err(Error::ParseReply(ParseReplyError::KeyNotFound));
618+
}
619+
590620
// AUTHENTICATE
591621
pub fn cmd_authenticate(&mut self, pwd: &[u8]) -> Result<Reply, Error> {
592622
self.raw_cmd(format!("AUTHENTICATE {}", hex::encode(pwd)).as_str())
@@ -642,27 +672,97 @@ impl<T: Read + Write> Controller<T> {
642672
self.raw_cmd(&format!("DEL_ONION {}", service_id.as_ref())).map(|_|())
643673
}
644674

675+
// SAVECONF
676+
pub fn cmd_saveconf(&mut self, force: bool) -> Result<(), Error> {
677+
self.raw_cmd(&format!("SAVECONF{}", if force {" FORCE"} else {""})).map(|_|())
678+
}
679+
680+
// GETCONF
681+
pub fn cmd_getconf(&mut self, args: &[&str]) -> Result<HashMap<String, Vec<String>>, Error> {
682+
let mut req = String::from("GETCONF");
683+
let mut res: HashMap<String, Vec<String>> = HashMap::new();
684+
685+
for arg in args.iter() {
686+
req.push_str(&format!(" {}", arg));
687+
}
688+
689+
for line in self.raw_cmd(&req)?.lines.iter() {
690+
let key_value_parts: Vec<&str> = line.reply.splitn(2, '=').collect();
691+
let key = key_value_parts[0].to_string();
692+
693+
let mut value: Vec<String> = match res.get(&key) {
694+
Some(val) => val.to_owned(),
695+
None => Vec::new()
696+
};
697+
698+
if key_value_parts.len() > 1 {
699+
value.push(key_value_parts[1].to_string());
700+
}
701+
702+
res.insert(key, value);
703+
}
704+
705+
return Ok(res);
706+
}
707+
645708
// SETCONF
709+
pub fn cmd_setconf(&mut self, args: &[(&str, &str)]) -> Result<(), Error> {
710+
self.cmd_key_val_list("SETCONF", args)
711+
}
712+
646713
// RESETCONF
647-
// GETCONF
714+
pub fn cmd_resetconf(&mut self, args: &[(&str, &str)]) -> Result<(), Error> {
715+
self.cmd_key_val_list("RESETCONF", args)
716+
}
717+
648718
// LOADCONF
649-
// SAVECONF
719+
pub fn cmd_loadconf(&mut self, conf: &str) -> Result<(), Error> {
720+
self.raw_cmd(&format!("+LOADCONF\r\n{}\r\n.", conf)).map(|_|())
721+
}
722+
723+
fn cmd_key_val_list(&mut self, cmd: &str, args: &[(&str, &str)]) -> Result<(), Error> {
724+
let mut req = String::from(cmd);
725+
for (key, val) in args {
726+
req.push_str(&format!(" {}={}", key, val));
727+
}
728+
return self.raw_cmd(&req).map(|_|())
729+
}
730+
731+
// MAPADDRESS
732+
pub fn cmd_mapaddress(&mut self, vars: &[(&IpAddr, &str)]) -> Result<(), Error> {
733+
let mut req = String::from("MAPADDRESS");
734+
for (key, val) in vars {
735+
req.push_str(&format!(" {}={}", key, val));
736+
}
737+
self.raw_cmd(&req).map(|_|())
738+
}
739+
740+
// TAKEOWNERSHIP
741+
pub fn cmd_takeownership(&mut self) -> Result<(), Error> {
742+
self.raw_cmd("TAKEOWNERSHIP").map(|_|())
743+
}
744+
745+
// DROPOWNERSHIP
746+
pub fn cmd_dropownership(&mut self) -> Result<(), Error> {
747+
self.raw_cmd("DROPOWNERSHIP").map(|_|())
748+
}
749+
750+
// DROPGUARDS
751+
pub fn cmd_dropguards(&mut self) -> Result<(), Error> {
752+
self.raw_cmd("DROPGUARDS").map(|_|())
753+
}
650754

651755
// SETEVENTS
652756
// SIGNAL
653-
// MAPADDRESS
654757
// EXTENDCIRCUIT
655758
// SETCIRCUITPURPOSE
656-
// SETROUTERPURPOSE
657759
// ATTACHSTREAM
658760
// POSTDESCRIPTOR
659761
// REDIRECTSTREAM
660762
// CLOSESTREAM
661763
// CLOSECIRCUIT
662764
// USEFEATURE
663765
// RESOLVE
664-
// TAKEOWNERSHIP
665-
// DROPGUARDS
666766
// HSFETCH
667767
// HSPOST
668768
}

0 commit comments

Comments
 (0)