1#![cfg_attr(
2 feature = "cargo-clippy",
3 allow(collapsible_if, needless_pass_by_value)
4)]
5
6use std::ops::Not;
7use std::rc::Rc;
8use std::str::FromStr;
9
10use super::specifier::SpecifierNode;
11use super::{Options, Result, SpecifierClass, SpecifierStack, WebsocatConfiguration2};
12
13extern crate hyper;
14extern crate url;
15
16use std::net::{IpAddr, SocketAddr};
17
18use super::socks5_peer::{SocksHostAddr, SocksSocketAddr};
19
20#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
21pub enum StdioUsageStatus {
22 None,
24 WithReuser,
27 Indirectly,
29 IsItself,
31}
32
33trait ClassExt {
34 fn is_stdio(&self) -> bool;
35 fn is_reuser(&self) -> bool;
36}
37
38pub type OnWarning = Box<dyn for<'a> Fn(&'a str) -> () + 'static>;
39
40#[cfg_attr(rustfmt, rustfmt_skip)]
41impl ClassExt for Rc<dyn SpecifierClass> {
42 fn is_stdio(&self) -> bool {
43 [
44 "StdioClass",
45 "ThreadedStdioClass",
46 "ThreadedStdioSubstituteClass",
47 ].contains(&self.get_name())
48 }
49 fn is_reuser(&self) -> bool {
50 [
51 "ReuserClass",
52 "BroadcastReuserClass",
53 ].contains(&self.get_name())
54 }
55}
56
57pub trait SpecifierStackExt {
58 fn stdio_usage_status(&self) -> StdioUsageStatus;
59 fn reuser_count(&self) -> usize;
60 fn contains(&self, t: &'static str) -> bool;
61 fn is_multiconnect(&self) -> bool;
62 fn is_stream_oriented(&self) -> bool;
63 fn autotoreconn_misuse(&self) -> bool;
64 fn insert_line_class_in_proper_place(&mut self, x: Rc<dyn SpecifierClass>);
65}
66impl SpecifierStackExt for SpecifierStack {
67 fn stdio_usage_status(&self) -> StdioUsageStatus {
68 use self::StdioUsageStatus::*;
69
70 if !self.addrtype.cls.is_stdio() {
71 return None;
72 }
73
74 let mut sus: StdioUsageStatus = IsItself;
75
76 for overlay in self.overlays.iter().rev() {
77 if overlay.cls.is_reuser() {
78 sus = WithReuser;
79 } else if sus == IsItself {
80 sus = Indirectly;
81 }
82 }
83 sus
84 }
85 fn reuser_count(&self) -> usize {
86 let mut c = 0;
87 for overlay in &self.overlays {
88 if overlay.cls.is_reuser() {
89 c += 1;
90 }
91 }
92 c
93 }
94 fn contains(&self, t: &'static str) -> bool {
95 for overlay in &self.overlays {
96 if overlay.cls.get_name() == t {
97 return true;
98 }
99 }
100 self.addrtype.cls.get_name() == t
101 }
102 fn autotoreconn_misuse(&self) -> bool {
103 let mut autoreconnect_found = false;
104 for overlay in &self.overlays {
105 if overlay.cls.get_name() == "AutoReconnectClass" {
106 autoreconnect_found = true;
107 }
108 if overlay.cls.get_name() == "BroadcastReuserClass"
109 || overlay.cls.get_name() == "ReuserClass"
110 {
111 if autoreconnect_found {
112 return true;
113 }
114 }
115 }
116 false
117 }
118 fn is_multiconnect(&self) -> bool {
119 use super::ClassMulticonnectStatus::*;
120 match self.addrtype.cls.multiconnect_status() {
121 MultiConnect => (),
122 SingleConnect => return false,
123 MulticonnectnessDependsOnInnerType => unreachable!(),
124 }
125 for overlay in self.overlays.iter().rev() {
126 match overlay.cls.multiconnect_status() {
127 MultiConnect => (),
128 SingleConnect => return false,
129 MulticonnectnessDependsOnInnerType => (),
130 }
131 }
132 true
133 }
134 fn is_stream_oriented(&self) -> bool {
135 use super::ClassMessageBoundaryStatus::*;
136 let mut q = match self.addrtype.cls.message_boundary_status() {
137 StreamOriented => true,
138 MessageOriented => false,
139 MessageBoundaryStatusDependsOnInnerType => unreachable!(),
140 };
141 for overlay in self.overlays.iter().rev() {
142 match overlay.cls.message_boundary_status() {
143 StreamOriented => q = true,
144 MessageOriented => q = false,
145 MessageBoundaryStatusDependsOnInnerType => (),
146 }
147 }
148 q
149 }
150 fn insert_line_class_in_proper_place(&mut self, x: Rc<dyn SpecifierClass>) {
151 use super::ClassMessageBoundaryStatus::*;
152 let mut insert_idx = 0;
153 for overlay in &self.overlays {
154 match overlay.cls.message_boundary_status() {
155 StreamOriented => break,
156 MessageOriented => break,
157 MessageBoundaryStatusDependsOnInnerType => insert_idx += 1,
158 }
159 }
160 self.overlays.insert(insert_idx, SpecifierNode { cls: x });
161 }
162}
163
164impl WebsocatConfiguration2 {
165 pub fn inetd_mode(&self) -> bool {
166 self.contains_class("InetdClass")
167 }
168
169 #[cfg_attr(rustfmt, rustfmt_skip)]
170 #[cfg_attr(feature="cargo-clippy", allow(nonminimal_bool))]
171 pub fn websocket_used(&self) -> bool {
172 false
173 || self.contains_class("WsConnectClass")
174 || self.contains_class("WsClientClass")
175 || self.contains_class("WsClientSecureClass")
176 || self.contains_class("WsServerClass")
177 }
178
179 #[cfg_attr(rustfmt, rustfmt_skip)]
180 #[cfg_attr(feature="cargo-clippy", allow(nonminimal_bool))]
181 pub fn exec_used(&self) -> bool {
182 false
183 || self.contains_class("ExecClass")
184 || self.contains_class("CmdClass")
185 || self.contains_class("ShCClass")
186 }
187
188 pub fn contains_class(&self, x: &'static str) -> bool {
189 self.s1.contains(x) || self.s2.contains(x)
190 }
191
192 pub fn get_exec_parameter(&self) -> Option<&str> {
193 if self.s1.addrtype.cls.get_name() == "ExecClass" {
194 return Some(self.s1.addr.as_str());
195 }
196 if self.s2.addrtype.cls.get_name() == "ExecClass" {
197 return Some(self.s2.addr.as_str());
198 }
199 None
200 }
201
202 fn l_stdio(
203 &mut self,
204 multiconnect: bool,
205 reuser_has_been_inserted: &mut bool,
206 r#async: bool,
207 ) -> Result<()> {
208 use self::StdioUsageStatus::{Indirectly, IsItself, None, WithReuser};
209 match (self.s1.stdio_usage_status(), self.s2.stdio_usage_status()) {
210 (_, None) => (),
211 (None, WithReuser) => (),
212 (None, IsItself) | (None, Indirectly) => {
213 if multiconnect {
214 self.s2.overlays.insert(
215 0,
216 SpecifierNode {
217 cls: Rc::new(super::broadcast_reuse_peer::BroadcastReuserClass),
218 },
219 );
220 *reuser_has_been_inserted = true;
221 }
222 }
223 (IsItself, IsItself) => {
224 info!("Special mode, exception from usual one-stdio rule. Acting like `cat(1)`");
225 self.s2 = SpecifierStack::from_str("mirror:")?;
226 if self.opts.unidirectional ^ self.opts.unidirectional_reverse {
227 self.opts.unidirectional = false;
228 self.opts.unidirectional_reverse = false;
229 }
230 return Ok(());
231 }
232 (_, _) => {
233 Err(
234 "Too many usages of stdin/stdout. Specify it either on left or right address, \
235 not on both.",
236 )?;
237 }
238 }
239
240 #[cfg(all(unix, feature = "unix_stdio"))]
241 {
242 if r#async {
243 if self.s1.addrtype.cls.get_name() == "StdioClass" {
244 debug!("Substituting StdioClass with AsyncStdioClass at the left");
245 self.s1.addrtype = SpecifierNode {
246 cls: Rc::new(crate::stdio_peer::AsyncStdioClass),
247 };
248 }
249 if self.s2.addrtype.cls.get_name() == "StdioClass" {
250 debug!("Substituting StdioClass with AsyncStdioClass at the right");
251 self.s2.addrtype = SpecifierNode {
252 cls: Rc::new(crate::stdio_peer::AsyncStdioClass),
253 };
254 }
255 }
256 }
257
258 Ok(())
259 }
260
261 fn l_reuser(&mut self, reuser_has_been_inserted: bool) -> Result<()> {
262 if self.s1.reuser_count() + self.s2.reuser_count() > 1 {
263 if reuser_has_been_inserted {
264 error!(
265 "The reuser you specified conflicts with automatically inserted reuser based \
266 on usage of stdin/stdout in multiconnect mode."
267 );
268 }
269 Err("Too many usages of connection reuser. Please limit to only one instance.")?;
270 }
271 Ok(())
272 }
273
274 fn l_linemode(&mut self) -> Result<()> {
275 if !self.opts.no_auto_linemode && self.opts.websocket_text_mode {
276 match (self.s1.is_stream_oriented(), self.s2.is_stream_oriented()) {
277 (false, false) => {}
278 (true, true) => {}
279 (true, false) => {
280 info!("Auto-inserting the line mode");
281 self.s1.insert_line_class_in_proper_place(Rc::new(
282 super::line_peer::Line2MessageClass,
283 ));
284 self.s2.insert_line_class_in_proper_place(Rc::new(
285 super::line_peer::Message2LineClass,
286 ));
287 }
288 (false, true) => {
289 info!("Auto-inserting the line mode");
290 self.s2.insert_line_class_in_proper_place(Rc::new(
291 super::line_peer::Line2MessageClass,
292 ));
293 self.s1.insert_line_class_in_proper_place(Rc::new(
294 super::line_peer::Message2LineClass,
295 ));
296 }
297 }
298 };
299 Ok(())
300 }
301 fn l_listener_on_the_right(&mut self, on_warning: &OnWarning) -> Result<()> {
302 if !self.opts.oneshot && self.s2.is_multiconnect() && !self.s1.is_multiconnect() {
303 on_warning(
304 "You have specified a listener on the right (as the second positional argument) \
305 instead of on the left. It will only serve one connection.\nChange arguments \
306 order to enable multiple parallel connections or use --oneshot argument to make \
307 single connection explicit.",
308 );
309 }
310 Ok(())
311 }
312 fn l_reuser_for_append(&mut self, multiconnect: bool) -> Result<()> {
313 if multiconnect
314 && (self.s2.addrtype.cls.get_name() == "WriteFileClass"
315 || self.s2.addrtype.cls.get_name() == "AppendFileClass")
316 && self.s2.reuser_count() == 0
317 {
318 info!("Auto-inserting the reuser");
319 self.s2.overlays.push(SpecifierNode {
320 cls: Rc::new(super::primitive_reuse_peer::ReuserClass),
321 });
322 };
323 Ok(())
324 }
325 fn l_exec(&mut self, on_warning: &OnWarning) -> Result<()> {
326 if self.s1.addrtype.cls.get_name() == "ExecClass"
327 && self.s2.addrtype.cls.get_name() == "ExecClass"
328 {
329 Err("Can't use exec: more than one time. Replace one of them with sh-c: or cmd:.")?;
330 }
331
332 if let Some(x) = self.get_exec_parameter() {
333 if self.opts.exec_args.is_empty() && x.contains(' ') {
334 on_warning(
335 "Warning: you specified exec: without the corresponding --exec-args at the \
336 end of command line. Unlike in cmd: or sh-c:, spaces inside exec:'s direct \
337 parameter are interpreted as part of program name, not as separator.",
338 );
339 }
340 }
341 Ok(())
342 }
343 fn l_uri_staticfiles(&mut self, on_warning: &OnWarning) -> Result<()> {
344 if self.opts.restrict_uri.is_some() && !self.contains_class("WsServerClass") {
345 on_warning("--restrict-uri is meaningless without a WebSocket server");
346 }
347
348 if !self.opts.serve_static_files.is_empty() && !self.contains_class("WsServerClass") {
349 on_warning("--static-file (-F) is meaningless without a WebSocket server");
350 }
351
352 for sf in &self.opts.serve_static_files {
353 if !sf.uri.starts_with('/') {
354 on_warning(&format!(
355 "Static file's URI `{}` should begin with `/`?",
356 sf.uri
357 ));
358 }
359 if !sf.file.exists() {
360 on_warning(&format!("File {:?} does not exist", sf.file));
361 }
362 if !sf.content_type.contains('/') {
363 on_warning(&format!(
364 "Content-Type `{}` lacks `/` character",
365 sf.content_type
366 ));
367 }
368 }
369 Ok(())
370 }
371 fn l_environ(&mut self, on_warning: &OnWarning) -> Result<()> {
372 if self.opts.exec_set_env {
373 if !self.exec_used() {
374 on_warning(
375 "-e (--set-environment) is meaningless without a exec: or sh-c: or cmd: \
376 address",
377 );
378 }
379 if !self.contains_class("TcpListenClass") && !self.contains_class("WsServerClass") {
380 on_warning(
381 "-e (--set-environment) is currently meaningless without a websocket server \
382 and/or TCP listener",
383 );
384 }
385 }
386
387 if !self.opts.headers_to_env.is_empty() && !self.opts.exec_set_env {
388 on_warning("--header-to-env is meaningless without -e (--set-environment)");
389 }
390
391 Ok(())
392 }
393 fn l_closebug(&mut self, on_warning: &OnWarning) -> Result<()> {
394 if !self.opts.oneshot && self.s1.is_multiconnect() {
395 if self.s1.contains("TcpListenClass")
396 || self.s1.contains("UnixListenClass")
397 || self.s1.contains("SeqpacketListenClass")
398 {
399 if !self.opts.unidirectional
400 && (self.opts.unidirectional_reverse || !self.opts.exit_on_eof)
401 {
402 on_warning(
403 "Unfortunately, serving multiple clients without --exit-on-eof (-E) or \
404 with -U option is prone to socket leak in this websocat version",
405 );
406 }
407 }
408 }
409 Ok(())
410 }
411
412 fn l_socks5_c(
413 s: &mut SpecifierStack,
414 opts: &mut Options,
415 on_warning: &OnWarning,
416 secure: bool,
417 ) -> Result<()> {
418 let url = if secure {
419 #[cfg(not(feature = "ssl"))]
420 {
421 Err("SSL support not compiled in")?;
422 }
423 format!("wss://{}", s.addr)
424 } else {
425 format!("ws://{}", s.addr)
426 };
427
428 s.addrtype = SpecifierNode {
430 cls: Rc::new(super::net_peer::TcpConnectClass),
431 };
432
433 match opts.auto_socks5.unwrap() {
434 SocketAddr::V4(sa4) => {
435 s.addr = format!("{}:{}", sa4.ip(), sa4.port());
436 }
437 SocketAddr::V6(sa6) => {
438 s.addr = format!("[{}]:{}", sa6.ip(), sa6.port());
439 }
440 }
441
442 use self::hyper::Url;
443 use self::url::Host;
444 let u = Url::parse(&url)?;
445
446 if !u.has_host() {
447 Err("WebSocket URL has no host")?;
448 }
449
450 let port = u.port_or_known_default().unwrap_or(80);
451 let host = u.host().unwrap();
452
453 let host = match host {
454 Host::Domain(dom) => SocksHostAddr::Name(dom.to_string()),
455 Host::Ipv4(ip4) => SocksHostAddr::Ip(IpAddr::V4(ip4)),
456 Host::Ipv6(ip6) => SocksHostAddr::Ip(IpAddr::V6(ip6)),
457 };
458 if opts.socks_destination.is_none() {
459 opts.socks_destination = Some(SocksSocketAddr { host, port });
460 }
461 if secure && opts.tls_domain.is_none() {
462 opts.tls_domain = u.host_str().map(|x| x.to_string());
463 }
464
465 if opts.ws_c_uri != "ws://0.0.0.0/" {
466 on_warning(
467 "Looks like you've overridden ws-c-uri. We are overwriting it for --socks5 option.",
468 );
469 }
470
471 opts.ws_c_uri = url;
472
473 s.overlays.push(SpecifierNode {
474 cls: Rc::new(super::ws_client_peer::WsConnectClass),
475 });
476 if secure {
477 #[cfg(feature = "ssl")]
478 s.overlays.push(SpecifierNode {
479 cls: Rc::new(super::ssl_peer::TlsConnectClass),
480 });
481 }
482 s.overlays.push(SpecifierNode {
483 cls: Rc::new(super::socks5_peer::SocksProxyClass),
484 });
485
486 Ok(())
487 }
488
489 fn l_socks5(&mut self, on_warning: &OnWarning) -> Result<()> {
490 if self.opts.socks_destination.is_some()
491 ^ (self.contains_class("SocksProxyClass") || self.contains_class("SocksBindClass"))
492 {
493 on_warning(
494 "--socks5-destination option and socks5-connect: overlay should go together",
495 );
496 }
497
498 if self.opts.socks5_bind_script.is_some() ^ self.contains_class("SocksBindClass") {
499 on_warning("--socks5-bind-script option and socks5-bind: overlay should go together");
500 }
501
502 if self.opts.auto_socks5.is_some() {
503 if !((self.s1.addrtype.cls.get_name() == "WsClientClass"
504 || self.s1.addrtype.cls.get_name() == "WsClientSecureClass")
505 ^ (self.s2.addrtype.cls.get_name() == "WsClientClass"
506 || self.s2.addrtype.cls.get_name() == "WsClientSecureClass"))
507 {
508 Err(
509 "User-friendly --socks5 option supports socksifying exactly one non-raw \
510 websocket client connection. You are using two or none.",
511 )?;
512 }
513
514 if self.s1.addrtype.cls.get_name() == "WsClientClass" {
515 WebsocatConfiguration2::l_socks5_c(
516 &mut self.s1,
517 &mut self.opts,
518 on_warning,
519 false,
520 )?;
521 }
522 if self.s1.addrtype.cls.get_name() == "WsClientSecureClass" {
523 WebsocatConfiguration2::l_socks5_c(&mut self.s1, &mut self.opts, on_warning, true)?;
524 }
525 if self.s2.addrtype.cls.get_name() == "WsClientClass" {
526 WebsocatConfiguration2::l_socks5_c(
527 &mut self.s2,
528 &mut self.opts,
529 on_warning,
530 false,
531 )?;
532 }
533 if self.s2.addrtype.cls.get_name() == "WsClientSecureClass" {
534 WebsocatConfiguration2::l_socks5_c(&mut self.s2, &mut self.opts, on_warning, true)?;
535 }
536 }
537 Ok(())
538 }
539
540 #[cfg(feature = "ssl")]
541 fn l_ssl(&mut self, _on_warning: &OnWarning) -> Result<()> {
542 if self.opts.pkcs12_der.is_some() && !self.contains_class("TlsAcceptClass") {
543 Err("--pkcs12-der makes no sense without an TLS connections acceptor")?;
544 }
545 if self.opts.pkcs12_der.is_none() && self.contains_class("TlsAcceptClass") {
546 Err(
547 "You need to specify server key and certificate using the --pkcs12-der option to \
548 use the TLS connections acceptor",
549 )?;
550 }
551 if self.opts.client_pkcs12_der.is_some()
552 && !self.contains_class("WsClientSecureClass")
553 && !self.contains_class("TlsConnectClass")
554 {
555 Err("--client-pkcs12-der makes no sense without wss:// or ssl: connectors")?;
556 }
557 #[cfg(target_os = "macos")]
558 {
559 if (self.opts.pkcs12_der.is_some() && self.opts.pkcs12_passwd.is_none())
560 || (self.opts.client_pkcs12_der.is_some()
561 && self.opts.client_pkcs12_passwd.is_none())
562 {
563 _on_warning("PKCS12 archives without password may be unsupported on Mac");
564
565 for x in ::std::env::args() {
566 if x.contains("test.pkcs12") {
567 _on_warning(
568 "If you want a pre-made test certificate, use other file: \
569 `--pkcs12-der 1234.pkcs12 --pkcs12-passwd 1234`",
570 );
571 break;
572 }
573 }
574 }
575 }
576 Ok(())
577 }
578
579 fn l_ping(&mut self, _on_warning: &OnWarning) -> Result<()> {
580 if self.opts.ws_ping_interval.is_some() || self.opts.ws_ping_timeout.is_some() {
581 if !self.websocket_used() {
582 _on_warning(
583 "--ping-interval or --ping-timeout options are not effective if no WebSocket \
584 usage is specified",
585 )
586 }
587 }
588 if self.opts.ws_ping_timeout.is_some() && self.opts.ws_ping_interval.is_none() {
589 _on_warning(
590 "--ping-timeout specified without --ping-interval. This will probably lead to \
591 unconditional disconnection after that interval.",
592 )
593 }
594 if let (Some(t), Some(i)) = (self.opts.ws_ping_timeout, self.opts.ws_ping_interval) {
595 if t <= i {
596 _on_warning(
597 "--ping-timeout's value is not more than --ping-interval. Expect spurious \
598 disconnections.",
599 );
600 }
601 }
602 if self.opts.ws_ping_timeout.is_some() {
603 if self.opts.unidirectional_reverse || self.opts.exit_on_eof {
604 } else {
606 _on_warning("--ping-interval is currently not very effective without -E or -U")
607 }
608 }
609 if self.opts.print_ping_rtts && self.opts.ws_ping_interval.is_none() {
610 _on_warning("--print-ping-rtts is not effective without --ping-interval");
611 }
612 Ok(())
613 }
614
615 fn l_proto(&mut self, _on_warning: &OnWarning) -> Result<()> {
616 if self.opts.websocket_protocol.is_some() {
617 if false
618 || self.contains_class("WsConnectClass")
619 || self.contains_class("WsClientClass")
620 || self.contains_class("WsClientSecureClass")
621 {
622 } else {
624 if self.contains_class("WsServerClass") {
625 _on_warning("--protocol option is unused. Maybe you want --server-protocol?")
626 } else {
627 _on_warning("--protocol option is unused.")
628 }
629 }
630 }
631 if self.opts.websocket_reply_protocol.is_some() {
632 if !self.contains_class("WsServerClass") {
633 _on_warning("--server-protocol option is unused.")
634 }
635 }
636 Ok(())
637 }
638
639 fn l_eeof_unidir(&mut self, _on_warning: &OnWarning) -> Result<()> {
640 if self.opts.exit_on_eof {
641 if self.opts.unidirectional || self.opts.unidirectional_reverse {
642 _on_warning(
643 "--exit-on-eof and --unidirectional[-reverse] options are now useless together",
644 );
645 _on_warning(
646 "You may want to remove --exit-on-eof. If you are happy with what happens, \
647 consider `-uU` instead of `-uE`.",
648 );
649 }
650 }
651 Ok(())
652 }
653
654 fn l_udp(&mut self, _on_warning: &OnWarning) -> Result<()> {
655 if self.opts.udp_join_multicast_addr.is_empty().not() {
656 if self.opts.udp_broadcast {
657 _on_warning(
658 "Both --udp-broadcast and a multicast address is set. This is strange.",
659 );
660 }
661 let ifs = self.opts.udp_join_multicast_iface_v4.len()
662 + self.opts.udp_join_multicast_iface_v6.len();
663 if ifs != 0 {
664 let mut v4_multicasts = 0;
665 let mut v6_multicasts = 0;
666 for i in &self.opts.udp_join_multicast_addr {
667 match i {
668 std::net::IpAddr::V4(_) => v4_multicasts += 1,
669 std::net::IpAddr::V6(_) => v6_multicasts += 1,
670 }
671 }
672 if v4_multicasts != self.opts.udp_join_multicast_iface_v4.len() {
673 return Err(
674 "--udp-multicast-iface-v4 option mush be specified the same number of \
675 times as IPv4 addresses for --udp-multicast (alternatively \
676 --udp-multicast-iface-* options should be not specified at all)",
677 )?;
678 }
679 if v6_multicasts != self.opts.udp_join_multicast_iface_v6.len() {
680 return Err(
681 "--udp-multicast-iface-v6 option mush be specified the same number of \
682 times as IPv6 addresses for --udp-multicast (alternatively \
683 --udp-multicast-iface-* options should be not specified at all)",
684 )?;
685 }
686 }
687 } else {
688 if self.opts.udp_multicast_loop {
689 return Err("--udp-multicast-loop is not applicable without --udp-multicast")?;
690 }
691 }
692 Ok(())
693 }
694 fn l_crypto(&mut self, _on_warning: &OnWarning) -> Result<()> {
695 #[cfg(feature = "crypto_peer")]
696 if self.opts.crypto_key.is_some() {
697 if !self.contains_class("CryptoClass") {
698 _on_warning("--crypto-key option is meaningless without a `crypto:` overlay");
699 }
700 }
701 Ok(())
702 }
703 fn l_prometheus(&mut self, _on_warning: &OnWarning) -> Result<()> {
704 #[cfg(feature = "prometheus_peer")]
705 if self.opts.prometheus.is_some() {
706 if !self.contains_class("PrometheusClass") {
707 self.s2.overlays.insert(
708 0,
709 SpecifierNode {
710 cls: Rc::new(crate::prometheus_peer::PrometheusClass),
711 },
712 );
713 }
714 } else {
715 if self.contains_class("PrometheusClass") {
716 _on_warning(
717 "Using `prometheus:` overlay without `--prometheus` option is meaningless",
718 );
719 }
720 }
721 Ok(())
722 }
723 fn l_sizelimits(&mut self, _on_warning: &OnWarning) -> Result<()> {
724 if self.opts.max_ws_message_length < self.opts.max_ws_frame_length {
725 _on_warning(
726 "Lowering --max-ws-message-length without also lowering --max-ws-frame-length may \
727 be meaningless, as the former only affects whether to begin accept a new frame \
728 or not, given accumulated message size. Succesfully accepted frames within the \
729 frame size limit may exceed the message size.",
730 )
731 }
732 Ok(())
733 }
734 fn l_compress(&mut self, _on_warning: &OnWarning) -> Result<()> {
735 let mut cn = 0;
736 let mut un = 0;
737 if self.opts.compress_deflate {
738 cn += 1
739 }
740 if self.opts.compress_gzip {
741 cn += 1
742 }
743 if self.opts.compress_zlib {
744 cn += 1
745 }
746 if self.opts.uncompress_deflate {
747 un += 1
748 }
749 if self.opts.uncompress_gzip {
750 un += 1
751 }
752 if self.opts.uncompress_zlib {
753 un += 1
754 }
755 if cn > 1 {
756 return Err("Multiple --compress-* options specifed")?;
757 }
758 if un > 1 {
759 return Err("Multiple --uncompress-* options specifed")?;
760 }
761
762 #[cfg(not(feature = "compression"))]
763 {
764 if cn > 0 || un > 0 {
765 return Err("Compression support is not selected during Websocat compilation")?;
766 }
767 }
768 Ok(())
769 }
770 #[cfg(feature = "native_plugins")]
771 fn l_plugins(&mut self, _on_warning: &OnWarning) -> Result<()> {
772 if self.contains_class("NativeTransformAClass") && self.opts.native_transform_a.is_none() {
773 return Err("--native-plugin-a must be specified to use `native_plugin_transform_a:`")?;
774 }
775 if self.contains_class("NativeTransformBClass") && self.opts.native_transform_b.is_none() {
776 return Err("--native-plugin-b must be specified to use `native_plugin_transform_b:`")?;
777 }
778 if self.contains_class("NativeTransformCClass") && self.opts.native_transform_c.is_none() {
779 return Err("--native-plugin-c must be specified to use `native_plugin_transform_c:`")?;
780 }
781 if self.contains_class("NativeTransformDClass") && self.opts.native_transform_d.is_none() {
782 return Err("--native-plugin-d must be specified to use `native_plugin_transform_d:`")?;
783 }
784 Ok(())
785 }
786 #[cfg(not(feature = "native_plugins"))]
787 fn l_plugins(&mut self, _on_warning: &OnWarning) -> Result<()> {
788 Ok(())
789 }
790
791 #[cfg(feature = "wasm_plugins")]
792 fn l_wasm(&mut self, _on_warning: &OnWarning) -> Result<()> {
793 if self.contains_class("WasmTransformAClass") && self.opts.wasm_transform_a.is_none() {
794 return Err("--wasm-plugin-a must be specified to use `wasm_plugin_transform_a:`")?;
795 }
796 if self.contains_class("WasmTransformBClass") && self.opts.wasm_transform_b.is_none() {
797 return Err("--wasm-plugin-b must be specified to use `wasm_plugin_transform_b:`")?;
798 }
799 if self.contains_class("WasmTransformCClass") && self.opts.wasm_transform_c.is_none() {
800 return Err("--wasm-plugin-c must be specified to use `wasm_plugin_transform_c:`")?;
801 }
802 if self.contains_class("WasmTransformDClass") && self.opts.wasm_transform_d.is_none() {
803 return Err("--wasm-plugin-d must be specified to use `wasm_plugin_transform_d:`")?;
804 }
805 Ok(())
806 }
807 #[cfg(not(feature = "wasm_plugins"))]
808 fn l_wasm(&mut self, _on_warning: &OnWarning) -> Result<()> {
809 Ok(())
810 }
811
812 fn l_autoreconn_reuse(&mut self, _on_warning: &OnWarning) -> Result<()> {
813 if self.s1.autotoreconn_misuse() || self.s2.autotoreconn_misuse() {
814 _on_warning(
815 "Warning: `autoreconnect:reuse:` is a bad overlay combination. Maybe you want \
816 `reuse:autoreconnect:",
817 );
818 }
819 Ok(())
820 }
821
822 pub fn lint_and_fixup(&mut self, on_warning: OnWarning) -> Result<()> {
823 let multiconnect = !self.opts.oneshot && self.s1.is_multiconnect();
824 let mut reuser_has_been_inserted = false;
825
826 self.l_prometheus(&on_warning)?;
827 self.l_stdio(
828 multiconnect,
829 &mut reuser_has_been_inserted,
830 self.opts.asyncstdio,
831 )?;
832 self.l_reuser(reuser_has_been_inserted)?;
833 self.l_linemode()?;
834 self.l_listener_on_the_right(&on_warning)?;
835 self.l_reuser_for_append(multiconnect)?;
836 self.l_exec(&on_warning)?;
837 self.l_uri_staticfiles(&on_warning)?;
838 self.l_environ(&on_warning)?;
839 self.l_closebug(&on_warning)?;
840 self.l_socks5(&on_warning)?;
841 #[cfg(feature = "ssl")]
842 self.l_ssl(&on_warning)?;
843 self.l_ping(&on_warning)?;
844 self.l_proto(&on_warning)?;
845 self.l_eeof_unidir(&on_warning)?;
846 self.l_udp(&on_warning)?;
847 self.l_crypto(&on_warning)?;
848 self.l_sizelimits(&on_warning)?;
849 self.l_compress(&on_warning)?;
850 self.l_plugins(&on_warning)?;
851 self.l_wasm(&on_warning)?;
852 self.l_autoreconn_reuse(&on_warning)?;
853
854 Ok(())
857 }
858}