#[cfg_attr(test, macro_use)]
extern crate log;
use core::fmt;
use byteorder::{BigEndian, ByteOrder};
#[cfg(feature = "proto-ipv4")]
use smoltcp::wire::Ipv4Address;
#[cfg(feature = "proto-ipv6")]
use smoltcp::wire::Ipv6Address;
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
use smoltcp::wire::{IpAddress, IpEndpoint};
#[cfg(feature = "proto-ipv4")]
pub use crate::ipv4::{ipv4_addr, ipv4_addr_from_bytes, SocketAddrV4};
#[cfg(feature = "proto-ipv6")]
pub use crate::ipv6::{ipv6_addr, ipv6_addr_from_bytes, SocketAddrV6};
#[cfg(feature = "std")]
pub use crate::std::*;
type Result<T> = core::result::Result<T, Error>;
pub const LEN_V4: usize = 6;
pub const LEN_V6: usize = 18;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
AddrParseError,
UnspecifiedIp,
}
impl ::std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::AddrParseError => write!(f, "invalid IP address bytes"),
Error::UnspecifiedIp => write!(f, "UnspecifiedIp"),
}
}
}
pub fn port_from_bytes(high: u8, low: u8) -> u16 {
u16::from(high) << 8 | u16::from(low)
}
pub fn port_to_bytes(port: u16) -> [u8; 2] {
let mut buf = [0; 2];
BigEndian::write_u16(&mut buf, port);
buf
}
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
#[derive(Copy, Clone, Eq, Hash, PartialEq)]
pub enum SocketAddr {
#[cfg(feature = "proto-ipv4")]
V4(SocketAddrV4),
#[cfg(feature = "proto-ipv6")]
V6(SocketAddrV6),
}
#[allow(clippy::len_without_is_empty)]
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
impl SocketAddr {
pub fn new(ip: IpAddress, port: u16) -> Result<Self> {
match ip {
#[cfg(feature = "proto-ipv4")]
IpAddress::Ipv4(ip) => Ok(SocketAddr::new_v4(ip, port)),
#[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(ip) => Ok(SocketAddr::V6(SocketAddrV6::new(ip, port))),
IpAddress::Unspecified => Err(Error::UnspecifiedIp),
_ => unreachable!(),
}
}
#[cfg(feature = "proto-ipv4")]
pub fn new_v4(ip: Ipv4Address, port: u16) -> Self {
SocketAddr::V4(SocketAddrV4::new(ip, port))
}
#[cfg(feature = "proto-ipv4")]
pub fn v4_from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() == LEN_V4 {
let ip_addr = ipv4_addr_from_bytes(&bytes[0..LEN_V4 - 2])?;
let port_bytes = &bytes[LEN_V4 - 2..LEN_V4];
let port = port_from_bytes(port_bytes[0], port_bytes[1]);
Ok(SocketAddr::new_v4(ip_addr, port))
} else {
Err(Error::AddrParseError)
}
}
#[cfg(feature = "proto-ipv4")]
pub fn new_ip4_port(a0: u8, a1: u8, a2: u8, a3: u8, port: u16) -> Self {
SocketAddr::V4(SocketAddrV4::new_ip4_port(a0, a1, a2, a3, port))
}
#[cfg(feature = "proto-ipv6")]
pub fn new_v6(ip: Ipv6Address, port: u16) -> Self {
SocketAddr::V6(SocketAddrV6::new(ip, port))
}
#[cfg(feature = "proto-ipv6")]
pub fn v6_from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() == LEN_V6 {
let ip_addr = ipv6_addr_from_bytes(&bytes[0..LEN_V6 - 2])?;
let port_bytes = &bytes[LEN_V6 - 2..LEN_V6];
let port = port_from_bytes(port_bytes[0], port_bytes[1]);
Ok(SocketAddr::new_v6(ip_addr, port))
} else {
Err(Error::AddrParseError)
}
}
#[cfg(feature = "proto-ipv6")]
#[allow(clippy::too_many_arguments)]
pub fn new_ip6_port(
a0: u16,
a1: u16,
a2: u16,
a3: u16,
a4: u16,
a5: u16,
a6: u16,
a7: u16,
port: u16,
) -> Self {
SocketAddr::V6(SocketAddrV6::new_ip6_port(
a0, a1, a2, a3, a4, a5, a6, a7, port,
))
}
pub fn len(&self) -> usize {
match self {
#[cfg(feature = "proto-ipv4")]
SocketAddr::V4(addr) => addr.len(),
#[cfg(feature = "proto-ipv6")]
SocketAddr::V6(addr) => addr.len(),
}
}
pub fn ip(&self) -> IpAddress {
match self {
#[cfg(feature = "proto-ipv4")]
SocketAddr::V4(addr) => IpAddress::Ipv4(addr.addr),
#[cfg(feature = "proto-ipv6")]
SocketAddr::V6(addr) => IpAddress::Ipv6(addr.addr),
}
}
pub fn ip_octets(&self) -> Vec<u8> {
match self {
#[cfg(feature = "proto-ipv4")]
SocketAddr::V4(addr) => addr.addr.0.to_vec(),
#[cfg(feature = "proto-ipv6")]
SocketAddr::V6(addr) => addr.addr.0.to_vec(),
}
}
pub fn port(&self) -> u16 {
match self {
#[cfg(feature = "proto-ipv4")]
SocketAddr::V4(addr) => addr.port,
#[cfg(feature = "proto-ipv6")]
SocketAddr::V6(addr) => addr.port,
}
}
pub fn to_vec(&self) -> Vec<u8> {
match self {
#[cfg(feature = "proto-ipv4")]
SocketAddr::V4(addr) => addr.to_vec(),
#[cfg(feature = "proto-ipv6")]
SocketAddr::V6(addr) => addr.to_vec(),
}
}
}
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
impl From<SocketAddr> for IpEndpoint {
fn from(val: SocketAddr) -> Self {
match val {
#[cfg(feature = "proto-ipv4")]
SocketAddr::V4(val) => IpEndpoint::new(IpAddress::Ipv4(val.addr), val.port),
#[cfg(feature = "proto-ipv6")]
SocketAddr::V6(val) => IpEndpoint::new(IpAddress::Ipv6(val.addr), val.port),
}
}
}
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
impl fmt::Display for SocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
#[cfg(feature = "proto-ipv4")]
SocketAddr::V4(ref a) => a.fmt(f),
#[cfg(feature = "proto-ipv6")]
SocketAddr::V6(ref a) => a.fmt(f),
}
}
}
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
impl fmt::Debug for SocketAddr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, fmt)
}
}
#[cfg(feature = "proto-ipv4")]
mod ipv4 {
use core::fmt;
use smoltcp::wire::Ipv4Address;
use super::{port_to_bytes, Error, Result, LEN_V4};
pub fn ipv4_addr_from_bytes(bytes: &[u8]) -> Result<Ipv4Address> {
if bytes.len() == 4 {
Ok(Ipv4Address::from_bytes(bytes))
} else {
Err(Error::AddrParseError)
}
}
pub fn ipv4_addr(a0: u8, a1: u8, a2: u8, a3: u8) -> Ipv4Address {
ipv4_addr_from_bytes(&[a0, a1, a2, a3]).expect("should be valid")
}
#[derive(Copy, Clone, Eq, Hash, PartialEq)]
pub struct SocketAddrV4 {
pub addr: Ipv4Address,
pub port: u16,
}
#[allow(clippy::len_without_is_empty)]
#[allow(clippy::trivially_copy_pass_by_ref)]
impl SocketAddrV4 {
pub fn new(addr: Ipv4Address, port: u16) -> Self {
SocketAddrV4 { addr, port }
}
pub fn new_ip4_port(a0: u8, a1: u8, a2: u8, a3: u8, port: u16) -> Self {
SocketAddrV4 {
addr: Ipv4Address::new(a0, a1, a2, a3),
port,
}
}
pub fn len(&self) -> usize {
LEN_V4
}
pub fn to_vec(&self) -> Vec<u8> {
let mut result = Vec::with_capacity(self.len());
result.extend_from_slice(&self.addr.0);
result.extend_from_slice(&port_to_bytes(self.port));
result
}
}
impl fmt::Display for SocketAddrV4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.addr, self.port)
}
}
impl fmt::Debug for SocketAddrV4 {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, fmt)
}
}
}
#[cfg(feature = "proto-ipv6")]
mod ipv6 {
use core::fmt;
use byteorder::{BigEndian, ByteOrder};
use smoltcp::wire::Ipv6Address;
use super::{port_to_bytes, Error, Result, LEN_V6};
#[cfg(feature = "proto-ipv6")]
pub fn ipv6_addr_from_bytes(bytes: &[u8]) -> Result<Ipv6Address> {
if bytes.len() == 16 {
Ok(Ipv6Address::from_bytes(bytes))
} else {
Err(Error::AddrParseError)
}
}
#[allow(clippy::too_many_arguments)]
pub fn ipv6_addr(
a0: u16,
a1: u16,
a2: u16,
a3: u16,
a4: u16,
a5: u16,
a6: u16,
a7: u16,
) -> Ipv6Address {
let ip_address: [u16; 8] = [a0, a1, a2, a3, a4, a5, a6, a7];
let mut ip_address_bytes = [0u8; 16];
{
let p = &mut ip_address_bytes[..];
for (idx, quartet) in ip_address.iter().enumerate() {
let idx = idx * 2;
BigEndian::write_u16(&mut p[idx..idx + 2], *quartet);
}
}
ipv6_addr_from_bytes(&ip_address_bytes).expect("should be valid")
}
#[derive(Copy, Clone, Eq, Hash, PartialEq)]
pub struct SocketAddrV6 {
pub addr: Ipv6Address,
pub port: u16,
}
#[allow(clippy::len_without_is_empty)]
#[allow(clippy::trivially_copy_pass_by_ref)]
impl SocketAddrV6 {
pub fn new(addr: Ipv6Address, port: u16) -> Self {
SocketAddrV6 { addr, port }
}
#[allow(clippy::too_many_arguments)]
pub fn new_ip6_port(
a0: u16,
a1: u16,
a2: u16,
a3: u16,
a4: u16,
a5: u16,
a6: u16,
a7: u16,
port: u16,
) -> Self {
SocketAddrV6 {
addr: Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7),
port,
}
}
pub fn len(&self) -> usize {
LEN_V6
}
pub fn to_vec(&self) -> Vec<u8> {
let mut result = Vec::with_capacity(self.len());
result.extend_from_slice(&self.addr.0);
result.extend_from_slice(&port_to_bytes(self.port));
result
}
}
impl fmt::Display for SocketAddrV6 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}]:{}", self.addr, self.port)
}
}
impl fmt::Debug for SocketAddrV6 {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, fmt)
}
}
}
#[cfg(feature = "std")]
mod std {
#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
use ::std::net::{IpAddr, SocketAddr as StdSocketAddr};
#[cfg(feature = "proto-ipv4")]
use ::std::net::{Ipv4Addr, SocketAddrV4 as StdSocketAddrV4};
#[cfg(feature = "proto-ipv6")]
use ::std::net::{Ipv6Addr, SocketAddrV6 as StdSocketAddrV6};
#[cfg(feature = "proto-ipv4")]
use smoltcp::wire::Ipv4Address;
#[cfg(feature = "proto-ipv6")]
use smoltcp::wire::Ipv6Address;
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
use smoltcp::wire::{IpAddress, IpEndpoint};
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
use super::SocketAddr;
#[cfg(feature = "proto-ipv4")]
use super::SocketAddrV4;
#[cfg(feature = "proto-ipv6")]
use super::SocketAddrV6;
use super::{Error, Result};
#[cfg(feature = "proto-ipv4")]
pub trait IntoStdIpv4Addr: Sized {
fn into_std(self) -> Ipv4Addr;
}
#[cfg(feature = "proto-ipv4")]
impl IntoStdIpv4Addr for Ipv4Address {
fn into_std(self) -> Ipv4Addr {
self.0.into()
}
}
#[cfg(feature = "proto-ipv6")]
pub trait IntoStdIpv6Addr: Sized {
fn into_std(self) -> Ipv6Addr;
}
#[cfg(feature = "proto-ipv6")]
impl IntoStdIpv6Addr for Ipv6Address {
fn into_std(self) -> Ipv6Addr {
self.0.into()
}
}
#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
pub trait IntoStdIpAddr: Sized {
fn into_std(self) -> Result<IpAddr>;
}
#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
impl IntoStdIpAddr for IpAddress {
fn into_std(self) -> Result<IpAddr> {
match self {
IpAddress::Ipv4(ip) => Ok(ip.0.into()),
IpAddress::Ipv6(ip) => Ok(ip.0.into()),
IpAddress::Unspecified => Err(Error::UnspecifiedIp),
_ => unreachable!(),
}
}
}
#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
pub trait IntoStdSocketAddr: Sized {
fn into_std(self) -> Result<StdSocketAddr>;
}
#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
impl IntoStdSocketAddr for IpEndpoint {
fn into_std(self) -> Result<StdSocketAddr> {
let addr: IpAddr = self.addr.into_std()?;
Ok(StdSocketAddr::new(addr, self.port))
}
}
#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
impl From<SocketAddr> for StdSocketAddr {
fn from(val: SocketAddr) -> Self {
match val {
SocketAddr::V4(val) => {
StdSocketAddr::V4(StdSocketAddrV4::new(val.addr.0.into(), val.port))
}
SocketAddr::V6(val) => {
StdSocketAddr::V6(StdSocketAddrV6::new(val.addr.0.into(), val.port, 0, 0))
}
}
}
}
#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
impl From<&SocketAddr> for StdSocketAddr {
fn from(val: &SocketAddr) -> Self {
match val {
SocketAddr::V4(val) => {
StdSocketAddr::V4(StdSocketAddrV4::new(val.addr.0.into(), val.port))
}
SocketAddr::V6(val) => {
StdSocketAddr::V6(StdSocketAddrV6::new(val.addr.0.into(), val.port, 0, 0))
}
}
}
}
#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
impl From<StdSocketAddr> for SocketAddr {
fn from(val: StdSocketAddr) -> Self {
match val {
StdSocketAddr::V4(val) => SocketAddr::V4(SocketAddrV4 {
addr: Ipv4Address::from(*val.ip()),
port: val.port(),
}),
StdSocketAddr::V6(val) => SocketAddr::V6(SocketAddrV6 {
addr: Ipv6Address::from(*val.ip()),
port: val.port(),
}),
}
}
}
}
#[cfg(test)]
mod tests {
use ::std::env;
#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
use ::std::net::IpAddr;
#[cfg(all(feature = "std", feature = "proto-ipv4"))]
use ::std::net::Ipv4Addr;
#[cfg(all(feature = "std", feature = "proto-ipv6"))]
use ::std::net::Ipv6Addr;
#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
use ::std::net::SocketAddr as StdSocketAddr;
use super::*;
#[cfg(feature = "proto-ipv4")]
#[test]
fn ipv4_socket_addr() {
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "debug");
}
let _ = pretty_env_logger::try_init_timed();
let ip_address_bytes = [127, 0, 0, 1];
let ip_address: Ipv4Address = ipv4_addr(127, 0, 0, 1);
info!("ip4 ip_address: {}", ip_address);
info!("ip4 ip_address debug: {:?}", ip_address);
assert_eq!(ip_address.as_bytes(), &ip_address_bytes);
let socket_addr = SocketAddrV4::new_ip4_port(127, 0, 0, 1, 8080);
info!("ip4 socket_addr: {}", socket_addr);
info!("ip4 socket_addr debug: {:?}", socket_addr);
assert_eq!(socket_addr.len(), LEN_V4);
assert_eq!(socket_addr.addr.as_bytes(), &ip_address_bytes);
assert_eq!(
socket_addr.addr.as_bytes(),
&socket_addr.to_vec().as_slice()[0..4]
);
assert_eq!(socket_addr.port, 8080);
}
#[cfg(feature = "proto-ipv6")]
#[test]
fn ipv6_socket_addr() {
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "debug");
}
let _ = pretty_env_logger::try_init_timed();
let ip_address_bytes: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
info!("ip6 ip_address_bytes: {:x?}", ip_address_bytes);
let ip_address: Ipv6Address = ipv6_addr(0, 0, 0, 0, 0, 0, 0, 1);
info!("ip6 ip_address: {}", ip_address);
info!("ip6 ip_address debug: {:?}", ip_address);
assert_eq!(ip_address.as_bytes(), &ip_address_bytes);
let socket_addr = SocketAddrV6::new_ip6_port(0, 0, 0, 0, 0, 0, 0, 1, 8080);
info!("ip6 socket_addr: {}", socket_addr);
info!("ip6 socket_addr debug: {:?}", socket_addr);
assert_eq!(socket_addr.len(), LEN_V6);
assert_eq!(socket_addr.addr.as_bytes(), &ip_address_bytes);
assert_eq!(
socket_addr.addr.as_bytes(),
&socket_addr.to_vec().as_slice()[0..16]
);
assert_eq!(socket_addr.port, 8080);
}
#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
#[test]
fn ipv4_to_std() {
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "debug");
}
let _ = pretty_env_logger::try_init_timed();
let ip_address_bytes = [127, 0, 0, 1];
let ip_address: Ipv4Address = ipv4_addr(127, 0, 0, 1);
let std_ip_address: Ipv4Addr = ip_address.into_std();
assert_eq!(std_ip_address.octets().as_ref(), ip_address_bytes);
let std_ip_address: Result<IpAddr> = IpAddress::Ipv4(ip_address).into_std();
assert_eq!(std_ip_address, Ok(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
let socket_addr_v4 = SocketAddrV4::new(ip_address, 80);
assert_eq!(socket_addr_v4.len(), LEN_V4);
let socket_addr = SocketAddr::V4(socket_addr_v4);
assert_eq!(
Ok(socket_addr),
SocketAddr::new(IpAddress::Ipv4(ip_address), 80)
);
assert_eq!(
Err(Error::UnspecifiedIp),
SocketAddr::new(IpAddress::Unspecified, 80)
);
assert_eq!(socket_addr, SocketAddr::new_v4(ip_address, 80));
assert_eq!(socket_addr, SocketAddr::new_ip4_port(127, 0, 0, 1, 80));
assert_eq!(socket_addr.len(), LEN_V4);
assert_eq!(socket_addr.ip().as_bytes(), ip_address_bytes);
assert_eq!(socket_addr.ip_octets(), ip_address_bytes);
let socket_addr_vec = socket_addr.to_vec();
assert_eq!(socket_addr_vec[0..4], ip_address_bytes);
assert_eq!(port_from_bytes(socket_addr_vec[4], socket_addr_vec[5]), 80);
info!("ip4 socket_addr: {}", socket_addr);
info!("ip4 socket_addr debug: {:?}", socket_addr);
let std_socket_addr: StdSocketAddr = socket_addr.into();
assert!(std_socket_addr.ip().is_loopback());
assert_eq!(std_socket_addr.ip().to_string(), "127.0.0.1".to_string());
assert_eq!(std_socket_addr.port(), socket_addr_v4.port);
assert_eq!(std_socket_addr.port(), socket_addr.port());
}
#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
#[test]
fn ipv6_to_std() {
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "debug");
}
let _ = pretty_env_logger::try_init_timed();
let ip_address_bytes: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
let ip_address: Ipv6Address = ipv6_addr(0, 0, 0, 0, 0, 0, 0, 1);
let std_ip_address: Ipv6Addr = ip_address.into_std();
assert_eq!(std_ip_address.octets().as_ref(), ip_address_bytes);
let std_ip_address: Result<IpAddr> = IpAddress::Ipv6(ip_address).into_std();
assert_eq!(
std_ip_address,
Ok(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)))
);
let socket_addr_v6 = SocketAddrV6::new(ip_address, 80);
assert_eq!(socket_addr_v6.len(), LEN_V6);
let socket_addr = SocketAddr::V6(socket_addr_v6);
assert_eq!(
Ok(socket_addr),
SocketAddr::new(IpAddress::Ipv6(ip_address), 80)
);
assert_eq!(
Err(Error::UnspecifiedIp),
SocketAddr::new(IpAddress::Unspecified, 80)
);
assert_eq!(socket_addr, SocketAddr::new_v6(ip_address, 80));
assert_eq!(
socket_addr,
SocketAddr::new_ip6_port(0, 0, 0, 0, 0, 0, 0, 1, 80)
);
assert_eq!(socket_addr.len(), LEN_V6);
assert_eq!(socket_addr.ip().as_bytes(), ip_address_bytes);
assert_eq!(socket_addr.ip_octets(), ip_address_bytes);
let socket_addr_vec = socket_addr.to_vec();
assert_eq!(socket_addr_vec[0..16], ip_address_bytes);
assert_eq!(
port_from_bytes(socket_addr_vec[16], socket_addr_vec[17]),
80
);
info!("ip6 socket_addr: {}", socket_addr);
info!("ip6 socket_addr debug: {:?}", socket_addr);
let std_socket_addr: StdSocketAddr = socket_addr.into();
assert!(std_socket_addr.ip().is_loopback());
assert_eq!(std_socket_addr.ip().to_string(), "::1".to_string());
assert_eq!(std_socket_addr.port(), socket_addr_v6.port);
assert_eq!(std_socket_addr.port(), socket_addr.port());
}
}