nut_client/blocking/
mod.rs1use std::io::{BufRead, BufReader, Write};
2use std::net::{SocketAddr, TcpStream};
3
4use crate::blocking::stream::ConnectionStream;
5use crate::cmd::{Command, Response};
6use crate::{ClientError, Config, Host, NutError};
7
8mod stream;
9
10pub enum Connection {
12 Tcp(TcpConnection),
14}
15
16impl Connection {
17 pub fn new(config: &Config) -> crate::Result<Self> {
19 let mut conn = match &config.host {
20 Host::Tcp(host) => Self::Tcp(TcpConnection::new(config.clone(), &host.addr)?),
21 };
22
23 conn.get_network_version()?;
24 conn.login(config)?;
25
26 Ok(conn)
27 }
28
29 pub fn close(mut self) -> crate::Result<()> {
31 self.logout()?;
32 Ok(())
33 }
34
35 fn login(&mut self, config: &Config) -> crate::Result<()> {
37 if let Some(auth) = config.auth.clone() {
38 self.set_username(&auth.username)?;
40
41 if let Some(password) = &auth.password {
43 self.set_password(password)?;
44 }
45 }
46 Ok(())
47 }
48}
49
50pub struct TcpConnection {
52 config: Config,
53 stream: ConnectionStream,
54}
55
56impl TcpConnection {
57 fn new(config: Config, socket_addr: &SocketAddr) -> crate::Result<Self> {
58 let tcp_stream = TcpStream::connect_timeout(socket_addr, config.timeout)?;
60 let mut connection = Self {
61 config,
62 stream: ConnectionStream::Plain(tcp_stream),
63 };
64 connection = connection.enable_ssl()?;
65 Ok(connection)
66 }
67
68 #[cfg(feature = "ssl")]
69 fn enable_ssl(mut self) -> crate::Result<Self> {
70 if self.config.ssl {
71 self.write_cmd(Command::StartTLS)?;
72 self.read_response()
73 .map_err(|e| {
74 if let crate::ClientError::Nut(NutError::FeatureNotConfigured) = e {
75 crate::ClientError::Nut(NutError::SslNotSupported)
76 } else {
77 e
78 }
79 })?
80 .expect_ok()?;
81
82 let mut ssl_config = rustls::ClientConfig::new();
83 let sess = if self.config.ssl_insecure {
84 ssl_config
85 .dangerous()
86 .set_certificate_verifier(std::sync::Arc::new(
87 crate::ssl::InsecureCertificateValidator::new(&self.config),
88 ));
89
90 let dns_name = webpki::DNSNameRef::try_from_ascii_str("www.google.com").unwrap();
91
92 rustls::ClientSession::new(&std::sync::Arc::new(ssl_config), dns_name)
93 } else {
94 let hostname = self
96 .config
97 .host
98 .hostname()
99 .ok_or(ClientError::Nut(NutError::SslInvalidHostname))?;
100
101 let dns_name = webpki::DNSNameRef::try_from_ascii_str(&hostname)
102 .map_err(|_| ClientError::Nut(NutError::SslInvalidHostname))?;
103
104 ssl_config
105 .root_store
106 .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
107
108 rustls::ClientSession::new(&std::sync::Arc::new(ssl_config), dns_name)
109 };
110
111 self.stream = self.stream.upgrade_ssl(sess)?;
113 }
114 Ok(self)
115 }
116
117 #[cfg(not(feature = "ssl"))]
118 fn enable_ssl(self) -> crate::Result<Self> {
119 Ok(self)
120 }
121
122 pub(crate) fn write_cmd(&mut self, line: Command) -> crate::Result<()> {
123 let line = format!("{}\n", line);
124 if self.config.debug {
125 eprint!("DEBUG -> {}", line);
126 }
127 self.stream.write_all(line.as_bytes())?;
128 self.stream.flush()?;
129 Ok(())
130 }
131
132 fn parse_line(
133 reader: &mut BufReader<&mut ConnectionStream>,
134 debug: bool,
135 ) -> crate::Result<Vec<String>> {
136 let mut raw = String::new();
137 reader.read_line(&mut raw)?;
138 if debug {
139 eprint!("DEBUG <- {}", raw);
140 }
141 raw = raw[..raw.len() - 1].to_string(); let args = shell_words::split(&raw)
145 .map_err(|e| NutError::Generic(format!("Parsing server response failed: {}", e)))?;
146
147 Ok(args)
148 }
149
150 pub(crate) fn read_response(&mut self) -> crate::Result<Response> {
151 let mut reader = BufReader::new(&mut self.stream);
152 let args = Self::parse_line(&mut reader, self.config.debug)?;
153 Response::from_args(args)
154 }
155
156 pub(crate) fn read_plain_response(&mut self) -> crate::Result<String> {
157 let mut reader = BufReader::new(&mut self.stream);
158 let args = Self::parse_line(&mut reader, self.config.debug)?;
159 Ok(args.join(" "))
160 }
161
162 pub(crate) fn read_list(&mut self, query: &[&str]) -> crate::Result<Vec<Response>> {
163 let mut reader = BufReader::new(&mut self.stream);
164 let args = Self::parse_line(&mut reader, self.config.debug)?;
165
166 Response::from_args(args)?.expect_begin_list(query)?;
167 let mut lines: Vec<Response> = Vec::new();
168
169 loop {
170 let args = Self::parse_line(&mut reader, self.config.debug)?;
171 let resp = Response::from_args(args)?;
172
173 match resp {
174 Response::EndList(_) => {
175 break;
176 }
177 _ => lines.push(resp),
178 }
179 }
180
181 Ok(lines)
182 }
183}