Merge pull request #249 from udoprog/develop

Bump dependencies, set rust-version, and some more maintenance work
This commit is contained in:
Aaron Weiss 2023-05-22 10:30:09 -07:00 committed by GitHub
commit 34aae72edc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 222 additions and 145 deletions

41
.github/workflows/ci.yml vendored Normal file
View 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

View file

@ -1,15 +1,16 @@
[package] [package]
name = "irc" name = "irc"
version = "0.15.0" version = "0.15.0"
description = "the irc crate usable, async IRC for Rust "
authors = ["Aaron Weiss <aweiss@hey.com>"] 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" license = "MPL-2.0"
keywords = ["irc", "client", "thread-safe", "async", "tokio"] keywords = ["irc", "client", "thread-safe", "async", "tokio"]
categories = ["asynchronous", "network-programming"] categories = ["asynchronous", "network-programming"]
documentation = "https://docs.rs/irc/"
repository = "https://github.com/aatxe/irc"
readme = "README.md"
edition = "2018"
[badges] [badges]
@ -37,46 +38,47 @@ yaml = ["yaml_config"]
proxy = ["tokio-socks"] proxy = ["tokio-socks"]
tls-native = ["native-tls", "tokio-native-tls"] tls-native = ["native-tls", "tokio-native-tls"]
tls-rust = ["tokio-rustls", "webpki-roots"] tls-rust = ["tokio-rustls", "webpki-roots", "rustls-pemfile"]
[dependencies] [dependencies]
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } chrono = { version = "0.4.24", default-features = false, features = ["clock", "std"] }
encoding = "0.2.0" encoding = "0.2.33"
futures-util = { version = "0.3.0", default-features = false, features = ["alloc", "sink"] } futures-util = { version = "0.3.28", default-features = false, features = ["alloc", "sink"] }
irc-proto = { version = "0.15.0", path = "irc-proto" } irc-proto = { version = "0.15.0", path = "irc-proto" }
log = "0.4.0" log = "0.4.17"
parking_lot = "0.11.0" parking_lot = "0.12.1"
thiserror = "1.0.0" thiserror = "1.0.40"
pin-project = "1.0.2" pin-project = "1.0.12"
tokio = { version = "1.0.0", features = ["net", "time", "sync"] } tokio = { version = "1.27.0", features = ["net", "time", "sync"] }
tokio-stream = "0.1.0" tokio-stream = "0.1.12"
tokio-util = { version = "0.6.0", features = ["codec"] } tokio-util = { version = "0.7.7", features = ["codec"] }
# Feature - Config # Feature - Config
serde = { version = "1.0.0", optional = true } serde = { version = "1.0.160", optional = true }
serde_derive = { version = "1.0.0", optional = true } serde_derive = { version = "1.0.160", optional = true }
serde_json = { version = "1.0.0", optional = true } serde_json = { version = "1.0.95", optional = true }
serde_yaml = { version = "0.8.0", optional = true } serde_yaml = { version = "0.9.21", optional = true }
toml = { version = "0.5.0", optional = true } toml = { version = "0.7.3", optional = true }
# Feature - Proxy # Feature - Proxy
tokio-socks = { version = "0.5.1", optional = true } tokio-socks = { version = "0.5.1", optional = true }
# Feature - TLS # Feature - TLS
native-tls = { version = "0.2.0", optional = true } native-tls = { version = "0.2.11", optional = true }
tokio-rustls = { version = "0.22.0", features = ["dangerous_configuration"], optional = true } tokio-rustls = { version = "0.24.0", features = ["dangerous_configuration"], optional = true }
tokio-native-tls = { version = "0.3.0", optional = true } rustls-pemfile = { version = "1.0.2", optional = true }
webpki-roots = { version = "0.20.0", optional = true } tokio-native-tls = { version = "0.3.1", optional = true }
webpki-roots = { version = "0.23.0", optional = true }
[dev-dependencies] [dev-dependencies]
anyhow = "1.0.0" anyhow = "1.0.70"
args = "2.0.0" args = "2.2.0"
env_logger = "0.7.0" env_logger = "0.10.0"
futures = "0.3.0" futures = "0.3.28"
getopts = "0.2.0" getopts = "0.2.21"
tokio = { version = "1.0.0", features = ["rt", "rt-multi-thread", "macros", "net", "time"] } tokio = { version = "1.27.0", features = ["rt", "rt-multi-thread", "macros", "net", "time"] }
[[example]] [[example]]

