1
1
#![ forbid( unsafe_code) ]
2
2
use std:: net:: ToSocketAddrs ;
3
3
use std:: num;
4
- use std:: net:: { TcpStream , Shutdown } ;
4
+ use std:: net:: { IpAddr , TcpStream , Shutdown } ;
5
5
use std:: io;
6
6
use std:: io:: { Read , Write } ;
7
7
// use std::str;
8
8
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 ;
11
11
use std:: fs:: File ;
12
12
use std:: fmt;
13
13
@@ -569,11 +569,9 @@ impl<T: Read + Write> Controller<T> {
569
569
Ok ( res)
570
570
}
571
571
572
- // So far we only support one keyword.
573
- // TODO: Supporting multiple keywords would imply returning a dictionary.
572
+ // GETINFO
574
573
// The output is not parsed (you are on your own), it's just a string containing the return
575
574
// value (the 'keyword=' part is stripped).
576
- // GETINFO
577
575
pub fn cmd_getinfo ( & mut self , info_key : & str ) -> Result < String , Error > {
578
576
let reply = self . raw_cmd ( format ! ( "GETINFO {}" , info_key) . as_str ( ) ) ?;
579
577
let reply_line = & reply. lines [ 0 ] ;
@@ -587,6 +585,38 @@ impl<T: Read + Write> Controller<T> {
587
585
}
588
586
}
589
587
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
+
590
620
// AUTHENTICATE
591
621
pub fn cmd_authenticate ( & mut self , pwd : & [ u8 ] ) -> Result < Reply , Error > {
592
622
self . raw_cmd ( format ! ( "AUTHENTICATE {}" , hex:: encode( pwd) ) . as_str ( ) )
@@ -642,27 +672,97 @@ impl<T: Read + Write> Controller<T> {
642
672
self . raw_cmd ( & format ! ( "DEL_ONION {}" , service_id. as_ref( ) ) ) . map ( |_|( ) )
643
673
}
644
674
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
+
645
708
// SETCONF
709
+ pub fn cmd_setconf ( & mut self , args : & [ ( & str , & str ) ] ) -> Result < ( ) , Error > {
710
+ self . cmd_key_val_list ( "SETCONF" , args)
711
+ }
712
+
646
713
// 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
+
648
718
// 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
+ }
650
754
651
755
// SETEVENTS
652
756
// SIGNAL
653
- // MAPADDRESS
654
757
// EXTENDCIRCUIT
655
758
// SETCIRCUITPURPOSE
656
- // SETROUTERPURPOSE
657
759
// ATTACHSTREAM
658
760
// POSTDESCRIPTOR
659
761
// REDIRECTSTREAM
660
762
// CLOSESTREAM
661
763
// CLOSECIRCUIT
662
764
// USEFEATURE
663
765
// RESOLVE
664
- // TAKEOWNERSHIP
665
- // DROPGUARDS
666
766
// HSFETCH
667
767
// HSPOST
668
768
}
0 commit comments