1mod codec;
2pub mod error;
3
4use encoding::DecoderTrap;
5use encoding::{all::GB18030, all::GBK, Encoding};
6use futures::stream::StreamExt;
7use regex::bytes::Regex;
8use tokio::{
9 io::AsyncWriteExt,
10 net::TcpStream,
11 time::{self, Duration},
12};
13use tokio_util::codec::FramedRead;
14
15use crate::codec::{Item, TelnetCodec};
16use crate::error::TelnetError;
17
18#[derive(Debug, Default)]
19pub struct TelnetBuilder {
20 prompts: Vec<String>,
21 username_prompt: String,
22 password_prompt: String,
23 connect_timeout: Duration,
24 timeout: Duration,
25}
26
27impl TelnetBuilder {
28 pub fn prompt<T: ToString>(mut self, prompt: T) -> TelnetBuilder {
30 self.prompts = vec![prompt.to_string()];
31 self
32 }
33
34 pub fn prompts<T: ToString>(mut self, prompts: &[T]) -> TelnetBuilder {
37 self.prompts = prompts.iter().map(|p| p.to_string()).collect();
38 self
39 }
40
41 pub fn login_prompt(mut self, user_prompt: &str, pass_prompt: &str) -> TelnetBuilder {
43 self.username_prompt = user_prompt.to_string();
44 self.password_prompt = pass_prompt.to_string();
45 self
46 }
47
48 pub fn connect_timeout(mut self, connect_timeout: Duration) -> TelnetBuilder {
50 self.connect_timeout = connect_timeout;
51 self
52 }
53
54 pub fn timeout(mut self, timeout: Duration) -> TelnetBuilder {
56 self.timeout = timeout;
57 self
58 }
59
60 pub async fn connect(self, addr: &str) -> Result<Telnet, TelnetError> {
62 let clear = Clear::new()?;
63 match time::timeout(self.connect_timeout, TcpStream::connect(addr)).await {
64 Ok(res) => Ok(Telnet {
65 content: vec![],
66 stream: res?,
67 timeout: self.timeout,
68 prompts: self.prompts,
69 username_prompt: self.username_prompt,
70 password_prompt: self.password_prompt,
71 clear,
72 }),
73 Err(_) => Err(TelnetError::Timeout(format!(
74 "Connect remote addr({})",
75 addr
76 ))),
77 }
78 }
79}
80
81pub struct Telnet {
82 timeout: Duration,
83 content: Vec<String>,
84 stream: TcpStream,
85 prompts: Vec<String>,
86 username_prompt: String,
87 password_prompt: String,
88 clear: Clear,
89}
90
91impl Telnet {
92 pub fn builder() -> TelnetBuilder {
94 TelnetBuilder::default()
95 }
96 fn format_enter_str(s: &str) -> String {
98 if !s.ends_with('\n') {
99 format!("{}\n", s)
100 } else {
101 s.to_string()
102 }
103 }
104
105 pub async fn login(&mut self, username: &str, password: &str) -> Result<(), TelnetError> {
122 let user = Telnet::format_enter_str(username);
123 let pass = Telnet::format_enter_str(password);
124
125 let mut auth_failed = false;
127
128 let (read, mut write) = self.stream.split();
129 let mut telnet = FramedRead::new(read, TelnetCodec::default());
130
131 loop {
132 match time::timeout(self.timeout, telnet.next()).await {
133 Ok(res) => {
134 match res {
135 Some(res) => {
136 match res? {
137 Item::Do(i) | Item::Dont(i) => {
138 if i == 0x1f {
140 write
141 .write_all(&[
142 0xff, 0xfb, 0x1f, 0xff, 0xfa, 0x1f, 0x00, 0xfc,
143 0x00, 0x1b, 0xff, 0xf0,
144 ])
145 .await?;
146 } else {
147 write.write_all(&[0xff, 0xfc, i]).await?;
148 }
149 }
150 Item::Will(i) | Item::Wont(i) => {
151 write.write_all(&[0xff, 0xfe, i]).await?;
152 }
153 Item::Line(line) => {
154 let line = self.clear.color(&line);
155 if line.ends_with(self.username_prompt.as_bytes()) {
156 if auth_failed {
157 return Err(TelnetError::AuthenticationFailed);
158 }
159 write.write_all(user.as_bytes()).await?;
160 } else if line.ends_with(self.password_prompt.as_bytes()) {
161 write.write_all(pass.as_bytes()).await?;
162 auth_failed = true;
163 } else if self
164 .prompts
165 .iter()
166 .filter(|p| line.ends_with(p.as_bytes()))
167 .count()
168 != 0
169 {
170 return Ok(());
171 }
172 }
173 item => return Err(TelnetError::UnknownIAC(format!("{:?}", item))),
174 }
175 }
176 None => return Err(TelnetError::NoMoreData),
177 };
178 }
179 Err(_) => return Err(TelnetError::Timeout("login".to_string())),
180 }
181 }
182 }
183
184 pub async fn execute(&mut self, cmd: &str) -> Result<String, TelnetError> {
193 let command = Telnet::format_enter_str(cmd);
194 let mut incomplete_line: Vec<u8> = vec![];
195 let mut line_feed_cnt = command.lines().count() as isize;
196 let mut real_output = false;
197
198 let (read, mut write) = self.stream.split();
199 match time::timeout(self.timeout, write.write(command.as_bytes())).await {
200 Ok(res) => res?,
201 Err(_) => return Err(TelnetError::Timeout("write cmd".to_string())),
202 };
203 let mut telnet = FramedRead::new(read, TelnetCodec::default());
204
205 loop {
206 match time::timeout(self.timeout, telnet.next()).await {
207 Ok(res) => match res {
208 Some(item) => {
209 if let Item::Line(line) = item? {
210 let mut line = self.clear.color(&line);
211
212 if self
214 .prompts
215 .iter()
216 .filter(|p| line.ends_with(p.as_bytes()))
217 .count()
218 != 0
219 {
220 break;
221 }
222 if line.ends_with(&[10]) && line_feed_cnt > 0 {
224 line_feed_cnt -= 1;
225 if line_feed_cnt == 0 {
226 real_output = true;
227 continue;
228 }
229 }
230
231 if !real_output {
232 continue;
233 }
234
235 if !line.ends_with(&[10]) || !incomplete_line.is_empty() {
236 incomplete_line.append(&mut line);
237 } else {
238 self.content.push(decode(&line)?);
239 continue;
240 }
241 if self
243 .prompts
244 .iter()
245 .filter(|p| incomplete_line.ends_with(p.as_bytes()))
246 .count()
247 != 0
248 {
249 break;
250 }
251 if incomplete_line.ends_with(&[10]) {
252 self.content.push(decode(&incomplete_line)?);
253 incomplete_line.clear();
254 }
255 }
256 }
257 None => return Err(TelnetError::NoMoreData),
258 },
259 Err(_) => return Err(TelnetError::Timeout("read next framed".to_string())),
260 }
261 }
262 let result = self.content.join("");
263 self.content.clear();
264 Ok(result)
265 }
266
267 pub async fn normal_execute(&mut self, cmd: &str) -> Result<String, TelnetError> {
280 let command = Telnet::format_enter_str(cmd);
281 let mut incomplete_line: Vec<u8> = vec![];
282
283 let (read, mut write) = self.stream.split();
284 match time::timeout(self.timeout, write.write(command.as_bytes())).await {
285 Ok(res) => res?,
286 Err(_) => return Err(TelnetError::Timeout("write cmd".to_string())),
287 };
288 let mut telnet = FramedRead::new(read, TelnetCodec::default());
289
290 loop {
291 match time::timeout(self.timeout, telnet.next()).await {
292 Ok(res) => match res {
293 Some(item) => {
294 if let Item::Line(line) = item? {
295 let mut line = self.clear.color(&line);
296 if self
297 .prompts
298 .iter()
299 .filter(|p| line.ends_with(p.as_bytes()))
300 .count()
301 != 0
302 {
303 break;
304 }
305
306 if !line.ends_with(&[10]) || !incomplete_line.is_empty() {
307 incomplete_line.append(&mut line);
308 } else {
309 self.content.push(decode(&line)?);
310 continue;
311 }
312 if self
314 .prompts
315 .iter()
316 .filter(|p| incomplete_line.ends_with(p.as_bytes()))
317 .count()
318 != 0
319 {
320 break;
321 }
322 if incomplete_line.ends_with(&[10]) {
323 self.content.push(decode(&incomplete_line)?);
324 incomplete_line.clear();
325 }
326 }
327 }
328 None => return Err(TelnetError::NoMoreData),
329 },
330 Err(_) => return Err(TelnetError::Timeout("read next framed".to_string())),
331 }
332 }
333 let result = self.content.join("");
334 self.content.clear();
335 Ok(result)
336 }
337}
338
339fn decode(line: &[u8]) -> Result<String, TelnetError> {
340 match String::from_utf8(line.to_vec()) {
341 Ok(result) => Ok(result),
342 Err(e) => {
343 if let Ok(result) = GBK.decode(line, DecoderTrap::Strict) {
344 return Ok(result);
345 }
346
347 if let Ok(result) = GB18030.decode(line, DecoderTrap::Strict) {
348 return Ok(result);
349 }
350 Err(TelnetError::ParseError(e))
351 }
352 }
353}
354
355struct Clear {
356 color_re: Regex,
357}
358
359impl Clear {
360 pub fn new() -> Result<Self, TelnetError> {
361 let color_re = Regex::new(r"\[\d{2,3}m")?;
362 Ok(Self { color_re })
363 }
364
365 pub fn color(&self, content: &[u8]) -> Vec<u8> {
366 self.color_re
367 .replace_all(content, &[] as &[u8])
368 .into_owned()
369 }
370}