View file

@ -1,14 +1,15 @@
[package] [package]
name = "irc-proto" name = "irc-proto"
version = "0.15.0" version = "0.15.0"
description = "The IRC protocol distilled."
authors = ["Aaron Weiss <aweiss@hey.com>"] 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" license = "MPL-2.0"
keywords = ["irc", "protocol", "tokio"] keywords = ["irc", "protocol", "tokio"]
categories = ["network-programming"] categories = ["network-programming"]
documentation = "https://docs.rs/irc-proto/"
repository = "https://github.com/aatxe/irc"
edition = "2018"
[badges] [badges]
travis-ci = { repository = "aatxe/irc" } travis-ci = { repository = "aatxe/irc" }
@ -17,9 +18,9 @@ travis-ci = { repository = "aatxe/irc" }
default = ["bytes", "tokio", "tokio-util"] default = ["bytes", "tokio", "tokio-util"]
[dependencies] [dependencies]
encoding = "0.2.0" encoding = "0.2.33"
thiserror = "1.0.0" thiserror = "1.0.40"
bytes = { version = "1.0.0", optional = true } bytes = { version = "1.4.0", optional = true }
tokio = { version = "1.0.0", optional = true } tokio = { version = "1.27.0", optional = true }
tokio-util = { version = "0.6.0", features = ["codec"], optional = true } tokio-util = { version = "0.7.7", features = ["codec"], optional = true }

View file

