Merge pull request #249 from udoprog/develop
Bump dependencies, set rust-version, and some more maintenance work
This commit is contained in:
commit
34aae72edc
7 changed files with 222 additions and 145 deletions
41
.github/workflows/ci.yml
vendored
Normal file
41
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
pull_request: {}
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust: ["1.60", stable]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{matrix.rust}}
|
||||
- run: cargo build --workspace --all-targets
|
||||
- run: cargo build --workspace --all-targets --no-default-features
|
||||
- run: cargo build --workspace --all-targets --features tls-native
|
||||
- run: cargo build --workspace --all-targets --features tls-rust
|
||||
# runs all tests for all targets, including examples and benchmarks. Only on
|
||||
# stable, since we don't care about tests running on MSRV.
|
||||
- run: cargo test --workspace --all-targets
|
||||
if: matrix.rust == 'stable'
|
||||
# runs all documentation tests separately, since those are not picked up by
|
||||
# `--all-targets`.
|
||||
- run: cargo test --workspace --doc
|
||||
if: matrix.rust == 'stable'
|
||||
|
||||
rustfmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- run: cargo fmt --all --check
|
64
Cargo.toml
64
Cargo.toml
|
@ -1,15 +1,16 @@
|
|||
[package]
|
||||
name = "irc"
|
||||
version = "0.15.0"
|
||||
description = "the irc crate – usable, async IRC for Rust "
|
||||
authors = ["Aaron Weiss <aweiss@hey.com>"]
|
||||
edition = "2018"
|
||||
rust-version = "1.60"
|
||||
description = "the irc crate – usable, async IRC for Rust "
|
||||
documentation = "https://docs.rs/irc/"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/aatxe/irc"
|
||||
license = "MPL-2.0"
|
||||
keywords = ["irc", "client", "thread-safe", "async", "tokio"]
|
||||
categories = ["asynchronous", "network-programming"]
|
||||
documentation = "https://docs.rs/irc/"
|
||||
repository = "https://github.com/aatxe/irc"
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
|
||||
[badges]
|
||||
|
@ -37,46 +38,47 @@ yaml = ["yaml_config"]
|
|||
proxy = ["tokio-socks"]
|
||||
|
||||
tls-native = ["native-tls", "tokio-native-tls"]
|
||||
tls-rust = ["tokio-rustls", "webpki-roots"]
|
||||
tls-rust = ["tokio-rustls", "webpki-roots", "rustls-pemfile"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
|
||||
encoding = "0.2.0"
|
||||
futures-util = { version = "0.3.0", default-features = false, features = ["alloc", "sink"] }
|
||||
chrono = { version = "0.4.24", default-features = false, features = ["clock", "std"] }
|
||||
encoding = "0.2.33"
|
||||
futures-util = { version = "0.3.28", default-features = false, features = ["alloc", "sink"] }
|
||||
irc-proto = { version = "0.15.0", path = "irc-proto" }
|
||||
log = "0.4.0"
|
||||
parking_lot = "0.11.0"
|
||||
thiserror = "1.0.0"
|
||||
pin-project = "1.0.2"
|
||||
tokio = { version = "1.0.0", features = ["net", "time", "sync"] }
|
||||
tokio-stream = "0.1.0"
|
||||
tokio-util = { version = "0.6.0", features = ["codec"] }
|
||||
log = "0.4.17"
|
||||
parking_lot = "0.12.1"
|
||||
thiserror = "1.0.40"
|
||||
pin-project = "1.0.12"
|
||||
tokio = { version = "1.27.0", features = ["net", "time", "sync"] }
|
||||
tokio-stream = "0.1.12"
|
||||
tokio-util = { version = "0.7.7", 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 }
|
||||
serde = { version = "1.0.160", optional = true }
|
||||
serde_derive = { version = "1.0.160", optional = true }
|
||||
serde_json = { version = "1.0.95", optional = true }
|
||||
serde_yaml = { version = "0.9.21", optional = true }
|
||||
toml = { version = "0.7.3", optional = true }
|
||||
|
||||
# Feature - Proxy
|
||||
tokio-socks = { version = "0.5.1", optional = true }
|
||||
|
||||
# Feature - TLS
|
||||
native-tls = { version = "0.2.0", optional = true }
|
||||
tokio-rustls = { version = "0.22.0", features = ["dangerous_configuration"], optional = true }
|
||||
tokio-native-tls = { version = "0.3.0", optional = true }
|
||||
webpki-roots = { version = "0.20.0", optional = true }
|
||||
native-tls = { version = "0.2.11", optional = true }
|
||||
tokio-rustls = { version = "0.24.0", features = ["dangerous_configuration"], optional = true }
|
||||
rustls-pemfile = { version = "1.0.2", optional = true }
|
||||
tokio-native-tls = { version = "0.3.1", optional = true }
|
||||
webpki-roots = { version = "0.23.0", optional = true }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.0"
|
||||
args = "2.0.0"
|
||||
env_logger = "0.7.0"
|
||||
futures = "0.3.0"
|
||||
getopts = "0.2.0"
|
||||
tokio = { version = "1.0.0", features = ["rt", "rt-multi-thread", "macros", "net", "time"] }
|
||||
anyhow = "1.0.70"
|
||||
args = "2.2.0"
|
||||
env_logger = "0.10.0"
|
||||
futures = "0.3.28"
|
||||
getopts = "0.2.21"
|
||||
tokio = { version = "1.27.0", features = ["rt", "rt-multi-thread", "macros", "net", "time"] }
|
||||
|
||||
|
||||
[[example]]
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
[package]
|
||||
name = "irc-proto"
|
||||
version = "0.15.0"
|
||||
description = "The IRC protocol distilled."
|
||||
authors = ["Aaron Weiss <aweiss@hey.com>"]
|
||||
edition = "2018"
|
||||
rust-version = "1.60"
|
||||
description = "The IRC protocol distilled."
|
||||
documentation = "https://docs.rs/irc-proto/"
|
||||
repository = "https://github.com/aatxe/irc"
|
||||
license = "MPL-2.0"
|
||||
keywords = ["irc", "protocol", "tokio"]
|
||||
categories = ["network-programming"]
|
||||
documentation = "https://docs.rs/irc-proto/"
|
||||
repository = "https://github.com/aatxe/irc"
|
||||
edition = "2018"
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "aatxe/irc" }
|
||||
|
@ -17,9 +18,9 @@ travis-ci = { repository = "aatxe/irc" }
|
|||
default = ["bytes", "tokio", "tokio-util"]
|
||||
|
||||
[dependencies]
|
||||
encoding = "0.2.0"
|
||||
thiserror = "1.0.0"
|
||||
encoding = "0.2.33"
|
||||
thiserror = "1.0.40"
|
||||
|
||||
bytes = { version = "1.0.0", optional = true }
|
||||
tokio = { version = "1.0.0", optional = true }
|
||||
tokio-util = { version = "0.6.0", features = ["codec"], optional = true }
|
||||
bytes = { version = "1.4.0", optional = true }
|
||||
tokio = { version = "1.27.0", optional = true }
|
||||
tokio-util = { version = "0.7.7", features = ["codec"], optional = true }
|
||||
|
|
|
@ -550,13 +550,11 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn to_message_with_colon_in_suffix() {
|
||||
let msg = "PRIVMSG #test ::test"
|
||||
.parse::<Message>()
|
||||
.unwrap();
|
||||
let msg = "PRIVMSG #test ::test".parse::<Message>().unwrap();
|
||||
let message = Message {
|
||||
tags: None,
|
||||
prefix: None,
|
||||
command: PRIVMSG("#test".to_string(), ":test".to_string())
|
||||
command: PRIVMSG("#test".to_string(), ":test".to_string()),
|
||||
};
|
||||
assert_eq!(msg, message);
|
||||
}
|
||||
|
|
|
@ -16,30 +16,32 @@ use tokio_socks::tcp::Socks5Stream;
|
|||
#[cfg(feature = "proxy")]
|
||||
use crate::client::data::ProxyType;
|
||||
|
||||
#[cfg(feature = "tls-native")]
|
||||
#[cfg(all(feature = "tls-native", not(feature = "tls-rust")))]
|
||||
use std::{fs::File, io::Read};
|
||||
|
||||
#[cfg(feature = "tls-native")]
|
||||
#[cfg(all(feature = "tls-native", not(feature = "tls-rust")))]
|
||||
use native_tls::{Certificate, Identity, TlsConnector};
|
||||
|
||||
#[cfg(feature = "tls-native")]
|
||||
#[cfg(all(feature = "tls-native", not(feature = "tls-rust")))]
|
||||
use tokio_native_tls::{self, TlsStream};
|
||||
|
||||
#[cfg(feature = "tls-rust")]
|
||||
use rustls_pemfile::certs;
|
||||
#[cfg(feature = "tls-rust")]
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
fs::File,
|
||||
io::{BufReader, Error, ErrorKind},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
#[cfg(feature = "tls-rust")]
|
||||
use webpki_roots::TLS_SERVER_ROOTS;
|
||||
|
||||
use tokio_rustls::client::TlsStream;
|
||||
#[cfg(feature = "tls-rust")]
|
||||
use tokio_rustls::{
|
||||
client::TlsStream,
|
||||
rustls::{self, internal::pemfile::certs, ClientConfig, PrivateKey},
|
||||
webpki::DNSNameRef,
|
||||
rustls::client::{ServerCertVerified, ServerCertVerifier},
|
||||
rustls::{
|
||||
self, Certificate, ClientConfig, OwnedTrustAnchor, PrivateKey, RootCertStore, ServerName,
|
||||
},
|
||||
TlsConnector,
|
||||
};
|
||||
|
||||
|
@ -157,7 +159,7 @@ impl Connection {
|
|||
Ok(Transport::new(&config, framed, tx))
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls-native")]
|
||||
#[cfg(all(feature = "tls-native", not(feature = "tls-rust")))]
|
||||
async fn new_secured_transport(
|
||||
config: &Config,
|
||||
tx: UnboundedSender<Message>,
|
||||
|
@ -221,20 +223,99 @@ impl Connection {
|
|||
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);
|
||||
struct DangerousAcceptAllVerifier;
|
||||
|
||||
if let Some(cert_path) = config.cert_path() {
|
||||
if let Ok(mut file) = File::open(cert_path) {
|
||||
let mut cert_data = BufReader::new(file);
|
||||
builder
|
||||
.root_store
|
||||
.add_pem_file(&mut cert_data)
|
||||
.map_err(|_| {
|
||||
impl ServerCertVerifier for DangerousAcceptAllVerifier {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_: &Certificate,
|
||||
_: &[Certificate],
|
||||
_: &ServerName,
|
||||
_: &mut dyn Iterator<Item = &[u8]>,
|
||||
_: &[u8],
|
||||
_: std::time::SystemTime,
|
||||
) -> Result<ServerCertVerified, rustls::Error> {
|
||||
return Ok(ServerCertVerified::assertion());
|
||||
}
|
||||
}
|
||||
|
||||
enum ClientAuth {
|
||||
SingleCert(Vec<Certificate>, PrivateKey),
|
||||
NoClientAuth,
|
||||
}
|
||||
|
||||
let client_auth = if let Some(client_cert_path) = config.client_cert_path() {
|
||||
if let Ok(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"))
|
||||
})?;
|
||||
|
||||
let client_cert_data = client_cert_data
|
||||
.into_iter()
|
||||
.map(Certificate)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let client_cert_pass = PrivateKey(Vec::from(config.client_cert_pass()));
|
||||
|
||||
log::info!(
|
||||
"Using {} for client certificate authentication.",
|
||||
client_cert_path
|
||||
);
|
||||
|
||||
ClientAuth::SingleCert(client_cert_data, client_cert_pass)
|
||||
} else {
|
||||
return Err(error::Error::InvalidConfig {
|
||||
path: config.path(),
|
||||
cause: error::ConfigError::FileMissing {
|
||||
file: client_cert_path.to_string(),
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
ClientAuth::NoClientAuth
|
||||
};
|
||||
|
||||
macro_rules! make_client_auth {
|
||||
($builder:expr) => {
|
||||
match client_auth {
|
||||
ClientAuth::SingleCert(data, pass) => {
|
||||
$builder.with_single_cert(data, pass).map_err(|err| {
|
||||
error::Error::Io(Error::new(ErrorKind::InvalidInput, err))
|
||||
})?
|
||||
}
|
||||
ClientAuth::NoClientAuth => $builder.with_no_client_auth(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let builder = ClientConfig::builder()
|
||||
.with_safe_default_cipher_suites()
|
||||
.with_safe_default_kx_groups()
|
||||
.with_safe_default_protocol_versions()?;
|
||||
|
||||
let tls_config = if config.dangerously_accept_invalid_certs() {
|
||||
let builder =
|
||||
builder.with_custom_certificate_verifier(Arc::new(DangerousAcceptAllVerifier));
|
||||
make_client_auth!(builder)
|
||||
} else {
|
||||
let mut root_store = RootCertStore::empty();
|
||||
|
||||
root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(
|
||||
|ta| {
|
||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
ta.subject,
|
||||
ta.spki,
|
||||
ta.name_constraints,
|
||||
)
|
||||
},
|
||||
));
|
||||
|
||||
if let Some(cert_path) = config.cert_path() {
|
||||
if let Ok(data) = std::fs::read(cert_path) {
|
||||
root_store.add(&Certificate(data)).map_err(|_| {
|
||||
error::Error::Io(Error::new(ErrorKind::InvalidInput, "invalid cert"))
|
||||
})?;
|
||||
|
||||
log::info!("Added {} to trusted certificates.", cert_path);
|
||||
} else {
|
||||
return Err(error::Error::InvalidConfig {
|
||||
|
@ -246,36 +327,12 @@ impl Connection {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(client_cert_path) = config.client_cert_path() {
|
||||
if let Ok(mut 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"))
|
||||
})?;
|
||||
let client_cert_pass = PrivateKey(Vec::from(config.client_cert_pass()));
|
||||
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
|
||||
);
|
||||
} else {
|
||||
return Err(error::Error::InvalidConfig {
|
||||
path: config.path(),
|
||||
cause: error::ConfigError::FileMissing {
|
||||
file: client_cert_path.to_string(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if config.dangerously_accept_invalid_certs() {
|
||||
builder.dangerous().set_certificate_verifier(Arc::new(DangerousAcceptAllVerifier));
|
||||
}
|
||||
|
||||
let connector = TlsConnector::from(Arc::new(builder));
|
||||
let domain = DNSNameRef::try_from_ascii_str(config.server()?)?;
|
||||
let builder = builder.with_root_certificates(root_store);
|
||||
make_client_auth!(builder)
|
||||
};
|
||||
|
||||
let connector = TlsConnector::from(Arc::new(tls_config));
|
||||
let domain = ServerName::try_from(config.server()?)?;
|
||||
let stream = Self::new_stream(config).await?;
|
||||
let stream = connector.connect(domain, stream).await?;
|
||||
let framed = Framed::new(stream, IrcCodec::new(config.encoding())?);
|
||||
|
@ -371,19 +428,3 @@ impl Sink<Message> for Connection {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls-rust")]
|
||||
struct DangerousAcceptAllVerifier;
|
||||
|
||||
#[cfg(feature = "tls-rust")]
|
||||
impl rustls::ServerCertVerifier for DangerousAcceptAllVerifier {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_: &rustls::RootCertStore,
|
||||
_: &[rustls::Certificate],
|
||||
_: DNSNameRef,
|
||||
_: &[u8]
|
||||
) -> Result<rustls::ServerCertVerified, rustls::TLSError> {
|
||||
return Ok(rustls::ServerCertVerified::assertion());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -523,7 +523,10 @@ impl Config {
|
|||
/// Gets whether or not to dangerously accept invalid certificates.
|
||||
/// This defaults to `false` when not specified.
|
||||
pub fn dangerously_accept_invalid_certs(&self) -> bool {
|
||||
self.dangerously_accept_invalid_certs.as_ref().cloned().unwrap_or(false)
|
||||
self.dangerously_accept_invalid_certs
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Gets the path to the client authentication certificate in DER format if specified.
|
||||
|
|
77
src/error.rs
77
src/error.rs
|
@ -5,9 +5,8 @@ use std::sync::mpsc::RecvError;
|
|||
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc::error::{SendError, TrySendError};
|
||||
|
||||
#[cfg(feature = "tls-rust")]
|
||||
use tokio_rustls::webpki::InvalidDNSNameError;
|
||||
use tokio_rustls::rustls::client::InvalidDnsNameError;
|
||||
|
||||
use crate::proto::error::{MessageParseError, ProtocolError};
|
||||
|
||||
|
@ -19,26 +18,51 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
|
|||
pub enum Error {
|
||||
/// An internal I/O error.
|
||||
#[error("an io error occurred")]
|
||||
Io(#[source] IoError),
|
||||
Io(
|
||||
#[source]
|
||||
#[from]
|
||||
IoError,
|
||||
),
|
||||
|
||||
/// An internal proxy error.
|
||||
#[cfg(feature = "proxy")]
|
||||
#[error("a proxy error occurred")]
|
||||
Proxy(tokio_socks::Error),
|
||||
Proxy(#[from] tokio_socks::Error),
|
||||
|
||||
/// An internal TLS error.
|
||||
#[cfg(feature = "tls-native")]
|
||||
#[cfg(all(feature = "tls-native", not(feature = "tls-rust")))]
|
||||
#[error("a TLS error occurred")]
|
||||
Tls(#[source] native_tls::Error),
|
||||
Tls(
|
||||
#[source]
|
||||
#[from]
|
||||
native_tls::Error,
|
||||
),
|
||||
|
||||
/// An internal DNS error.
|
||||
/// An internal TLS error.
|
||||
#[cfg(feature = "tls-rust")]
|
||||
#[error("a DNS error occurred")]
|
||||
Dns(#[source] InvalidDNSNameError),
|
||||
#[error("a TLS error occurred")]
|
||||
Tls(
|
||||
#[source]
|
||||
#[from]
|
||||
tokio_rustls::rustls::Error,
|
||||
),
|
||||
|
||||
/// An invalid DNS name was specified.
|
||||
#[cfg(feature = "tls-rust")]
|
||||
#[error("invalid DNS name")]
|
||||
InvalidDnsNameError(
|
||||
#[source]
|
||||
#[from]
|
||||
InvalidDnsNameError,
|
||||
),
|
||||
|
||||
/// An internal synchronous channel closed.
|
||||
#[error("a sync channel closed")]
|
||||
SyncChannelClosed(#[source] RecvError),
|
||||
SyncChannelClosed(
|
||||
#[source]
|
||||
#[from]
|
||||
RecvError,
|
||||
),
|
||||
|
||||
/// An internal asynchronous channel closed.
|
||||
#[error("an async channel closed")]
|
||||
|
@ -176,39 +200,6 @@ impl From<ProtocolError> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<IoError> for Error {
|
||||
fn from(e: IoError) -> Error {
|
||||
Error::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "proxy")]
|
||||
impl From<tokio_socks::Error> for Error {
|
||||
fn from(e: tokio_socks::Error) -> Error {
|
||||
Error::Proxy(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls-native")]
|
||||
impl From<native_tls::Error> for Error {
|
||||
fn from(e: native_tls::Error) -> Error {
|
||||
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 {
|
||||
fn from(e: RecvError) -> Error {
|
||||
Error::SyncChannelClosed(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<SendError<T>> for Error {
|
||||
fn from(_: SendError<T>) -> Error {
|
||||
Error::AsyncChannelClosed
|
||||
|
|
Loading…
Reference in a new issue