mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
Properly parse network addresses. (#1515)
This commit is contained in:
parent
9e9550ceea
commit
3c1a0ad19e
5 changed files with 200 additions and 15 deletions
|
@ -9,13 +9,12 @@ testPerm({ net: true }, function netListenClose() {
|
|||
});
|
||||
|
||||
testPerm({ net: true }, async function netDialListen() {
|
||||
const addr = "127.0.0.1:4500";
|
||||
const listener = deno.listen("tcp", addr);
|
||||
const listener = deno.listen("tcp", ":4500");
|
||||
listener.accept().then(async conn => {
|
||||
await conn.write(new Uint8Array([1, 2, 3]));
|
||||
conn.close();
|
||||
});
|
||||
const conn = await deno.dial("tcp", addr);
|
||||
const conn = await deno.dial("tcp", "127.0.0.1:4500");
|
||||
const buf = new Uint8Array(1024);
|
||||
const readResult = await conn.read(buf);
|
||||
assertEqual(3, readResult.nread);
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
use hyper;
|
||||
|
||||
pub use msg::ErrorKind;
|
||||
use resolve_addr::ResolveAddrError;
|
||||
|
||||
use hyper;
|
||||
use std;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
|
@ -149,6 +152,22 @@ impl From<hyper::Error> for DenoError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ResolveAddrError> for DenoError {
|
||||
fn from(e: ResolveAddrError) -> Self {
|
||||
match e {
|
||||
ResolveAddrError::Syntax => Self {
|
||||
repr: Repr::Simple(
|
||||
ErrorKind::InvalidInput,
|
||||
"invalid address syntax".to_string(),
|
||||
),
|
||||
},
|
||||
ResolveAddrError::Resolution(io_err) => Self {
|
||||
repr: Repr::IoErr(io_err),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bad_resource() -> DenoError {
|
||||
new(ErrorKind::BadResource, String::from("bad resource id"))
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ pub mod msg_util;
|
|||
pub mod ops;
|
||||
pub mod permissions;
|
||||
mod repl;
|
||||
pub mod resolve_addr;
|
||||
pub mod resources;
|
||||
pub mod snapshot;
|
||||
mod tokio_util;
|
||||
|
|
23
src/ops.rs
23
src/ops.rs
|
@ -10,6 +10,7 @@ use isolate::Op;
|
|||
use libdeno;
|
||||
use msg;
|
||||
use msg_util;
|
||||
use resolve_addr::resolve_addr;
|
||||
use resources;
|
||||
use resources::Resource;
|
||||
use version;
|
||||
|
@ -28,7 +29,7 @@ use resources::table_entries;
|
|||
use std;
|
||||
use std::convert::From;
|
||||
use std::fs;
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
use std::net::Shutdown;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
#[cfg(unix)]
|
||||
|
@ -36,7 +37,6 @@ use std::os::unix::process::ExitStatusExt;
|
|||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::UNIX_EPOCH;
|
||||
use std::time::{Duration, Instant};
|
||||
|
@ -1241,8 +1241,7 @@ fn op_listen(
|
|||
// https://github.com/rust-lang-nursery/rust-clippy/issues/1684
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
|
||||
Box::new(futures::future::result((move || {
|
||||
// TODO properly parse addr
|
||||
let addr = SocketAddr::from_str(address).unwrap();
|
||||
let addr = resolve_addr(address).wait()?;
|
||||
|
||||
let listener = TcpListener::bind(&addr)?;
|
||||
let resource = resources::add_tcp_listener(listener);
|
||||
|
@ -1325,15 +1324,17 @@ fn op_dial(
|
|||
let cmd_id = base.cmd_id();
|
||||
let inner = base.inner_as_dial().unwrap();
|
||||
let network = inner.network().unwrap();
|
||||
assert_eq!(network, "tcp");
|
||||
assert_eq!(network, "tcp"); // TODO Support others.
|
||||
let address = inner.address().unwrap();
|
||||
|
||||
// TODO properly parse addr
|
||||
let addr = SocketAddr::from_str(address).unwrap();
|
||||
|
||||
let op = TcpStream::connect(&addr)
|
||||
.map_err(|err| err.into())
|
||||
.and_then(move |tcp_stream| new_conn(cmd_id, tcp_stream));
|
||||
let op =
|
||||
resolve_addr(address)
|
||||
.map_err(DenoError::from)
|
||||
.and_then(move |addr| {
|
||||
TcpStream::connect(&addr)
|
||||
.map_err(DenoError::from)
|
||||
.and_then(move |tcp_stream| new_conn(cmd_id, tcp_stream))
|
||||
});
|
||||
Box::new(op)
|
||||
}
|
||||
|
||||
|
|
165
src/resolve_addr.rs
Normal file
165
src/resolve_addr.rs
Normal file
|
@ -0,0 +1,165 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use futures::Async;
|
||||
use futures::Future;
|
||||
use futures::Poll;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::net::SocketAddr;
|
||||
use std::net::ToSocketAddrs;
|
||||
|
||||
/// Go-style network address parsing. Returns a future.
|
||||
/// Examples:
|
||||
/// "192.0.2.1:25"
|
||||
/// ":80"
|
||||
/// "[2001:db8::1]:80"
|
||||
/// "198.51.100.1:80"
|
||||
/// "deno.land:443"
|
||||
pub fn resolve_addr(address: &str) -> ResolveAddrFuture {
|
||||
ResolveAddrFuture {
|
||||
address: address.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ResolveAddrError {
|
||||
Syntax,
|
||||
Resolution(std::io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for ResolveAddrError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str(self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ResolveAddrError {
|
||||
fn description(&self) -> &str {
|
||||
match self {
|
||||
ResolveAddrError::Syntax => "invalid address syntax",
|
||||
ResolveAddrError::Resolution(e) => e.description(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResolveAddrFuture {
|
||||
address: String,
|
||||
}
|
||||
|
||||
impl Future for ResolveAddrFuture {
|
||||
type Item = SocketAddr;
|
||||
type Error = ResolveAddrError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
// The implementation of this is not actually async at the moment,
|
||||
// however we intend to use async DNS resolution in the future and
|
||||
// so we expose this as a future instead of Result.
|
||||
match split(&self.address) {
|
||||
None => Err(ResolveAddrError::Syntax),
|
||||
Some(addr_port_pair) => {
|
||||
// I absolutely despise the .to_socket_addrs() API.
|
||||
let r = addr_port_pair
|
||||
.to_socket_addrs()
|
||||
.map_err(|e| ResolveAddrError::Resolution(e));
|
||||
|
||||
r.and_then(|mut iter| match iter.next() {
|
||||
Some(a) => Ok(Async::Ready(a)),
|
||||
None => panic!("There should be at least one result"),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn split<'a>(address: &'a str) -> Option<(&'a str, u16)> {
|
||||
address.rfind(":").and_then(|i| {
|
||||
let (a, p) = address.split_at(i);
|
||||
// Default to localhost if given just the port. Example: ":80"
|
||||
let addr = if a.len() > 0 { a } else { "0.0.0.0" };
|
||||
// If this looks like an ipv6 IP address. Example: "[2001:db8::1]"
|
||||
// Then we remove the brackets.
|
||||
let addr = if addr.starts_with('[') && addr.ends_with(']') {
|
||||
let l = addr.len() - 1;
|
||||
addr.get(1..l).unwrap()
|
||||
} else {
|
||||
addr
|
||||
};
|
||||
|
||||
let p = p.trim_left_matches(':');
|
||||
match p.parse::<u16>() {
|
||||
Err(_) => None,
|
||||
Ok(port) => Some((addr, port)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::Ipv6Addr;
|
||||
use std::net::SocketAddrV4;
|
||||
use std::net::SocketAddrV6;
|
||||
|
||||
#[test]
|
||||
fn split1() {
|
||||
assert_eq!(split("127.0.0.1:80"), Some(("127.0.0.1", 80)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split2() {
|
||||
assert_eq!(split(":80"), Some(("0.0.0.0", 80)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split3() {
|
||||
assert_eq!(split("no colon"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split4() {
|
||||
assert_eq!(split("deno.land:443"), Some(("deno.land", 443)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split5() {
|
||||
assert_eq!(split("[2001:db8::1]:8080"), Some(("2001:db8::1", 8080)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_addr1() {
|
||||
let expected =
|
||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 80));
|
||||
let actual = resolve_addr("127.0.0.1:80").wait().unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_addr3() {
|
||||
let expected =
|
||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 0, 2, 1), 25));
|
||||
let actual = resolve_addr("192.0.2.1:25").wait().unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_addr_ipv6() {
|
||||
let expected = SocketAddr::V6(SocketAddrV6::new(
|
||||
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
|
||||
8080,
|
||||
0,
|
||||
0,
|
||||
));
|
||||
let actual = resolve_addr("[2001:db8::1]:8080").wait().unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_addr_err() {
|
||||
let r = resolve_addr("not-a-real-domain.blahblah:8080").wait();
|
||||
match r {
|
||||
Err(ResolveAddrError::Resolution(_)) => {} // expected
|
||||
_ => assert!(false),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue