use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use bytes::{BufMut, Bytes, BytesMut};
pub const REQUEST_LEN: usize = 0;
pub const RESPONSE_LEN: usize = 19;
const FAMILY_V4: u8 = 4;
const FAMILY_V6: u8 = 6;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReflexMsg {
Request,
Response(SocketAddr),
}
#[inline]
pub fn encode_request() -> Bytes {
Bytes::new()
}
pub fn encode_response(observed: SocketAddr) -> Bytes {
let mut buf = BytesMut::with_capacity(RESPONSE_LEN);
match observed {
SocketAddr::V4(v4) => {
buf.put_u8(FAMILY_V4);
let mut addr = [0u8; 16];
addr[..4].copy_from_slice(&v4.ip().octets());
buf.put_slice(&addr);
buf.put_u16(v4.port());
}
SocketAddr::V6(v6) => {
buf.put_u8(FAMILY_V6);
buf.put_slice(&v6.ip().octets());
buf.put_u16(v6.port());
}
}
debug_assert_eq!(buf.len(), RESPONSE_LEN);
buf.freeze()
}
pub fn decode(payload: &[u8]) -> Option<ReflexMsg> {
match payload.len() {
REQUEST_LEN => Some(ReflexMsg::Request),
RESPONSE_LEN => {
let family = payload[0];
let addr_bytes: [u8; 16] = payload[1..17].try_into().ok()?;
let port = u16::from_be_bytes([payload[17], payload[18]]);
let ip = match family {
FAMILY_V4 => IpAddr::V4(Ipv4Addr::new(
addr_bytes[0],
addr_bytes[1],
addr_bytes[2],
addr_bytes[3],
)),
FAMILY_V6 => IpAddr::V6(Ipv6Addr::from(addr_bytes)),
_ => return None,
};
Some(ReflexMsg::Response(SocketAddr::new(ip, port)))
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn request_is_empty() {
assert_eq!(encode_request().len(), REQUEST_LEN);
assert_eq!(REQUEST_LEN, 0);
}
#[test]
fn response_roundtrip_ipv4() {
let addr: SocketAddr = "192.0.2.45:9001".parse().unwrap();
let encoded = encode_response(addr);
assert_eq!(encoded.len(), RESPONSE_LEN);
match decode(&encoded) {
Some(ReflexMsg::Response(out)) => assert_eq!(out, addr),
other => panic!("expected Response, got {other:?}"),
}
}
#[test]
fn response_roundtrip_ipv6() {
let addr: SocketAddr = "[2001:db8::1]:443".parse().unwrap();
let encoded = encode_response(addr);
assert_eq!(encoded.len(), RESPONSE_LEN);
match decode(&encoded) {
Some(ReflexMsg::Response(out)) => assert_eq!(out, addr),
other => panic!("expected Response, got {other:?}"),
}
}
#[test]
fn response_layout_ipv4_zero_padded_upper_bytes() {
let addr: SocketAddr = "10.0.0.1:80".parse().unwrap();
let encoded = encode_response(addr);
assert_eq!(encoded[0], 4, "family byte");
assert_eq!(&encoded[1..5], &[10, 0, 0, 1], "IPv4 in low 4 bytes");
assert_eq!(&encoded[5..17], &[0u8; 12], "upper 12 bytes zeroed");
assert_eq!(&encoded[17..19], &80u16.to_be_bytes(), "port big-endian");
}
#[test]
fn empty_payload_is_request() {
assert_eq!(decode(&[]), Some(ReflexMsg::Request));
}
#[test]
fn unknown_length_rejects() {
assert!(decode(&[0xff]).is_none());
assert!(decode(&[0; 18]).is_none());
assert!(decode(&[0; 20]).is_none());
}
#[test]
fn unknown_family_rejects() {
let mut payload = vec![0u8; RESPONSE_LEN];
payload[0] = 7; assert!(decode(&payload).is_none());
}
}