feature: add TLS as feature and support multiples TLS backends (currently native-tls and rustls)
This commit is contained in:
parent
751c56e85b
commit
67e61e0606
16 changed files with 355 additions and 179 deletions
|
@ -36,6 +36,9 @@ jobs:
|
||||||
# nochanlists on w/toml
|
# nochanlists on w/toml
|
||||||
- <<: *test
|
- <<: *test
|
||||||
env: FEATURES=nochanlists toml_config
|
env: FEATURES=nochanlists toml_config
|
||||||
|
# tls-rust
|
||||||
|
- <<: *test
|
||||||
|
env: FEATURES=tls-rust
|
||||||
|
|
||||||
# Beta
|
# Beta
|
||||||
- <<: *test
|
- <<: *test
|
||||||
|
|
89
Cargo.toml
89
Cargo.toml
|
@ -11,58 +11,81 @@ repository = "https://github.com/aatxe/irc"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
travis-ci = { repository = "aatxe/irc" }
|
travis-ci = { repository = "aatxe/irc" }
|
||||||
is-it-maintained-issue-resolution = { repository = "aatxe/irc" }
|
is-it-maintained-issue-resolution = { repository = "aatxe/irc" }
|
||||||
is-it-maintained-open-issues = { repository = "aatxe/irc" }
|
is-it-maintained-open-issues = { repository = "aatxe/irc" }
|
||||||
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [ "./", "irc-proto" ]
|
members = [ "./", "irc-proto/" ]
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["ctcp", "toml_config"]
|
default = ["ctcp", "tls-native", "toml_config"]
|
||||||
ctcp = []
|
ctcp = []
|
||||||
nochanlists = []
|
nochanlists = []
|
||||||
json_config = ["serde", "serde_derive", "serde_json"]
|
|
||||||
toml_config = ["serde", "serde_derive", "toml"]
|
|
||||||
yaml_config = ["serde", "serde_derive", "serde_yaml"]
|
|
||||||
proxy = ["tokio-socks"]
|
|
||||||
|
|
||||||
|
json_config = ["serde", "serde/derive", "serde_derive", "serde_json"]
|
||||||
|
toml_config = ["serde", "serde/derive", "serde_derive", "toml"]
|
||||||
|
yaml_config = ["serde", "serde/derive", "serde_derive", "serde_yaml"]
|
||||||
# Temporary transitionary features
|
# Temporary transitionary features
|
||||||
json = ["json_config"]
|
json = ["json_config"]
|
||||||
yaml = ["yaml_config"]
|
yaml = ["yaml_config"]
|
||||||
|
|
||||||
|
proxy = ["tokio-socks"]
|
||||||
|
|
||||||
|
tls-native = ["native-tls", "tokio-tls"]
|
||||||
|
tls-rust = ["tokio-rustls", "webpki-roots"]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "1.0.2"
|
bufstream = "0.1.0"
|
||||||
bufstream = "0.1"
|
bytes = "0.5.0"
|
||||||
bytes = "0.5"
|
chrono = "0.4.0"
|
||||||
chrono = "0.4"
|
encoding = "0.2.0"
|
||||||
encoding = "0.2"
|
futures-channel = "0.3.0"
|
||||||
irc-proto = { version = "*", path = "irc-proto" }
|
futures-util = { version = "0.3.0", features = ["sink"] }
|
||||||
log = "0.4"
|
irc-proto = { path = "irc-proto" }
|
||||||
native-tls = "0.2"
|
log = "0.4.0"
|
||||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
|
||||||
serde_derive = { version = "1.0", optional = true }
|
|
||||||
tokio = { version = "0.2.4", features = ["time", "net", "stream", "macros"] }
|
|
||||||
tokio-util = { version = "0.3.0", features = ["codec"] }
|
|
||||||
tokio-socks = { version = "0.2.0", optional = true }
|
|
||||||
tokio-tls = "0.3.0"
|
|
||||||
serde_json = { version = "1.0", optional = true }
|
|
||||||
serde_yaml = { version = "0.8", optional = true }
|
|
||||||
toml = { version = "0.5", optional = true }
|
|
||||||
pin-utils = "0.1.0-alpha.4"
|
|
||||||
parking_lot = "0.10.0"
|
parking_lot = "0.10.0"
|
||||||
futures-channel = "0.3.1"
|
pin-utils = "0.1.0-alpha.4"
|
||||||
futures-util = { version = "0.3.1", features = ["sink"] }
|
thiserror = "1.0.0"
|
||||||
|
tokio = { version = "0.2.0", features = ["macros", "net", "stream", "time"] }
|
||||||
|
tokio-util = { version = "0.3.0", features = ["codec"] }
|
||||||
|
|
||||||
|
# Feature - Config
|
||||||
|
serde = { version = "1.0.0", optional = true }
|
||||||
|
serde_derive = { version = "1.0.0", optional = true }
|
||||||
|
serde_json = { version = "1.0.0", optional = true }
|
||||||
|
serde_yaml = { version = "0.8.0", optional = true }
|
||||||
|
toml = { version = "0.5.0", optional = true }
|
||||||
|
|
||||||
|
# Feature - Proxy
|
||||||
|
tokio-socks = { version = "0.2.0", optional = true }
|
||||||
|
|
||||||
|
# Feature - TLS
|
||||||
|
native-tls = { version = "0.2.0", optional = true }
|
||||||
|
tokio-rustls = { version = "0.13.0", optional = true }
|
||||||
|
tokio-tls = { version = "0.3.0", optional = true }
|
||||||
|
webpki-roots = { version = "0.19.0", optional = true }
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
futures = "0.3.1"
|
anyhow = "1.0.0"
|
||||||
anyhow = "1.0.13"
|
args = "2.0.0"
|
||||||
args = "2.0"
|
env_logger = "0.7.0"
|
||||||
getopts = "0.2"
|
futures = "0.3.0"
|
||||||
env_logger = "0.7"
|
getopts = "0.2.0"
|
||||||
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "proxy"
|
name = "simple_proxy"
|
||||||
path = "examples/proxy.rs"
|
path = "examples/simple_proxy.rs"
|
||||||
required-features = ["proxy"]
|
required-features = ["proxy"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "simple_plaintext"
|
||||||
|
path = "examples/simple_plaintext.rs"
|
||||||
|
required-features = ["tls-native"]
|
||||||
|
|
|
@ -109,7 +109,7 @@ proxy_server = "127.0.0.1"
|
||||||
proxy_port = "1080"
|
proxy_port = "1080"
|
||||||
proxy_username = ""
|
proxy_username = ""
|
||||||
proxy_password = ""
|
proxy_password = ""
|
||||||
use_ssl = true
|
use_tls = true
|
||||||
cert_path = "cert.der"
|
cert_path = "cert.der"
|
||||||
client_cert_path = "client.der"
|
client_cert_path = "client.der"
|
||||||
client_cert_pass = "password"
|
client_cert_pass = "password"
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
extern crate irc;
|
|
||||||
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use irc::{client::prelude::*, error};
|
use irc::{client::prelude::*, error};
|
||||||
|
|
||||||
|
@ -9,21 +7,21 @@ async fn main() -> irc::error::Result<()> {
|
||||||
|
|
||||||
let cfg1 = Config {
|
let cfg1 = Config {
|
||||||
nickname: Some("pickles".to_owned()),
|
nickname: Some("pickles".to_owned()),
|
||||||
server: Some("irc.mozilla.org".to_owned()),
|
server: Some("irc.pdgn.co".to_owned()),
|
||||||
channels: vec!["#rust-spam".to_owned()],
|
channels: vec!["#rust-spam".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let cfg2 = Config {
|
let cfg2 = Config {
|
||||||
nickname: Some("bananas".to_owned()),
|
nickname: Some("bananas".to_owned()),
|
||||||
server: Some("irc.mozilla.org".to_owned()),
|
server: Some("irc.pdgn.co".to_owned()),
|
||||||
channels: vec!["#rust-spam".to_owned()],
|
channels: vec!["#rust-spam".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let configs = vec![cfg1, cfg2];
|
let configs = vec![cfg1, cfg2];
|
||||||
let mut senders = Vec::new();
|
|
||||||
let mut streams = Vec::new();
|
let mut streams = Vec::new();
|
||||||
|
let mut senders = Vec::new();
|
||||||
|
|
||||||
for config in configs {
|
for config in configs {
|
||||||
// Immediate errors like failure to resolve the server's domain or to establish any connection will
|
// Immediate errors like failure to resolve the server's domain or to establish any connection will
|
||||||
|
@ -31,8 +29,8 @@ async fn main() -> irc::error::Result<()> {
|
||||||
let mut client = Client::from_config(config).await?;
|
let mut client = Client::from_config(config).await?;
|
||||||
client.identify()?;
|
client.identify()?;
|
||||||
|
|
||||||
senders.push(client.sender());
|
|
||||||
streams.push(client.stream()?);
|
streams.push(client.stream()?);
|
||||||
|
senders.push(client.sender());
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -45,7 +43,7 @@ async fn main() -> irc::error::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_msg(sender: &Sender, message: Message) -> error::Result<()> {
|
fn process_msg(sender: &Sender, message: Message) -> error::Result<()> {
|
||||||
// print!("{}", message);
|
print!("{}", message);
|
||||||
|
|
||||||
match message.command {
|
match message.command {
|
||||||
Command::PRIVMSG(ref target, ref msg) => {
|
Command::PRIVMSG(ref target, ref msg) => {
|
||||||
|
|
|
@ -4,9 +4,8 @@ use irc::client::prelude::*;
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> irc::error::Result<()> {
|
async fn main() -> irc::error::Result<()> {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
nickname: Some("repeater".to_owned()),
|
nickname: Some("pickles".to_owned()),
|
||||||
alt_nicks: vec!["blaster".to_owned(), "smg".to_owned()],
|
server: Some("irc.pdgn.co".to_owned()),
|
||||||
server: Some("irc.mozilla.org".to_owned()),
|
|
||||||
channels: vec!["#rust-spam".to_owned()],
|
channels: vec!["#rust-spam".to_owned()],
|
||||||
burst_window_length: Some(4),
|
burst_window_length: Some(4),
|
||||||
max_messages_in_burst: Some(4),
|
max_messages_in_burst: Some(4),
|
||||||
|
@ -17,6 +16,7 @@ async fn main() -> irc::error::Result<()> {
|
||||||
client.identify()?;
|
client.identify()?;
|
||||||
|
|
||||||
let mut stream = client.stream()?;
|
let mut stream = client.stream()?;
|
||||||
|
let sender = client.sender();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let message = stream.select_next_some().await?;
|
let message = stream.select_next_some().await?;
|
||||||
|
@ -28,7 +28,7 @@ async fn main() -> irc::error::Result<()> {
|
||||||
let n = tokens[0].len() + tokens[1].len() + 2;
|
let n = tokens[0].len() + tokens[1].len() + 2;
|
||||||
if let Ok(count) = tokens[1].parse::<u8>() {
|
if let Ok(count) = tokens[1].parse::<u8>() {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
client.send_privmsg(
|
sender.send_privmsg(
|
||||||
message.response_target().unwrap_or(target),
|
message.response_target().unwrap_or(target),
|
||||||
&msg[n..],
|
&msg[n..],
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -5,8 +5,7 @@ use irc::client::prelude::*;
|
||||||
async fn main() -> irc::error::Result<()> {
|
async fn main() -> irc::error::Result<()> {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
nickname: Some("pickles".to_owned()),
|
nickname: Some("pickles".to_owned()),
|
||||||
alt_nicks: vec!["bananas".to_owned(), "apples".to_owned()],
|
server: Some("irc.pdgn.co".to_owned()),
|
||||||
server: Some("irc.mozilla.org".to_owned()),
|
|
||||||
channels: vec!["#rust-spam".to_owned()],
|
channels: vec!["#rust-spam".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
@ -15,14 +14,20 @@ async fn main() -> irc::error::Result<()> {
|
||||||
client.identify()?;
|
client.identify()?;
|
||||||
|
|
||||||
let mut stream = client.stream()?;
|
let mut stream = client.stream()?;
|
||||||
|
let sender = client.sender();
|
||||||
|
|
||||||
loop {
|
while let Some(message) = stream.next().await.transpose()? {
|
||||||
let message = stream.select_next_some().await?;
|
print!("{}", message);
|
||||||
|
|
||||||
if let Command::PRIVMSG(ref target, ref msg) = message.command {
|
match message.command {
|
||||||
if msg.contains("pickles") {
|
Command::PRIVMSG(ref target, ref msg) => {
|
||||||
client.send_privmsg(target, "Hi!").unwrap();
|
if msg.contains(client.current_nickname()) {
|
||||||
|
sender.send_privmsg(target, "Hi!")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -5,23 +5,30 @@ use irc::client::prelude::*;
|
||||||
async fn main() -> irc::error::Result<()> {
|
async fn main() -> irc::error::Result<()> {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
nickname: Some("pickles".to_owned()),
|
nickname: Some("pickles".to_owned()),
|
||||||
server: Some("irc.mozilla.org".to_owned()),
|
server: Some("irc.pdgn.co".to_owned()),
|
||||||
channels: vec!["#rust-spam".to_owned()],
|
channels: vec!["#rust-spam".to_owned()],
|
||||||
use_ssl: Some(false),
|
use_tls: Some(false),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut client = Client::from_config(config).await?;
|
let mut client = Client::from_config(config).await?;
|
||||||
|
client.identify()?;
|
||||||
|
|
||||||
let mut stream = client.stream()?;
|
let mut stream = client.stream()?;
|
||||||
let sender = client.sender();
|
let sender = client.sender();
|
||||||
|
|
||||||
loop {
|
while let Some(message) = stream.next().await.transpose()? {
|
||||||
let message = stream.select_next_some().await?;
|
print!("{}", message);
|
||||||
|
|
||||||
if let Command::PRIVMSG(ref target, ref msg) = message.command {
|
match message.command {
|
||||||
if msg.contains("pickles") {
|
Command::PRIVMSG(ref target, ref msg) => {
|
||||||
|
if msg.contains(client.current_nickname()) {
|
||||||
sender.send_privmsg(target, "Hi!")?;
|
sender.send_privmsg(target, "Hi!")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use irc::client::data::ProxyType;
|
|
||||||
use irc::client::prelude::*;
|
use irc::client::prelude::*;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> irc::error::Result<()> {
|
async fn main() -> irc::error::Result<()> {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
nickname: Some("rust-irc-bot".to_owned()),
|
nickname: Some("pickles".to_owned()),
|
||||||
alt_nicks: vec!["bananas".to_owned(), "apples".to_owned()],
|
server: Some("irc.pdgn.co".to_owned()),
|
||||||
server: Some("irc.oftc.net".to_owned()),
|
|
||||||
channels: vec!["#rust-spam".to_owned()],
|
channels: vec!["#rust-spam".to_owned()],
|
||||||
proxy_type: Some(ProxyType::Socks5),
|
proxy_type: Some(ProxyType::Socks5),
|
||||||
proxy_server: Some("127.0.0.1".to_owned()),
|
proxy_server: Some("127.0.0.1".to_owned()),
|
||||||
|
@ -19,9 +17,19 @@ async fn main() -> irc::error::Result<()> {
|
||||||
client.identify()?;
|
client.identify()?;
|
||||||
|
|
||||||
let mut stream = client.stream()?;
|
let mut stream = client.stream()?;
|
||||||
|
let sender = client.sender();
|
||||||
|
|
||||||
while let Some(message) = stream.next().await.transpose()? {
|
while let Some(message) = stream.next().await.transpose()? {
|
||||||
print!("{}", message);
|
print!("{}", message);
|
||||||
|
|
||||||
|
match message.command {
|
||||||
|
Command::PRIVMSG(ref target, ref msg) => {
|
||||||
|
if msg.contains(client.current_nickname()) {
|
||||||
|
sender.send_privmsg(target, "Hi!")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
|
@ -7,7 +7,7 @@ use std::time::Duration;
|
||||||
async fn main() -> irc::error::Result<()> {
|
async fn main() -> irc::error::Result<()> {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
nickname: Some("mastodon".to_owned()),
|
nickname: Some("mastodon".to_owned()),
|
||||||
server: Some("irc.mozilla.org".to_owned()),
|
server: Some("irc.pdgn.co".to_owned()),
|
||||||
channels: vec!["#rust-spam".to_owned()],
|
channels: vec!["#rust-spam".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::time::Duration;
|
||||||
async fn main() -> irc::error::Result<()> {
|
async fn main() -> irc::error::Result<()> {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
nickname: Some("pickles".to_owned()),
|
nickname: Some("pickles".to_owned()),
|
||||||
server: Some("irc.mozilla.org".to_owned()),
|
server: Some("irc.pdgn.co".to_owned()),
|
||||||
channels: vec!["#rust-spam".to_owned()],
|
channels: vec!["#rust-spam".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
|
@ -247,11 +247,9 @@ impl FromStr for Message {
|
||||||
args.push(suffix);
|
args.push(suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::with_tags(tags, prefix, command, args).map_err(|e| {
|
Message::with_tags(tags, prefix, command, args).map_err(|e| ProtocolError::InvalidMessage {
|
||||||
ProtocolError::InvalidMessage {
|
|
||||||
string: s.to_owned(),
|
string: s.to_owned(),
|
||||||
cause: e,
|
cause: e,
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
//! A module providing IRC connections for use by `IrcServer`s.
|
//! A module providing IRC connections for use by `IrcServer`s.
|
||||||
use futures_channel::mpsc::UnboundedSender;
|
use futures_channel::mpsc::UnboundedSender;
|
||||||
use futures_util::{sink::Sink, stream::Stream};
|
use futures_util::{sink::Sink, stream::Stream};
|
||||||
use native_tls::{Certificate, Identity, TlsConnector};
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
fs::File,
|
|
||||||
io::Read,
|
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_tls::{self, TlsStream};
|
|
||||||
use tokio_util::codec::Decoder;
|
use tokio_util::codec::Decoder;
|
||||||
|
|
||||||
#[cfg(feature = "proxy")]
|
#[cfg(feature = "proxy")]
|
||||||
|
@ -19,9 +15,37 @@ use tokio_socks::tcp::Socks5Stream;
|
||||||
#[cfg(feature = "proxy")]
|
#[cfg(feature = "proxy")]
|
||||||
use crate::client::data::ProxyType;
|
use crate::client::data::ProxyType;
|
||||||
|
|
||||||
|
#[cfg(feature = "tls-native")]
|
||||||
|
use std::{fs::File, io::Read};
|
||||||
|
|
||||||
|
#[cfg(feature = "tls-native")]
|
||||||
|
use native_tls::{Certificate, Identity, TlsConnector};
|
||||||
|
|
||||||
|
#[cfg(feature = "tls-native")]
|
||||||
|
use tokio_tls::{self, TlsStream};
|
||||||
|
|
||||||
|
#[cfg(feature = "tls-rust")]
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{BufReader, Error, ErrorKind},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "tls-rust")]
|
||||||
|
use webpki_roots::TLS_SERVER_ROOTS;
|
||||||
|
|
||||||
|
#[cfg(feature = "tls-rust")]
|
||||||
|
use tokio_rustls::{
|
||||||
|
client::TlsStream,
|
||||||
|
rustls::{internal::pemfile::certs, ClientConfig, PrivateKey},
|
||||||
|
webpki::DNSNameRef,
|
||||||
|
TlsConnector,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{
|
client::{
|
||||||
data::Config,
|
data::Config,
|
||||||
|
mock::MockStream,
|
||||||
transport::{LogView, Logged, Transport},
|
transport::{LogView, Logged, Transport},
|
||||||
},
|
},
|
||||||
error,
|
error,
|
||||||
|
@ -33,9 +57,10 @@ pub enum Connection {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
Unsecured(Transport<TcpStream>),
|
Unsecured(Transport<TcpStream>),
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
Secured(Transport<TlsStream<TcpStream>>),
|
Secured(Transport<TlsStream<TcpStream>>),
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
Mock(Logged<crate::client::mock::MockStream>),
|
Mock(Logged<MockStream>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Connection {
|
impl fmt::Debug for Connection {
|
||||||
|
@ -45,6 +70,7 @@ impl fmt::Debug for Connection {
|
||||||
"{}",
|
"{}",
|
||||||
match *self {
|
match *self {
|
||||||
Connection::Unsecured(_) => "Connection::Unsecured(...)",
|
Connection::Unsecured(_) => "Connection::Unsecured(...)",
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
Connection::Secured(_) => "Connection::Secured(...)",
|
Connection::Secured(_) => "Connection::Secured(...)",
|
||||||
Connection::Mock(_) => "Connection::Mock(...)",
|
Connection::Mock(_) => "Connection::Mock(...)",
|
||||||
}
|
}
|
||||||
|
@ -59,31 +85,81 @@ impl Connection {
|
||||||
tx: UnboundedSender<Message>,
|
tx: UnboundedSender<Message>,
|
||||||
) -> error::Result<Connection> {
|
) -> error::Result<Connection> {
|
||||||
if config.use_mock_connection() {
|
if config.use_mock_connection() {
|
||||||
use encoding::{label::encoding_from_whatwg_label, EncoderTrap};
|
log::info!("Connecting via mock to {}.", config.server()?);
|
||||||
|
return Ok(Connection::Mock(Logged::wrap(
|
||||||
let encoding = encoding_from_whatwg_label(config.encoding()).ok_or_else(|| {
|
Self::new_mocked_transport(config, tx).await?,
|
||||||
error::Error::UnknownCodec {
|
)));
|
||||||
codec: config.encoding().to_owned(),
|
|
||||||
}
|
}
|
||||||
})?;
|
|
||||||
|
|
||||||
let init_str = config.mock_initial_value();
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
let initial = encoding
|
{
|
||||||
.encode(init_str, EncoderTrap::Replace)
|
if config.use_tls() {
|
||||||
.map_err(|data| error::Error::CodecFailed {
|
log::info!("Connecting via TLS to {}.", config.server()?);
|
||||||
codec: encoding.name(),
|
return Ok(Connection::Secured(
|
||||||
data: data.into_owned(),
|
Self::new_secured_transport(config, tx).await?,
|
||||||
})?;
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let stream = crate::client::mock::MockStream::new(&initial);
|
log::info!("Connecting to {}.", config.server()?);
|
||||||
|
Ok(Connection::Unsecured(
|
||||||
|
Self::new_unsecured_transport(config, tx).await?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "proxy"))]
|
||||||
|
async fn new_stream(config: &Config) -> error::Result<TcpStream> {
|
||||||
|
Ok(TcpStream::connect((config.server()?, config.port())).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "proxy")]
|
||||||
|
async fn new_stream(config: &Config) -> error::Result<TcpStream> {
|
||||||
|
let server = config.server()?;
|
||||||
|
let port = config.port();
|
||||||
|
let address = (server, port);
|
||||||
|
|
||||||
|
match config.proxy_type() {
|
||||||
|
ProxyType::None => Ok(TcpStream::connect(address).await?),
|
||||||
|
ProxyType::Socks5 => {
|
||||||
|
let proxy_server = config.proxy_server();
|
||||||
|
let proxy_port = config.proxy_port();
|
||||||
|
let proxy = (proxy_server, proxy_port);
|
||||||
|
|
||||||
|
log::info!("Setup proxy {:?}.", proxy);
|
||||||
|
|
||||||
|
let proxy_username = config.proxy_username();
|
||||||
|
let proxy_password = config.proxy_password();
|
||||||
|
if !proxy_username.is_empty() || !proxy_password.is_empty() {
|
||||||
|
return Ok(Socks5Stream::connect_with_password(
|
||||||
|
proxy,
|
||||||
|
address,
|
||||||
|
proxy_username,
|
||||||
|
proxy_password,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.into_inner());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Socks5Stream::connect(proxy, address).await?.into_inner())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn new_unsecured_transport(
|
||||||
|
config: &Config,
|
||||||
|
tx: UnboundedSender<Message>,
|
||||||
|
) -> error::Result<Transport<TcpStream>> {
|
||||||
|
let stream = Self::new_stream(config).await?;
|
||||||
let framed = IrcCodec::new(config.encoding())?.framed(stream);
|
let framed = IrcCodec::new(config.encoding())?.framed(stream);
|
||||||
let transport = Transport::new(&config, framed, tx);
|
|
||||||
return Ok(Connection::Mock(Logged::wrap(transport)));
|
Ok(Transport::new(&config, framed, tx))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.use_ssl() {
|
#[cfg(feature = "tls-native")]
|
||||||
log::info!("Building SSL connection.");
|
async fn new_secured_transport(
|
||||||
|
config: &Config,
|
||||||
|
tx: UnboundedSender<Message>,
|
||||||
|
) -> error::Result<Transport<TlsStream<TcpStream>>> {
|
||||||
let mut builder = TlsConnector::builder();
|
let mut builder = TlsConnector::builder();
|
||||||
|
|
||||||
if let Some(cert_path) = config.cert_path() {
|
if let Some(cert_path) = config.cert_path() {
|
||||||
|
@ -107,75 +183,88 @@ impl Connection {
|
||||||
client_cert_path
|
client_cert_path
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let connector: tokio_tls::TlsConnector = builder.build()?.into();
|
let connector: tokio_tls::TlsConnector = builder.build()?.into();
|
||||||
|
let domain = config.server()?;
|
||||||
|
|
||||||
let socket = Self::new_conn(config).await?;
|
let stream = Self::new_stream(config).await?;
|
||||||
let stream = connector.connect(config.server()?, socket).await?;
|
let stream = connector.connect(domain, stream).await?;
|
||||||
let framed = IrcCodec::new(config.encoding())?.framed(stream);
|
let framed = IrcCodec::new(config.encoding())?.framed(stream);
|
||||||
let transport = Transport::new(&config, framed, tx);
|
|
||||||
|
|
||||||
Ok(Connection::Secured(transport))
|
Ok(Transport::new(&config, framed, tx))
|
||||||
} else {
|
}
|
||||||
let stream = Self::new_conn(config).await?;
|
|
||||||
|
#[cfg(feature = "tls-rust")]
|
||||||
|
async fn new_secured_transport(
|
||||||
|
config: &Config,
|
||||||
|
tx: UnboundedSender<Message>,
|
||||||
|
) -> error::Result<Transport<TlsStream<TcpStream>>> {
|
||||||
|
let mut builder = ClientConfig::default();
|
||||||
|
builder
|
||||||
|
.root_store
|
||||||
|
.add_server_trust_anchors(&TLS_SERVER_ROOTS);
|
||||||
|
|
||||||
|
if let Some(cert_path) = config.cert_path() {
|
||||||
|
let file = File::open(cert_path)?;
|
||||||
|
let mut cert_data = BufReader::new(file);
|
||||||
|
builder
|
||||||
|
.root_store
|
||||||
|
.add_pem_file(&mut cert_data)
|
||||||
|
.map_err(|_| {
|
||||||
|
error::Error::Io(Error::new(ErrorKind::InvalidInput, "invalid cert"))
|
||||||
|
})?;
|
||||||
|
log::info!("Added {} to trusted certificates.", cert_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(client_cert_path) = config.client_cert_path() {
|
||||||
|
let client_cert_pass = PrivateKey(Vec::from(config.client_cert_pass()));
|
||||||
|
let file = File::open(client_cert_path)?;
|
||||||
|
let client_cert_data = certs(&mut BufReader::new(file)).map_err(|_| {
|
||||||
|
error::Error::Io(Error::new(ErrorKind::InvalidInput, "invalid cert"))
|
||||||
|
})?;
|
||||||
|
builder
|
||||||
|
.set_single_client_cert(client_cert_data, client_cert_pass)
|
||||||
|
.map_err(|err| error::Error::Io(Error::new(ErrorKind::InvalidInput, err)))?;
|
||||||
|
log::info!(
|
||||||
|
"Using {} for client certificate authentication.",
|
||||||
|
client_cert_path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let connector = TlsConnector::from(Arc::new(builder));
|
||||||
|
let domain = DNSNameRef::try_from_ascii_str(config.server()?)?;
|
||||||
|
|
||||||
|
let stream = Self::new_stream(config).await?;
|
||||||
|
let stream = connector.connect(domain, stream).await?;
|
||||||
let framed = IrcCodec::new(config.encoding())?.framed(stream);
|
let framed = IrcCodec::new(config.encoding())?.framed(stream);
|
||||||
let transport = Transport::new(&config, framed, tx);
|
|
||||||
|
|
||||||
Ok(Connection::Unsecured(transport))
|
Ok(Transport::new(&config, framed, tx))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "proxy"))]
|
async fn new_mocked_transport(
|
||||||
async fn new_conn(config: &Config) -> error::Result<TcpStream> {
|
config: &Config,
|
||||||
let server = config.server()?;
|
tx: UnboundedSender<Message>,
|
||||||
let port = config.port();
|
) -> error::Result<Transport<MockStream>> {
|
||||||
let address = (server, port);
|
use encoding::{label::encoding_from_whatwg_label, EncoderTrap};
|
||||||
|
|
||||||
log::info!(
|
let encoding = encoding_from_whatwg_label(config.encoding()).ok_or_else(|| {
|
||||||
"Connecting to {:?} using SSL: {}",
|
error::Error::UnknownCodec {
|
||||||
address,
|
codec: config.encoding().to_owned(),
|
||||||
config.use_ssl()
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(TcpStream::connect(address).await?)
|
|
||||||
}
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
#[cfg(feature = "proxy")]
|
let init_str = config.mock_initial_value();
|
||||||
async fn new_conn(config: &Config) -> error::Result<TcpStream> {
|
let initial = encoding
|
||||||
let server = config.server()?;
|
.encode(init_str, EncoderTrap::Replace)
|
||||||
let port = config.port();
|
.map_err(|data| error::Error::CodecFailed {
|
||||||
let address = (server, port);
|
codec: encoding.name(),
|
||||||
|
data: data.into_owned(),
|
||||||
|
})?;
|
||||||
|
|
||||||
log::info!(
|
let stream = MockStream::new(&initial);
|
||||||
"Connecting to {:?} using SSL: {}",
|
let framed = IrcCodec::new(config.encoding())?.framed(stream);
|
||||||
address,
|
|
||||||
config.use_ssl()
|
|
||||||
);
|
|
||||||
|
|
||||||
match config.proxy_type() {
|
Ok(Transport::new(&config, framed, tx))
|
||||||
ProxyType::None => Ok(TcpStream::connect(address).await?),
|
|
||||||
_ => {
|
|
||||||
let proxy_server = config.proxy_server();
|
|
||||||
let proxy_port = config.proxy_port();
|
|
||||||
let proxy_username = config.proxy_username();
|
|
||||||
let proxy_password = config.proxy_password();
|
|
||||||
let proxy = (proxy_server, proxy_port);
|
|
||||||
|
|
||||||
log::info!("Setup proxy {:?}.", proxy);
|
|
||||||
|
|
||||||
if !proxy_username.is_empty() || !proxy_password.is_empty() {
|
|
||||||
return Ok(Socks5Stream::connect_with_password(
|
|
||||||
proxy,
|
|
||||||
address,
|
|
||||||
proxy_username,
|
|
||||||
proxy_password,
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
.into_inner());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Socks5Stream::connect(proxy, address).await?.into_inner())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a view of the internal logging if and only if this connection is using a mock stream.
|
/// Gets a view of the internal logging if and only if this connection is using a mock stream.
|
||||||
|
@ -194,6 +283,7 @@ impl Stream for Connection {
|
||||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
match &mut *self {
|
match &mut *self {
|
||||||
Connection::Unsecured(inner) => Pin::new(inner).poll_next(cx),
|
Connection::Unsecured(inner) => Pin::new(inner).poll_next(cx),
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
Connection::Secured(inner) => Pin::new(inner).poll_next(cx),
|
Connection::Secured(inner) => Pin::new(inner).poll_next(cx),
|
||||||
Connection::Mock(inner) => Pin::new(inner).poll_next(cx),
|
Connection::Mock(inner) => Pin::new(inner).poll_next(cx),
|
||||||
}
|
}
|
||||||
|
@ -206,6 +296,7 @@ impl Sink<Message> for Connection {
|
||||||
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
match &mut *self {
|
match &mut *self {
|
||||||
Connection::Unsecured(inner) => Pin::new(inner).poll_ready(cx),
|
Connection::Unsecured(inner) => Pin::new(inner).poll_ready(cx),
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
Connection::Secured(inner) => Pin::new(inner).poll_ready(cx),
|
Connection::Secured(inner) => Pin::new(inner).poll_ready(cx),
|
||||||
Connection::Mock(inner) => Pin::new(inner).poll_ready(cx),
|
Connection::Mock(inner) => Pin::new(inner).poll_ready(cx),
|
||||||
}
|
}
|
||||||
|
@ -214,6 +305,7 @@ impl Sink<Message> for Connection {
|
||||||
fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
|
fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
|
||||||
match &mut *self {
|
match &mut *self {
|
||||||
Connection::Unsecured(inner) => Pin::new(inner).start_send(item),
|
Connection::Unsecured(inner) => Pin::new(inner).start_send(item),
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
Connection::Secured(inner) => Pin::new(inner).start_send(item),
|
Connection::Secured(inner) => Pin::new(inner).start_send(item),
|
||||||
Connection::Mock(inner) => Pin::new(inner).start_send(item),
|
Connection::Mock(inner) => Pin::new(inner).start_send(item),
|
||||||
}
|
}
|
||||||
|
@ -222,6 +314,7 @@ impl Sink<Message> for Connection {
|
||||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
match &mut *self {
|
match &mut *self {
|
||||||
Connection::Unsecured(inner) => Pin::new(inner).poll_flush(cx),
|
Connection::Unsecured(inner) => Pin::new(inner).poll_flush(cx),
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
Connection::Secured(inner) => Pin::new(inner).poll_flush(cx),
|
Connection::Secured(inner) => Pin::new(inner).poll_flush(cx),
|
||||||
Connection::Mock(inner) => Pin::new(inner).poll_flush(cx),
|
Connection::Mock(inner) => Pin::new(inner).poll_flush(cx),
|
||||||
}
|
}
|
||||||
|
@ -230,6 +323,7 @@ impl Sink<Message> for Connection {
|
||||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
match &mut *self {
|
match &mut *self {
|
||||||
Connection::Unsecured(inner) => Pin::new(inner).poll_close(cx),
|
Connection::Unsecured(inner) => Pin::new(inner).poll_close(cx),
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
Connection::Secured(inner) => Pin::new(inner).poll_close(cx),
|
Connection::Secured(inner) => Pin::new(inner).poll_close(cx),
|
||||||
Connection::Mock(inner) => Pin::new(inner).poll_close(cx),
|
Connection::Mock(inner) => Pin::new(inner).poll_close(cx),
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,17 +120,21 @@ pub struct Config {
|
||||||
#[cfg(feature = "proxy")]
|
#[cfg(feature = "proxy")]
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub proxy_password: Option<String>,
|
pub proxy_password: Option<String>,
|
||||||
/// Whether or not to use SSL.
|
/// Whether or not to use TLS.
|
||||||
/// Clients will automatically panic if this is enabled without SSL support.
|
/// Clients will automatically panic if this is enabled without TLS support.
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub use_ssl: Option<bool>,
|
pub use_tls: Option<bool>,
|
||||||
/// The path to the SSL certificate for this server in DER format.
|
/// The path to the TLS certificate for this server in DER format.
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub cert_path: Option<String>,
|
pub cert_path: Option<String>,
|
||||||
/// The path to a SSL certificate to use for CertFP client authentication in DER format.
|
/// The path to a TLS certificate to use for CertFP client authentication in DER format.
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub client_cert_path: Option<String>,
|
pub client_cert_path: Option<String>,
|
||||||
/// The password for the certificate to use in CertFP authentication.
|
/// The password for the certificate to use in CertFP authentication.
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub client_cert_pass: Option<String>,
|
pub client_cert_pass: Option<String>,
|
||||||
/// The encoding type used for this connection.
|
/// The encoding type used for this connection.
|
||||||
|
@ -436,17 +440,22 @@ impl Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the port of the server specified in the configuration.
|
/// Gets the port of the server specified in the configuration.
|
||||||
/// This defaults to 6697 (or 6667 if use_ssl is specified as false) when not specified.
|
/// This defaults to 6697 (or 6667 if use_tls is specified as false) when not specified.
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
pub fn port(&self) -> u16 {
|
pub fn port(&self) -> u16 {
|
||||||
self.port
|
self.port.as_ref().cloned().unwrap_or(match self.use_tls() {
|
||||||
.as_ref()
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or(match self.use_ssl() {
|
|
||||||
true => 6697,
|
true => 6697,
|
||||||
false => 6667
|
false => 6667,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the port of the server specified in the configuration.
|
||||||
|
/// This defaults to 6667 when not specified.
|
||||||
|
#[cfg(not(any(feature = "tls-native", feature = "tls-rust")))]
|
||||||
|
pub fn port(&self) -> u16 {
|
||||||
|
self.port.as_ref().cloned().unwrap_or(6667)
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the server password specified in the configuration.
|
/// Gets the server password specified in the configuration.
|
||||||
/// This defaults to an empty string when not specified.
|
/// This defaults to an empty string when not specified.
|
||||||
pub fn password(&self) -> &str {
|
pub fn password(&self) -> &str {
|
||||||
|
@ -490,23 +499,27 @@ impl Config {
|
||||||
self.proxy_password.as_ref().map_or("", String::as_str)
|
self.proxy_password.as_ref().map_or("", String::as_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets whether or not to use SSL with this connection.
|
/// Gets whether or not to use TLS with this connection.
|
||||||
/// This defaults to true when not specified.
|
/// This defaults to true when not specified.
|
||||||
pub fn use_ssl(&self) -> bool {
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
self.use_ssl.as_ref().cloned().map_or(true, |s| s)
|
pub fn use_tls(&self) -> bool {
|
||||||
|
self.use_tls.as_ref().cloned().map_or(true, |s| s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the path to the SSL certificate in DER format if specified.
|
/// Gets the path to the TLS certificate in DER format if specified.
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
pub fn cert_path(&self) -> Option<&str> {
|
pub fn cert_path(&self) -> Option<&str> {
|
||||||
self.cert_path.as_ref().map(String::as_str)
|
self.cert_path.as_ref().map(String::as_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the path to the client authentication certificate in DER format if specified.
|
/// Gets the path to the client authentication certificate in DER format if specified.
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
pub fn client_cert_path(&self) -> Option<&str> {
|
pub fn client_cert_path(&self) -> Option<&str> {
|
||||||
self.client_cert_path.as_ref().map(String::as_str)
|
self.client_cert_path.as_ref().map(String::as_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the password to the client authentication certificate.
|
/// Gets the password to the client authentication certificate.
|
||||||
|
#[cfg(any(feature = "tls-native", feature = "tls-rust"))]
|
||||||
pub fn client_cert_pass(&self) -> &str {
|
pub fn client_cert_pass(&self) -> &str {
|
||||||
self.client_cert_pass.as_ref().map_or("", String::as_str)
|
self.client_cert_pass.as_ref().map_or("", String::as_str)
|
||||||
}
|
}
|
||||||
|
@ -618,9 +631,16 @@ impl Config {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{Config, Result};
|
use super::Config;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "json_config",
|
||||||
|
feature = "toml_config",
|
||||||
|
feature = "yaml_config"
|
||||||
|
))]
|
||||||
|
use super::Result;
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn test_config() -> Config {
|
fn test_config() -> Config {
|
||||||
Config {
|
Config {
|
||||||
|
|
|
@ -621,20 +621,20 @@ impl ClientState {
|
||||||
};
|
};
|
||||||
|
|
||||||
for s in seq {
|
for s in seq {
|
||||||
self.send(NICKSERV(vec!(
|
self.send(NICKSERV(vec![
|
||||||
s.to_string(),
|
s.to_string(),
|
||||||
self.config().nickname()?.to_string(),
|
self.config().nickname()?.to_string(),
|
||||||
self.config().nick_password().to_string(),
|
self.config().nick_password().to_string(),
|
||||||
)))?;
|
]))?;
|
||||||
}
|
}
|
||||||
*index = 0;
|
*index = 0;
|
||||||
self.send(NICK(self.config().nickname()?.to_owned()))?
|
self.send(NICK(self.config().nickname()?.to_owned()))?
|
||||||
}
|
}
|
||||||
|
|
||||||
self.send(NICKSERV(vec!(
|
self.send(NICKSERV(vec![
|
||||||
"IDENTIFY".to_string(),
|
"IDENTIFY".to_string(),
|
||||||
self.config().nick_password().to_string()
|
self.config().nick_password().to_string(),
|
||||||
)))
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
//! dealing with IRC channel and user modes. They appear in methods for sending mode commands,
|
//! dealing with IRC channel and user modes. They appear in methods for sending mode commands,
|
||||||
//! as well as in the parsed form of received mode commands.
|
//! as well as in the parsed form of received mode commands.
|
||||||
|
|
||||||
|
#[cfg(feature = "proxy")]
|
||||||
|
pub use crate::client::data::ProxyType;
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
client::{data::Config, Client, Sender},
|
client::{data::Config, Client, Sender},
|
||||||
proto::{
|
proto::{
|
||||||
|
|
17
src/error.rs
17
src/error.rs
|
@ -9,6 +9,9 @@ use futures_channel::{
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[cfg(feature = "tls-rust")]
|
||||||
|
use tokio_rustls::webpki::InvalidDNSNameError;
|
||||||
|
|
||||||
use crate::proto::error::{MessageParseError, ProtocolError};
|
use crate::proto::error::{MessageParseError, ProtocolError};
|
||||||
|
|
||||||
/// A specialized `Result` type for the `irc` crate.
|
/// A specialized `Result` type for the `irc` crate.
|
||||||
|
@ -27,9 +30,15 @@ pub enum Error {
|
||||||
Proxy(tokio_socks::Error),
|
Proxy(tokio_socks::Error),
|
||||||
|
|
||||||
/// An internal TLS error.
|
/// An internal TLS error.
|
||||||
|
#[cfg(feature = "tls-native")]
|
||||||
#[error("a TLS error occurred")]
|
#[error("a TLS error occurred")]
|
||||||
Tls(#[source] native_tls::Error),
|
Tls(#[source] native_tls::Error),
|
||||||
|
|
||||||
|
/// An internal DNS error.
|
||||||
|
#[cfg(feature = "tls-rust")]
|
||||||
|
#[error("a DNS error occurred")]
|
||||||
|
Dns(#[source] InvalidDNSNameError),
|
||||||
|
|
||||||
/// An internal synchronous channel closed.
|
/// An internal synchronous channel closed.
|
||||||
#[error("a sync channel closed")]
|
#[error("a sync channel closed")]
|
||||||
SyncChannelClosed(#[source] RecvError),
|
SyncChannelClosed(#[source] RecvError),
|
||||||
|
@ -176,12 +185,20 @@ impl From<tokio_socks::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tls-native")]
|
||||||
impl From<native_tls::Error> for Error {
|
impl From<native_tls::Error> for Error {
|
||||||
fn from(e: native_tls::Error) -> Error {
|
fn from(e: native_tls::Error) -> Error {
|
||||||
Error::Tls(e)
|
Error::Tls(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tls-rust")]
|
||||||
|
impl From<InvalidDNSNameError> for Error {
|
||||||
|
fn from(e: InvalidDNSNameError) -> Error {
|
||||||
|
Error::Dns(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<RecvError> for Error {
|
impl From<RecvError> for Error {
|
||||||
fn from(e: RecvError) -> Error {
|
fn from(e: RecvError) -> Error {
|
||||||
Error::SyncChannelClosed(e)
|
Error::SyncChannelClosed(e)
|
||||||
|
|
Loading…
Reference in a new issue