@ -550,13 +550,11 @@ mod test {
#[test] #[test]
fn to_message_with_colon_in_suffix() { fn to_message_with_colon_in_suffix() {
let msg = "PRIVMSG #test ::test" let msg = "PRIVMSG #test ::test".parse::<Message>().unwrap();
.parse::<Message>()
.unwrap();
let message = Message { let message = Message {
tags: None, tags: None,
prefix: None, prefix: None,
command: PRIVMSG("#test".to_string(), ":test".to_string()) command: PRIVMSG("#test".to_string(), ":test".to_string()),
}; };
assert_eq!(msg, message); assert_eq!(msg, message);
} }

View file

@ -16,30 +16,32 @@ 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")] #[cfg(all(feature = "tls-native", not(feature = "tls-rust")))]
use std::{fs::File, io::Read}; 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}; 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}; use tokio_native_tls::{self, TlsStream};
#[cfg(feature = "tls-rust")]
use rustls_pemfile::certs;
#[cfg(feature = "tls-rust")] #[cfg(feature = "tls-rust")]
use std::{ use std::{
convert::TryFrom,
fs::File, fs::File,
io::{BufReader, Error, ErrorKind}, io::{BufReader, Error, ErrorKind},
sync::Arc, sync::Arc,
}; };
#[cfg(feature = "tls-rust")] #[cfg(feature = "tls-rust")]
use webpki_roots::TLS_SERVER_ROOTS; use tokio_rustls::client::TlsStream;
#[cfg(feature = "tls-rust")] #[cfg(feature = "tls-rust")]
use tokio_rustls::{ use tokio_rustls::{
client::TlsStream, rustls::client::{ServerCertVerified, ServerCertVerifier},
rustls::{self, internal::pemfile::certs, ClientConfig, PrivateKey}, rustls::{
webpki::DNSNameRef, self, Certificate, ClientConfig, OwnedTrustAnchor, PrivateKey, RootCertStore, ServerName,
},
TlsConnector, TlsConnector,
}; };
@ -157,7 +159,7 @@ impl Connection {
Ok(Transport::new(&config, framed, tx)) Ok(Transport::new(&config, framed, tx))
} }
#[cfg(feature = "tls-native")] #[cfg(all(feature = "tls-native", not(feature = "tls-rust")))]
async fn new_secured_transport( async fn new_secured_transport(
config: &Config, config: &Config,
tx: UnboundedSender<Message>, tx: UnboundedSender<Message>,
@ -221,20 +223,99 @@ impl Connection {
config: &Config, config: &Config,
tx: UnboundedSender<Message>, tx: UnboundedSender<Message>,
) -> error::Result<Transport<TlsStream<TcpStream>>> { ) -> error::Result<Transport<TlsStream<TcpStream>>> {
let mut builder = ClientConfig::default(); struct DangerousAcceptAllVerifier;
builder
.root_store
.add_server_trust_anchors(&TLS_SERVER_ROOTS);
if let Some(cert_path) = config.cert_path() { impl ServerCertVerifier for DangerousAcceptAllVerifier {
if let Ok(mut file) = File::open(cert_path) { fn verify_server_cert(
let mut cert_data = BufReader::new(file); &self,
builder _: &Certificate,
.root_store _: &[Certificate],
.add_pem_file(&mut cert_data) _: &ServerName,
.map_err(|_| { _: &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")) 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); log::info!("Added {} to trusted certificates.", cert_path);
} else { } else {
return Err(error::Error::InvalidConfig { return Err(error::Error::InvalidConfig {
@ -246,36 +327,12 @@ impl Connection {
} }
} }
if let Some(client_cert_path) = config.client_cert_path() { let builder = builder.with_root_certificates(root_store);
if let Ok(mut file) = File::open(client_cert_path) { make_client_auth!(builder)
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 connector = TlsConnector::from(Arc::new(tls_config));
let domain = ServerName::try_from(config.server()?)?;
let stream = Self::new_stream(config).await?; let stream = Self::new_stream(config).await?;
let stream = connector.connect(domain, stream).await?; let stream = connector.connect(domain, stream).await?;
let framed = Framed::new(stream, IrcCodec::new(config.encoding())?); 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());
}
}

View file

@ -523,7 +523,10 @@ impl Config {
/// Gets whether or not to dangerously accept invalid certificates. /// Gets whether or not to dangerously accept invalid certificates.
/// This defaults to `false` when not specified. /// This defaults to `false` when not specified.
pub fn dangerously_accept_invalid_certs(&self) -> bool { 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. /// Gets the path to the client authentication certificate in DER format if specified.

View file

@ -5,9 +5,8 @@ use std::sync::mpsc::RecvError;
use thiserror::Error; use thiserror::Error;
use tokio::sync::mpsc::error::{SendError, TrySendError}; use tokio::sync::mpsc::error::{SendError, TrySendError};
#[cfg(feature = "tls-rust")] #[cfg(feature = "tls-rust")]
use tokio_rustls::webpki::InvalidDNSNameError; use tokio_rustls::rustls::client::InvalidDnsNameError;
use crate::proto::error::{MessageParseError, ProtocolError}; use crate::proto::error::{MessageParseError, ProtocolError};
@ -19,26 +18,51 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
pub enum Error { pub enum Error {
/// An internal I/O error. /// An internal I/O error.
#[error("an io error occurred")] #[error("an io error occurred")]
Io(#[source] IoError), Io(
#[source]
#[from]
IoError,
),
/// An internal proxy error. /// An internal proxy error.
#[cfg(feature = "proxy")] #[cfg(feature = "proxy")]
#[error("a proxy error occurred")] #[error("a proxy error occurred")]
Proxy(tokio_socks::Error), Proxy(#[from] tokio_socks::Error),
/// An internal TLS error. /// An internal TLS error.
#[cfg(feature = "tls-native")] #[cfg(all(feature = "tls-native", not(feature = "tls-rust")))]
#[error("a TLS error occurred")] #[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")] #[cfg(feature = "tls-rust")]
#[error("a DNS error occurred")] #[error("a TLS error occurred")]
Dns(#[source] InvalidDNSNameError), 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. /// An internal synchronous channel closed.
#[error("a sync channel closed")] #[error("a sync channel closed")]
SyncChannelClosed(#[source] RecvError), SyncChannelClosed(
#[source]
#[from]
RecvError,
),
/// An internal asynchronous channel closed. /// An internal asynchronous channel closed.
#[error("an async 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 { impl<T> From<SendError<T>> for Error {
fn from(_: SendError<T>) -> Error { fn from(_: SendError<T>) -> Error {
Error::AsyncChannelClosed Error::AsyncChannelClosed