mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(net): add nat external ip crate (#605)
This commit is contained in:
62
Cargo.lock
generated
62
Cargo.lock
generated
@ -173,6 +173,18 @@ dependencies = [
|
||||
"critical-section",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attohttpc"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb8867f378f33f78a811a8eb9bf108ad99430d7aad43315dd9319c827ef6247"
|
||||
dependencies = [
|
||||
"http",
|
||||
"log",
|
||||
"url",
|
||||
"wildmatch",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@ -1933,6 +1945,23 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
|
||||
|
||||
[[package]]
|
||||
name = "igd"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/stevefan1999-personal/rust-igd#c2d1f83eb1612a462962453cb0703bc93258b173"
|
||||
dependencies = [
|
||||
"attohttpc",
|
||||
"bytes",
|
||||
"futures",
|
||||
"http",
|
||||
"hyper",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"tokio",
|
||||
"url",
|
||||
"xmltree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-codec"
|
||||
version = "0.6.0"
|
||||
@ -3579,6 +3608,18 @@ dependencies = [
|
||||
"reth-primitives",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reth-net-nat"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"igd",
|
||||
"pin-project-lite",
|
||||
"public-ip",
|
||||
"reth-tracing",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reth-network"
|
||||
version = "0.1.0"
|
||||
@ -5251,6 +5292,12 @@ dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wildmatch"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f44b95f62d34113cf558c93511ac93027e03e9c29a60dd0fd70e6e025c7270a"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@ -5418,6 +5465,21 @@ dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
|
||||
|
||||
[[package]]
|
||||
name = "xmltree"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb"
|
||||
dependencies = [
|
||||
"xml-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.5.7"
|
||||
|
||||
@ -11,6 +11,7 @@ members = [
|
||||
"crates/net/ecies",
|
||||
"crates/net/eth-wire",
|
||||
"crates/net/discv4",
|
||||
"crates/net/nat",
|
||||
"crates/net/network",
|
||||
"crates/net/ipc",
|
||||
"crates/net/rpc",
|
||||
|
||||
27
crates/net/nat/Cargo.toml
Normal file
27
crates/net/nat/Cargo.toml
Normal file
@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "reth-net-nat"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/paradigmxyz/reth"
|
||||
description = """
|
||||
Helpers for working around NAT
|
||||
"""
|
||||
|
||||
[dependencies]
|
||||
|
||||
# nat
|
||||
public-ip = "0.2"
|
||||
## fork of rust-igd with ipv6 support: https://github.com/sbstp/rust-igd/issues/47
|
||||
igd = { git = "https://github.com/stevefan1999-personal/rust-igd", features = [
|
||||
"aio",
|
||||
"tokio1",
|
||||
] }
|
||||
|
||||
# misc
|
||||
tracing = "0.1"
|
||||
pin-project-lite = "0.2.9"
|
||||
|
||||
[dev-dependencies]
|
||||
reth-tracing = { path = "../../tracing" }
|
||||
tokio = { version = "1", features = ["macros"] }
|
||||
137
crates/net/nat/src/lib.rs
Normal file
137
crates/net/nat/src/lib.rs
Normal file
@ -0,0 +1,137 @@
|
||||
#![warn(missing_docs, unused_crate_dependencies)]
|
||||
#![deny(unused_must_use, rust_2018_idioms)]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
))]
|
||||
|
||||
//! Helpers for resolving the external IP.
|
||||
|
||||
use igd::aio::search_gateway;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::{
|
||||
future::Future,
|
||||
net::IpAddr,
|
||||
pin::Pin,
|
||||
task::{ready, Context, Poll},
|
||||
};
|
||||
use tracing::warn;
|
||||
|
||||
/// All builtin resolvers.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Hash)]
|
||||
pub enum NatResolver {
|
||||
/// Resolve with any available resolver.
|
||||
#[default]
|
||||
Any,
|
||||
/// Resolve via Upnp
|
||||
Upnp,
|
||||
/// Resolve external IP via [public_ip::Resolver]
|
||||
ExternalIp,
|
||||
}
|
||||
|
||||
// === impl NatResolver ===
|
||||
|
||||
impl NatResolver {
|
||||
/// Attempts to produce an IP address (best effort).
|
||||
pub async fn external_addr(self) -> Option<IpAddr> {
|
||||
external_addr_with(self).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to produce an IP address with all builtin resolvers (best effort).
|
||||
pub async fn external_ip() -> Option<IpAddr> {
|
||||
external_addr_with(NatResolver::Any).await
|
||||
}
|
||||
|
||||
/// Given a [`NatResolver`] attempts to produce an IP address (best effort).
|
||||
pub async fn external_addr_with(resolver: NatResolver) -> Option<IpAddr> {
|
||||
match resolver {
|
||||
NatResolver::Any => {
|
||||
ResolveAny {
|
||||
upnp: Some(Box::pin(resolve_external_ip_upnp())),
|
||||
external: Some(Box::pin(resolve_external_ip())),
|
||||
}
|
||||
.await
|
||||
}
|
||||
NatResolver::Upnp => resolve_external_ip_upnp().await,
|
||||
NatResolver::ExternalIp => resolve_external_ip().await,
|
||||
}
|
||||
}
|
||||
|
||||
type ResolveFut = Pin<Box<dyn Future<Output = Option<IpAddr>>>>;
|
||||
|
||||
pin_project! {
|
||||
/// A future that resolves the first ip via all configured resolvers
|
||||
struct ResolveAny {
|
||||
#[pin]
|
||||
upnp: Option<ResolveFut>,
|
||||
#[pin]
|
||||
external: Option<ResolveFut>,
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for ResolveAny {
|
||||
type Output = Option<IpAddr>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut this = self.as_mut().project();
|
||||
|
||||
if let Some(upnp) = this.upnp.as_mut().as_pin_mut() {
|
||||
// if upnp is configured we prefer it over http and dns resolvers
|
||||
let ip = ready!(upnp.poll(cx));
|
||||
this.upnp.set(None);
|
||||
if ip.is_some() {
|
||||
return Poll::Ready(ip)
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(upnp) = this.external.as_mut().as_pin_mut() {
|
||||
if let Poll::Ready(ip) = upnp.poll(cx) {
|
||||
this.external.set(None);
|
||||
if ip.is_some() {
|
||||
return Poll::Ready(ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if this.upnp.is_none() && this.external.is_none() {
|
||||
return Poll::Ready(None)
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
async fn resolve_external_ip_upnp() -> Option<IpAddr> {
|
||||
search_gateway(Default::default())
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!(target: "net::nat", ?err, "failed to find upnp gateway");
|
||||
err
|
||||
})
|
||||
.ok()?
|
||||
.get_external_ip()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!(target: "net::nat", ?err, "failed to resolve external ip via upnp gateway");
|
||||
err
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
|
||||
async fn resolve_external_ip() -> Option<IpAddr> {
|
||||
public_ip::addr().await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn get_external_ip() {
|
||||
reth_tracing::init_tracing();
|
||||
let ip = external_ip().await;
|
||||
dbg!(ip);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user