#![doc(
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
html_root_url = "https://doc.rust-lang.org/eui48/"
)]
extern crate regex;
#[cfg(feature = "rustc-serialize")]
extern crate rustc_serialize;
#[cfg(feature = "serde")]
extern crate serde;
#[cfg(feature = "serde_json")]
extern crate serde_json;
use std::default::Default;
use std::error::Error;
use std::fmt;
use std::str::FromStr;
use regex::Regex;
#[cfg(feature = "rustc-serialize")]
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
#[cfg(feature = "serde")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
pub const EUI48LEN: usize = 6;
pub type Eui48 = [u8; EUI48LEN];
pub const EUI64LEN: usize = 8;
pub type Eui64 = [u8; EUI64LEN];
#[repr(C)]
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct MacAddress {
eui: Eui48,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum MacAddressFormat {
Canonical,
HexString,
DotNotation,
Hexadecimal,
}
#[derive(PartialEq, Eq, Copy, Clone, Debug, Ord, PartialOrd, Hash)]
pub enum ParseError {
InvalidLength(usize),
InvalidByteCount(usize, Eui48),
}
impl MacAddress {
pub const fn new(eui: Eui48) -> MacAddress {
MacAddress { eui }
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
if bytes.len() != EUI48LEN {
return Err(ParseError::InvalidLength(bytes.len()));
}
let mut input: [u8; EUI48LEN] = Default::default();
input[..EUI48LEN].clone_from_slice(&bytes[..EUI48LEN]);
Ok(Self::new(input))
}
pub fn nil() -> MacAddress {
MacAddress { eui: [0; EUI48LEN] }
}
pub fn broadcast() -> MacAddress {
MacAddress {
eui: [0xFF; EUI48LEN],
}
}
pub fn is_nil(&self) -> bool {
self.eui.iter().all(|&b| b == 0)
}
pub fn is_broadcast(&self) -> bool {
self.eui.iter().all(|&b| b == 0xFF)
}
pub fn is_unicast(&self) -> bool {
self.eui[0] & 1 == 0
}
pub fn is_multicast(&self) -> bool {
self.eui[0] & 1 == 1
}
pub fn is_universal(&self) -> bool {
self.eui[0] & 1 << 1 == 0
}
pub fn is_local(&self) -> bool {
self.eui[0] & 1 << 1 == 2
}
pub fn to_canonical(&self) -> String {
format!(
"{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
self.eui[0], self.eui[1], self.eui[2], self.eui[3], self.eui[4], self.eui[5]
)
}
pub fn to_hex_string(&self) -> String {
format!(
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
self.eui[0], self.eui[1], self.eui[2], self.eui[3], self.eui[4], self.eui[5]
)
}
pub fn to_dot_string(&self) -> String {
format!(
"{:02x}{:02x}.{:02x}{:02x}.{:02x}{:02x}",
self.eui[0], self.eui[1], self.eui[2], self.eui[3], self.eui[4], self.eui[5]
)
}
pub fn to_hexadecimal(&self) -> String {
format!(
"0x{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
self.eui[0], self.eui[1], self.eui[2], self.eui[3], self.eui[4], self.eui[5]
)
}
pub fn to_interfaceid(&self) -> String {
format!(
"{:02x}{:02x}:{:02x}ff:fe{:02x}:{:02x}{:02x}",
(self.eui[0] ^ 0x02),
self.eui[1],
self.eui[2],
self.eui[3],
self.eui[4],
self.eui[5]
)
}
pub fn to_link_local(&self) -> String {
format!(
"fe80::{:02x}{:02x}:{:02x}ff:fe{:02x}:{:02x}{:02x}",
(self.eui[0] ^ 0x02),
self.eui[1],
self.eui[2],
self.eui[3],
self.eui[4],
self.eui[5]
)
}
pub fn to_string(&self, fmt: MacAddressFormat) -> String {
match fmt {
MacAddressFormat::Canonical => self.to_canonical(),
MacAddressFormat::HexString => self.to_hex_string(),
MacAddressFormat::DotNotation => self.to_dot_string(),
MacAddressFormat::Hexadecimal => self.to_hexadecimal(),
}
}
pub fn parse_str(s: &str) -> Result<MacAddress, ParseError> {
let re = Regex::new("(0x)?([0-9a-fA-F]{1,2})[:.-]?").unwrap();
let mut eui: Eui48 = [0; EUI48LEN];
match s.len() {
11..=17 => {}
_ => {
return Err(ParseError::InvalidLength(s.len()));
}
}
let mut i = 0;
for caps in re.captures_iter(s) {
if i < EUI48LEN {
let matched_byte = caps.get(2).unwrap().as_str();
eui[i] = u8::from_str_radix(matched_byte, 16).unwrap();
}
i += 1;
}
if i != EUI48LEN {
return Err(ParseError::InvalidByteCount(i, eui));
}
Ok(MacAddress::new(eui))
}
pub fn as_bytes(&self) -> &[u8] {
&self.eui
}
pub fn to_array(&self) -> Eui48 {
self.eui
}
pub fn get_display_format() -> MacAddressFormat {
if cfg!(feature = "disp_hexstring") {
MacAddressFormat::HexString
} else {
MacAddressFormat::Canonical
}
}
}
impl FromStr for MacAddress {
type Err = ParseError;
fn from_str(us: &str) -> Result<MacAddress, ParseError> {
MacAddress::parse_str(us)
}
}
impl Default for MacAddress {
fn default() -> MacAddress {
MacAddress::nil()
}
}
impl fmt::Debug for MacAddress {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"MacAddress(\"{}\")",
self.to_string(MacAddressFormat::HexString)
)
}
}
impl fmt::Display for MacAddress {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let disp_fmt = MacAddress::get_display_format();
write!(f, "{}", self.to_string(disp_fmt))
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ParseError::InvalidLength(found) => write!(
f,
"Invalid length; expecting 11 to 17 chars, found {}",
found
),
ParseError::InvalidByteCount(found, eui) => write!(
f,
"Invalid byte count; Matched `{}` bytes ({:?})",
found,
&eui[..found]
),
}
}
}
impl Error for ParseError {
fn description(&self) -> &str {
"MacAddress parse error"
}
}
#[cfg(feature = "rustc-serialize")]
impl Encodable for MacAddress {
fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
let disp_fmt = MacAddress::get_display_format();
e.emit_str(&self.to_string(disp_fmt))
}
}
#[cfg(feature = "rustc-serialize")]
impl Decodable for MacAddress {
fn decode<D: Decoder>(d: &mut D) -> Result<MacAddress, D::Error> {
let string = d.read_str()?;
string.parse().map_err(|err| d.error(&format!("{}", err)))
}
}
#[cfg(all(feature = "serde", not(feature = "serde_bytes")))]
impl Serialize for MacAddress {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let disp_fmt = MacAddress::get_display_format();
serializer.serialize_str(&self.to_string(disp_fmt))
}
}
#[cfg(feature = "serde_bytes")]
impl Serialize for MacAddress {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(self.as_bytes())
}
}
#[cfg(all(feature = "serde", not(feature = "serde_bytes")))]
impl<'de> Deserialize<'de> for MacAddress {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct MacAddressVisitor;
impl<'de> de::Visitor<'de> for MacAddressVisitor {
type Value = MacAddress;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a string representation of a MAC address")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
value.parse().map_err(|err| E::custom(&format!("{}", err)))
}
}
deserializer.deserialize_str(MacAddressVisitor)
}
}
#[cfg(feature = "serde_bytes")]
impl<'de> Deserialize<'de> for MacAddress {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct MacAddressVisitor;
impl<'de> de::Visitor<'de> for MacAddressVisitor {
type Value = MacAddress;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "6-element byte array")
}
fn visit_bytes<E: de::Error>(self, value: &[u8]) -> Result<Self::Value, E> {
MacAddress::from_bytes(value).map_err(|_| E::invalid_length(value.len(), &self))
}
}
deserializer.deserialize_bytes(MacAddressVisitor)
}
}
#[cfg(test)]
mod tests {
use super::{Eui48, MacAddress, MacAddressFormat, ParseError};
#[test]
fn test_new() {
let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
let mac = MacAddress::new(eui);
assert!(mac.eui[0..5] == eui[0..5]);
}
#[test]
fn test_from_bytes() {
assert_eq!(
"12:34:56:ab:cd:ef",
MacAddress::from_bytes(&[0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF])
.unwrap()
.to_hex_string()
);
assert!(MacAddress::from_bytes(&[0x12, 0x34, 0x56, 0xAB, 0xCD]).is_err());
}
#[test]
fn test_nil() {
let nil = MacAddress::nil();
let not_nil = MacAddress::broadcast();
assert_eq!("00:00:00:00:00:00", nil.to_hex_string());
assert!(nil.is_nil());
assert!(!not_nil.is_nil());
}
#[test]
fn test_default() {
let default = MacAddress::default();
assert!(default.is_nil());
}
#[test]
fn test_broadcast() {
let broadcast = MacAddress::broadcast();
let not_broadcast = MacAddress::nil();
assert_eq!("ff:ff:ff:ff:ff:ff", broadcast.to_hex_string());
assert!(broadcast.is_broadcast());
assert!(!not_broadcast.is_broadcast());
}
#[test]
fn test_is_nil() {
let nil = MacAddress::nil();
let not_nil = MacAddress::parse_str("01:00:5E:AB:CD:EF").unwrap();
assert!(nil.is_nil());
assert!(!not_nil.is_nil());
}
#[test]
fn test_is_broadcast() {
let broadcast = MacAddress::broadcast();
let not_broadcast = MacAddress::parse_str("01:00:5E:AB:CD:EF").unwrap();
assert!(broadcast.is_broadcast());
assert!(!not_broadcast.is_broadcast());
}
#[test]
fn test_is_unicast() {
let mac_u = MacAddress::parse_str("FE:00:5E:AB:CD:EF").unwrap();
let mac_m = MacAddress::parse_str("01:00:5E:AB:CD:EF").unwrap();
assert!(mac_u.is_unicast());
assert!(!mac_m.is_unicast());
assert_eq!("fe:00:5e:ab:cd:ef", mac_u.to_hex_string());
let mac = MacAddress::parse_str("FF:00:5E:AB:CD:EF").unwrap();
assert!(!mac.is_unicast());
assert_eq!("ff:00:5e:ab:cd:ef", mac.to_hex_string());
assert!(MacAddress::nil().is_unicast());
assert!(!MacAddress::broadcast().is_unicast());
}
#[test]
fn test_is_multicast() {
let mac_u = MacAddress::parse_str("FE:00:5E:AB:CD:EF").unwrap();
let mac_m = MacAddress::parse_str("01:00:5E:AB:CD:EF").unwrap();
assert!(!mac_u.is_multicast());
assert!(mac_m.is_multicast());
assert!(!MacAddress::nil().is_multicast());
assert_eq!("01:00:5e:ab:cd:ef", mac_m.to_hex_string());
let mac = MacAddress::parse_str("F0:00:5E:AB:CD:EF").unwrap();
assert!(!mac.is_multicast());
assert_eq!("f0:00:5e:ab:cd:ef", mac.to_hex_string());
assert!(MacAddress::broadcast().is_multicast());
}
#[test]
fn test_is_universal() {
let universal = MacAddress::parse_str("11:24:56:AB:CD:EF").unwrap();
let not_universal = MacAddress::parse_str("12:24:56:AB:CD:EF").unwrap();
assert!(universal.is_universal());
assert!(!not_universal.is_universal());
assert_eq!("11:24:56:ab:cd:ef", universal.to_hex_string());
}
#[test]
fn test_is_local() {
let local = MacAddress::parse_str("06:34:56:AB:CD:EF").unwrap();
let not_local = MacAddress::parse_str("00:34:56:AB:CD:EF").unwrap();
assert!(local.is_local());
assert!(!not_local.is_local());
assert_eq!("06:34:56:ab:cd:ef", local.to_hex_string());
}
#[test]
fn test_to_canonical() {
let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
let mac = MacAddress::new(eui);
assert_eq!("12-34-56-ab-cd-ef", mac.to_canonical());
}
#[test]
fn test_to_hex_string() {
let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
let mac = MacAddress::new(eui);
assert_eq!("12:34:56:ab:cd:ef", mac.to_hex_string());
}
#[test]
fn test_to_dot_string() {
let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
let mac = MacAddress::new(eui);
assert_eq!("1234.56ab.cdef", mac.to_dot_string());
}
#[test]
fn test_to_hexadecimal() {
let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
let mac = MacAddress::new(eui);
assert_eq!("0x123456abcdef", mac.to_hexadecimal());
}
#[test]
fn test_to_interfaceid() {
let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
let mac = MacAddress::new(eui);
assert_eq!("1034:56ff:feab:cdef", mac.to_interfaceid());
}
#[test]
fn test_to_link_local() {
let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
let mac = MacAddress::new(eui);
assert_eq!("fe80::1034:56ff:feab:cdef", mac.to_link_local());
}
#[test]
fn test_to_string() {
let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
let mac = MacAddress::new(eui);
assert_eq!(
"0x123456abcdef",
mac.to_string(MacAddressFormat::Hexadecimal)
);
assert_eq!(
"1234.56ab.cdef",
mac.to_string(MacAddressFormat::DotNotation)
);
assert_eq!(
"12:34:56:ab:cd:ef",
mac.to_string(MacAddressFormat::HexString)
);
assert_eq!(
"12-34-56-ab-cd-ef",
mac.to_string(MacAddressFormat::Canonical)
);
}
#[test]
fn test_parse_str() {
use super::ParseError::*;
assert_eq!(
"0x123456abcdef",
MacAddress::parse_str("0x123456ABCDEF")
.unwrap()
.to_hexadecimal()
);
assert_eq!(
"1234.56ab.cdef",
MacAddress::parse_str("1234.56AB.CDEF")
.unwrap()
.to_dot_string()
);
assert_eq!(
"12:34:56:ab:cd:ef",
MacAddress::parse_str("12:34:56:AB:CD:EF")
.unwrap()
.to_hex_string()
);
assert_eq!(
"12-34-56-ab-cd-ef",
MacAddress::parse_str("12-34-56-AB-CD-EF")
.unwrap()
.to_canonical()
);
assert_eq!(
"12-34-56-78-90-0a",
MacAddress::parse_str("0x1234567890A")
.unwrap()
.to_canonical()
);
assert_eq!(
"12-34-56-ab-cd-ef",
MacAddress::parse_str("123456ABCDEF")
.unwrap()
.to_canonical()
);
assert_eq!(
"00-00-00-00-00-00",
MacAddress::parse_str("!0x00000000000")
.unwrap()
.to_canonical()
);
assert_eq!(
"00-00-00-00-00-00",
MacAddress::parse_str("0x00000000000!")
.unwrap()
.to_canonical()
);
assert_eq!(MacAddress::parse_str(""), Err(InvalidLength(0)));
assert_eq!(MacAddress::parse_str("0"), Err(InvalidLength(1)));
assert_eq!(
MacAddress::parse_str("1234567890ABCD"),
Err(InvalidByteCount(7, [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB]))
);
assert_eq!(
MacAddress::parse_str("1234567890ABCDEF"),
Err(InvalidByteCount(8, [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB]))
);
assert_eq!(
MacAddress::parse_str("01234567890ABCDEF"),
Err(InvalidByteCount(9, [0x01, 0x23, 0x45, 0x67, 0x89, 0x0A]))
);
assert_eq!(
MacAddress::parse_str("0x1234567890ABCDE"),
Err(InvalidByteCount(8, [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB]))
);
assert_eq!(
MacAddress::parse_str("0x00:01:02:03:"),
Err(InvalidByteCount(4, [0, 1, 2, 3, 0, 0]))
);
assert_eq!(
MacAddress::parse_str("0x00:01:02:03:04:"),
Err(InvalidByteCount(5, [0, 1, 2, 3, 4, 0]))
);
assert_eq!(
MacAddress::parse_str("::::::::::::::"),
Err(InvalidByteCount(0, [0, 0, 0, 0, 0, 0]))
);
assert_eq!(
MacAddress::parse_str(":::::::::::::::::"),
Err(InvalidByteCount(0, [0, 0, 0, 0, 0, 0]))
);
assert_eq!(
MacAddress::parse_str("0x0x0x0x0x0x0x"),
Err(InvalidByteCount(4, [0, 0, 0, 0, 0, 0]))
);
}
#[test]
fn test_as_bytes() {
let mac = MacAddress::broadcast();
let bytes = mac.as_bytes();
assert!(bytes.len() == 6);
assert!(bytes.iter().all(|&b| b == 0xFF));
}
#[test]
fn test_compare() {
let m1 = MacAddress::nil();
let m2 = MacAddress::broadcast();
assert_eq!(m1, m1);
assert_eq!(m2, m2);
assert_ne!(m1, m2);
assert_ne!(m2, m1);
}
#[test]
fn test_clone() {
let m1 = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
let m2 = m1;
assert_eq!(m1, m1);
assert_eq!(m2, m2);
assert_eq!(m1, m2);
assert_eq!(m2, m1);
}
#[test]
fn test_serialize() {
use rustc_serialize::json;
let mac = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
if cfg!(feature = "disp_hexstring") {
assert_eq!("\"12:34:56:ab:cd:ef\"", json::encode(&mac).unwrap());
} else {
assert_eq!("\"12-34-56-ab-cd-ef\"", json::encode(&mac).unwrap());
}
}
#[test]
fn test_deserialize() {
use rustc_serialize::json;
let mac = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
if cfg!(feature = "disp_hexstring") {
let d = "\"12:34:56:AB:CD:EF\"";
assert_eq!(mac, json::decode(&d).unwrap());
} else {
let d = "\"12-34-56-AB-CD-EF\"";
assert_eq!(mac, json::decode(&d).unwrap());
}
}
#[test]
fn test_serialize_roundtrip() {
use rustc_serialize::json;
let m1 = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
let s = json::encode(&m1).unwrap();
let m2 = json::decode(&s).unwrap();
assert_eq!(m1, m2);
}
#[test]
fn test_fmt_debug() {
let mac = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
assert_eq!(
"MacAddress(\"12:34:56:ab:cd:ef\")".to_owned(),
format!("{:?}", mac)
);
}
#[test]
fn test_fmt() {
let mac = MacAddress::parse_str("0x123456ABCDEF").unwrap();
match MacAddress::get_display_format() {
MacAddressFormat::HexString => {
assert_eq!("12:34:56:ab:cd:ef".to_owned(), format!("{}", mac))
}
_ => assert_eq!("12-34-56-ab-cd-ef".to_owned(), format!("{}", mac)),
};
}
#[test]
fn test_fmt_parse_errors() {
assert_eq!(
"Err(InvalidByteCount(7, [18, 52, 86, 171, 205, 239]))".to_owned(),
format!("{:?}", MacAddress::parse_str("123456ABCDEF1"))
);
assert_eq!(
"Err(InvalidLength(19))",
format!("{:?}", MacAddress::parse_str("12##45#67#89#AB#C#D"))
);
}
#[test]
#[cfg(feature = "serde_json")]
fn test_serde_json_serialize() {
use serde_json;
let serialized =
serde_json::to_string(&MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap()).unwrap();
if cfg!(feature = "disp_hexstring") {
assert_eq!("\"12:34:56:ab:cd:ef\"", serialized);
} else {
assert_eq!("\"12-34-56-ab-cd-ef\"", serialized);
}
}
#[test]
#[cfg(feature = "serde_json")]
fn test_serde_json_deserialize() {
use serde_json;
let mac = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
let deserialized: MacAddress = serde_json::from_str("\"12-34-56-AB-CD-EF\"").unwrap();
assert_eq!(deserialized, mac);
}
#[test]
#[should_panic(expected = "Invalid length; expecting 11 to 17 chars, found 2")]
#[cfg(feature = "serde_json")]
fn test_serde_json_deserialize_panic() {
let _should_panic: MacAddress = serde_json::from_str("\"12\"").unwrap();
}
#[test]
#[cfg(feature = "serde_bytes")]
fn test_serde_bytes_serialization_roundtrip() {
use bincode;
let mac = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
let mut buffer = Vec::new();
bincode::serialize_into(&mut buffer, &mac).unwrap();
let deserialized: MacAddress = bincode::deserialize_from(&*buffer).unwrap();
assert_eq!(deserialized, mac);
}
#[test]
fn test_macaddressformat_derive() {
assert_eq!(MacAddressFormat::HexString, MacAddressFormat::HexString);
assert_ne!(MacAddressFormat::HexString, MacAddressFormat::Canonical);
}
#[test]
fn test_parseerror_fmt() {
assert_eq!(
"Invalid length; expecting 11 to 17 chars, found 2".to_owned(),
format!("{}", ParseError::InvalidLength(2))
);
assert_eq!(
"Invalid length; expecting 11 to 17 chars, found 2".to_owned(),
ParseError::InvalidLength(2).to_string()
);
}
#[test]
fn test_to_array() {
let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
let mac = MacAddress::new(eui);
assert_eq!(eui, MacAddress::new(eui).to_array());
assert_eq!(mac, MacAddress::new(mac.to_array()));
}
}