Skip to content

Commit

Permalink
Allow async and sync nats to compile on MIPS and PowerPC
Browse files Browse the repository at this point in the history
Now that nats.rs depends on a version of rustls that includes the latest
version of ring, it can now be compiled to run on additional types of
CPU architectures.

Specifically this converts all uses of `AtomicU64` to use the type
provided by the `portable-atomic` crate, which uses the native version
of `AtomicU64` on all architectures except ones where that type isn't
supported.

I went ahead and updated sync nats with the latest version of rustls
which inflated the size and scope of this PR beyond just swapping out
the AtomicU64 implementation.
  • Loading branch information
protochron authored and Jarema committed Mar 4, 2024
1 parent 22a9718 commit 86b5bef
Show file tree
Hide file tree
Showing 9 changed files with 44 additions and 78 deletions.
1 change: 1 addition & 0 deletions async-nats/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ tryhard = "0.5"
ring = "0.17"
rand = "0.8"
webpki = { package = "rustls-webpki", version = "0.102" }
portable-atomic = "1"

[dev-dependencies]
criterion = { version = "0.5", features = ["async_tokio"]}
Expand Down
3 changes: 2 additions & 1 deletion async-nats/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ use bytes::Bytes;
use futures::future::TryFutureExt;
use futures::StreamExt;
use once_cell::sync::Lazy;
use portable_atomic::AtomicU64;
use regex::Regex;
use std::fmt::Display;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
use thiserror::Error;
Expand Down
6 changes: 2 additions & 4 deletions async-nats/src/jetstream/consumer/push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,16 @@ use crate::{

use bytes::Bytes;
use futures::{future::BoxFuture, FutureExt};
use portable_atomic::AtomicU64;
use serde::{Deserialize, Serialize};
#[cfg(feature = "server_2_10")]
use std::collections::HashMap;
use std::task::{self, Poll};
use std::{
io::{self, ErrorKind},
pin::Pin,
sync::Arc,
};
use std::{
sync::atomic::AtomicU64,
task::{self, Poll},
};
use std::{sync::atomic::Ordering, time::Duration};
use tokio::{sync::oneshot::error::TryRecvError, task::JoinHandle};
use tracing::{debug, trace};
Expand Down
11 changes: 6 additions & 5 deletions nats/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,20 @@ nkeys = "0.3.2"
nuid = "0.3.1"
once_cell = "1.8.0"
parking_lot = "0.12.0"
portable-atomic = "1"
regex = { version = "1.5.5", default-features = false, features = ["std", "unicode-perl"] }
rustls = "0.21"
rustls-native-certs = "0.6"
rustls-pemfile = "1.0.2"
webpki = { package = "rustls-webpki", version = "0.100.0", features = ["alloc", "std"] }
rustls = "0.22"
rustls-native-certs = "0.7"
rustls-pemfile = "2"
webpki = { package = "rustls-webpki", version = "0.102"}
serde = { version = "1.0.126", features = ["derive"] }
serde_json = "1.0.64"
serde_nanos = "0.1.1"
serde_repr = "0.1.7"
memchr = "2.4.0"
url = "2.2.2"
time = { version = "0.3.6", features = ["parsing", "formatting", "serde", "serde-well-known"] }
ring = "0.16"
ring = "0.17"

[target.'cfg(unix)'.dependencies]
libc = "0.2.98"
Expand Down
30 changes: 6 additions & 24 deletions nats/src/auth_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::path::Path;
use nkeys::KeyPair;
use once_cell::sync::Lazy;
use regex::Regex;
use rustls::{Certificate, PrivateKey};
use webpki::types::{CertificateDer, PrivateKeyDer};

use crate::SecureString;

Expand Down Expand Up @@ -100,36 +100,18 @@ fn parse_decorated_nkey(contents: &str) -> Option<SecureString> {
/// If the pem file is found, but does not contain any certificates, it will return
/// empty set of Certificates, not error.
/// Can be used to parse only client certificates from .pem file containing both client key and certs.
pub(crate) fn load_certs(path: &Path) -> io::Result<Vec<Certificate>> {
pub(crate) fn load_certs(path: &Path) -> io::Result<Vec<CertificateDer<'static>>> {
let file = std::fs::File::open(path)?;
let mut reader = BufReader::new(file);
let certs = rustls_pemfile::certs(&mut reader)?
.iter()
.map(|v| Certificate(v.clone()))
.collect();
Ok(certs)
rustls_pemfile::certs(&mut reader).collect::<io::Result<Vec<_>>>()
}

/// Loads client key from a `.pem` file.
/// Can be used to parse only client key from .pem file containing both client key and certs.
pub(crate) fn load_key(path: &Path) -> io::Result<PrivateKey> {
pub(crate) fn load_key(path: &Path) -> io::Result<PrivateKeyDer<'static>> {
let file = std::fs::File::open(path)?;
let mut reader = BufReader::new(file);

loop {
let cert = rustls_pemfile::read_one(&mut reader)?;
match cert {
Some(rustls_pemfile::Item::RSAKey(key))
| Some(rustls_pemfile::Item::PKCS8Key(key))
| Some(rustls_pemfile::Item::ECKey(key)) => return Ok(PrivateKey(key)),
// if public key is found, don't error, just skip it and hope to find client key next.
Some(rustls_pemfile::Item::X509Certificate(_)) | Some(_) => {}
None => break,
}
}

Err(io::Error::new(
ErrorKind::NotFound,
"could not find client key in the path",
))
rustls_pemfile::private_key(&mut reader)?
.ok_or_else(|| io::Error::new(ErrorKind::NotFound, "could not find client key in the path"))
}
55 changes: 22 additions & 33 deletions nats/src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,14 @@ fn configure_tls(options: &Arc<Options>) -> Result<ClientConfig, Error> {

// load native system certs only if user did not specify them
if options.tls_client_config.is_some() || options.certificates.is_empty() {
let native_certs = rustls_native_certs::load_native_certs()
.map_err(|err| {
io::Error::new(
ErrorKind::Other,
format!("could not load platform certs: {err}"),
)
})?
.into_iter()
.map(|cert| cert.0)
.collect::<Vec<Vec<u8>>>();
let native_certs = rustls_native_certs::load_native_certs().map_err(|err| {
io::Error::new(
ErrorKind::Other,
format!("could not load platform certs: {err}"),
)
})?;

root_store.add_parsable_certificates(&native_certs);
root_store.add_parsable_certificates(native_certs);
}

if let Some(config) = &options.tls_client_config {
Expand All @@ -71,28 +67,21 @@ fn configure_tls(options: &Arc<Options>) -> Result<ClientConfig, Error> {
// Include user-provided certificates
for path in &options.certificates {
let mut pem = BufReader::new(std::fs::File::open(path)?);
let certs = rustls_pemfile::certs(&mut pem)?;
let trust_anchors = certs.iter().map(|cert| {
let trust_anchor = webpki::TrustAnchor::try_from_cert_der(&cert[..])
.map_err(|err| {
io::Error::new(
ErrorKind::InvalidInput,
format!("could not load certs: {err}"),
)
})
.unwrap();
rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
trust_anchor.subject,
trust_anchor.spki,
trust_anchor.name_constraints,
)
});
root_store.add_server_trust_anchors(trust_anchors);
let certs = rustls_pemfile::certs(&mut pem).collect::<io::Result<Vec<_>>>()?;
let trust_anchors = certs
.into_iter()
.map(|cert| webpki::anchor_from_trusted_cert(&cert).map(|ta| ta.to_owned()))
.collect::<Result<Vec<_>, webpki::Error>>()
.map_err(|err| {
io::Error::new(
ErrorKind::InvalidInput,
format!("could not load certs: {err}"),
)
})?;
root_store.extend(trust_anchors);
}

let builder = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store);
let builder = rustls::ClientConfig::builder().with_root_certificates(root_store);

if let Some(cert) = &options.client_cert {
if let Some(key) = &options.client_key {
Expand Down Expand Up @@ -278,8 +267,8 @@ impl Connector {
inject_io_failure()?;

// Connect using TLS.
let server_name =
rustls::client::ServerName::try_from(server.host()).map_err(|_| {
let server_name = webpki::types::ServerName::try_from(server.host().to_string())
.map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"cannot determine hostname for TLS connection",
Expand Down
3 changes: 2 additions & 1 deletion nats/src/jetstream/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ use std::{
};

use parking_lot::Mutex;
use std::sync::atomic::{AtomicU64, Ordering};
use portable_atomic::AtomicU64;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;

Expand Down
3 changes: 2 additions & 1 deletion nats/src/jetstream/push_subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use portable_atomic::AtomicU64;
use std::io;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
Expand Down
10 changes: 1 addition & 9 deletions nats/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,17 +343,9 @@ impl Options {
/// ```no_run
/// # fn main() -> std::io::Result<()> {
/// let mut root_store = nats::rustls::RootCertStore::empty();
///
/// root_store.add_parsable_certificates(
/// rustls_native_certs::load_native_certs()?
/// .into_iter()
/// .map(|cert| cert.0)
/// .collect::<Vec<Vec<u8>>>()
/// .as_ref(),
/// );
/// root_store.add_parsable_certificates(rustls_native_certs::load_native_certs()?);
///
/// let tls_client_config = nats::rustls::ClientConfig::builder()
/// .with_safe_defaults()
/// .with_root_certificates(root_store)
/// .with_no_client_auth();
///
Expand Down

0 comments on commit 86b5bef

Please sign in to comment.