webrtc-stun 0.1.13

A pure Rust implementation of STUN
Documentation
#[cfg(test)]
mod xoraddr_test;

use crate::addr::*;
use crate::attributes::*;
use crate::checks::*;
use crate::errors::*;
use crate::message::*;

use util::Error;

use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

use std::mem;

const WORD_SIZE: usize = mem::size_of::<usize>();

//var supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" // nolint:gochecknoglobals

// fastXORBytes xors in bulk. It only works on architectures that
// support unaligned read/writes.
/*TODO: fn fastXORBytes(dst:&[u8], a:&[u8], b:&[u8]) ->usize {
    let mut n = a.len();
    if b.len() < n {
        n = b.len();
    }

    let w = n / WORD_SIZE;
    if w > 0 {
        let dw = *(*[]uintptr)(unsafe.Pointer(&dst))
        let aw = *(*[]uintptr)(unsafe.Pointer(&a))
        let bw = *(*[]uintptr)(unsafe.Pointer(&b))
        for i := 0; i < w; i++ {
            dw[i] = aw[i] ^ bw[i]
        }
    }

    for i := n - n%WORD_SIZE; i < n; i++ {
        dst[i] = a[i] ^ b[i]
    }

    return n
}*/

fn safe_xorbytes(dst: &mut [u8], a: &[u8], b: &[u8]) -> usize {
    let mut n = a.len();
    if b.len() < n {
        n = b.len();
    }
    if dst.len() < n {
        n = dst.len();
    }
    for i in 0..n {
        dst[i] = a[i] ^ b[i];
    }
    n
}

// xor_bytes xors the bytes in a and b. The destination is assumed to have enough
// space. Returns the number of bytes xor'd.
fn xor_bytes(dst: &mut [u8], a: &[u8], b: &[u8]) -> usize {
    //if supportsUnaligned {
    //	return fastXORBytes(dst, a, b)
    //}
    safe_xorbytes(dst, a, b)
}

// XORMappedAddress implements XOR-MAPPED-ADDRESS attribute.
//
// RFC 5389 Section 15.2
pub struct XORMappedAddress {
    pub ip: IpAddr,
    pub port: u16,
}

impl Default for XORMappedAddress {
    fn default() -> Self {
        XORMappedAddress {
            ip: IpAddr::V4(Ipv4Addr::from(0)),
            port: 0,
        }
    }
}

impl fmt::Display for XORMappedAddress {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let family = match self.ip {
            IpAddr::V4(_) => FAMILY_IPV4,
            IpAddr::V6(_) => FAMILY_IPV6,
        };
        if family == FAMILY_IPV4 {
            write!(f, "{}:{}", self.ip, self.port)
        } else {
            write!(f, "[{}]:{}", self.ip, self.port)
        }
    }
}

impl Setter for XORMappedAddress {
    // AddTo adds XOR-MAPPED-ADDRESS to m. Can return ErrBadIPLength
    // if len(a.IP) is invalid.
    fn add_to(&self, m: &mut Message) -> Result<(), Error> {
        self.add_to_as(m, ATTR_XORMAPPED_ADDRESS)
    }
}

impl Getter for XORMappedAddress {
    // GetFrom decodes XOR-MAPPED-ADDRESS attribute in message and returns
    // error if any. While decoding, a.IP is reused if possible and can be
    // rendered to invalid state (e.g. if a.IP was set to IPv6 and then
    // IPv4 value were decoded into it), be careful.
    //
    // Example:
    //
    //  expectedIP := net.ParseIP("213.141.156.236")
    //  expectedIP.String() // 213.141.156.236, 16 bytes, first 12 of them are zeroes
    //  expectedPort := 21254
    //  addr := &XORMappedAddress{
    //    IP:   expectedIP,
    //    Port: expectedPort,
    //  }
    //  // addr were added to message that is decoded as newMessage
    //  // ...
    //
    //  addr.GetFrom(newMessage)
    //  addr.IP.String()    // 213.141.156.236, net.IPv4Len
    //  expectedIP.String() // d58d:9cec::ffff:d58d:9cec, 16 bytes, first 4 are IPv4
    //  // now we have len(expectedIP) = 16 and len(addr.IP) = 4.
    fn get_from(&mut self, m: &Message) -> Result<(), Error> {
        self.get_from_as(m, ATTR_XORMAPPED_ADDRESS)
    }
}

impl XORMappedAddress {
    // AddToAs adds XOR-MAPPED-ADDRESS value to m as t attribute.
    pub fn add_to_as(&self, m: &mut Message, t: AttrType) -> Result<(), Error> {
        let (family, ip_len, ip) = match self.ip {
            IpAddr::V4(ipv4) => (FAMILY_IPV4, IPV4LEN, ipv4.octets().to_vec()),
            IpAddr::V6(ipv6) => (FAMILY_IPV6, IPV6LEN, ipv6.octets().to_vec()),
        };

        let mut value = vec![0; 32 + 128];
        //value[0] = 0 // first 8 bits are zeroes
        let mut xor_value = vec![0; IPV6LEN];
        xor_value[4..].copy_from_slice(&m.transaction_id.0);
        xor_value[0..4].copy_from_slice(&MAGIC_COOKIE.to_be_bytes());
        value[0..2].copy_from_slice(&family.to_be_bytes());
        value[2..4].copy_from_slice(&(self.port ^ (MAGIC_COOKIE >> 16) as u16).to_be_bytes());
        xor_bytes(&mut value[4..4 + ip_len], &ip, &xor_value);
        m.add(t, &value[..4 + ip_len]);
        Ok(())
    }

    // GetFromAs decodes XOR-MAPPED-ADDRESS attribute value in message
    // getting it as for t type.
    pub fn get_from_as(&mut self, m: &Message, t: AttrType) -> Result<(), Error> {
        let v = m.get(t)?;
        if v.len() <= 4 {
            return Err(ERR_UNEXPECTED_EOF.clone());
        }

        let family = u16::from_be_bytes([v[0], v[1]]);
        if family != FAMILY_IPV6 && family != FAMILY_IPV4 {
            return Err(Error::new(format!("bad value {}", family)));
        }

        check_overflow(
            t,
            v[4..].len(),
            if family == FAMILY_IPV4 {
                IPV4LEN
            } else {
                IPV6LEN
            },
        )?;
        self.port = u16::from_be_bytes([v[2], v[3]]) ^ (MAGIC_COOKIE >> 16) as u16;
        let mut xor_value = vec![0; 4 + TRANSACTION_ID_SIZE];
        xor_value[0..4].copy_from_slice(&MAGIC_COOKIE.to_be_bytes());
        xor_value[4..].copy_from_slice(&m.transaction_id.0);

        if family == FAMILY_IPV6 {
            let mut ip = [0; IPV6LEN];
            xor_bytes(&mut ip, &v[4..], &xor_value);
            self.ip = IpAddr::V6(Ipv6Addr::from(ip));
        } else {
            let mut ip = [0; IPV4LEN];
            xor_bytes(&mut ip, &v[4..], &xor_value);
            self.ip = IpAddr::V4(Ipv4Addr::from(ip));
        };

        Ok(())
    }
}