Skip to content

Commit

Permalink
Merge rust-bitcoin#968: Refactor address byte swapping
Browse files Browse the repository at this point in the history
07c7530 Refactor address byte swapping (Tobin C. Harding)

Pull request description:

  Refactor address byte swapping

  When encoding a `network::Address` two of the fields are encoded
  big-endian instead of little-endian as is done by `consensus_encode`. In
  order to achieve this we have a helper function `addr_to_be` that swaps
  the bytes. This function is miss-named because it is not converting to a
  specific endian-ness (which implies different behaviour on machines with
  different endian-ness) but is reversing the byte order irrespective of
  the underlying architecture.

  - Remove function `addr_to_be`
  - Inline the endian-ness code when encoding an address
  - Remove TODO and use `to_be_bytes` when encoding port
  - Add a function for reading big-endian bytes `read_be_address`
  - Use `read_be_address` when decoding `Address` and `Addrv2`

  Refactor only, no logic changes. Code path is already covered by
  unit tests.

ACKs for top commit:
  apoelstra:
    ACK 07c7530
  Kixunil:
    ACK 07c7530

Tree-SHA512: 186bc86512e264a7b306f3bc2e18d1619f3cd84fc54412148cfc2663e8d6e9616ea9e2fe19eafec72d76cc11367a9b39cac2b73210d9e43eb8f453bd253b33de
  • Loading branch information
apoelstra committed May 24, 2022
2 parents 0e82376 + 07c7530 commit 324fa0f
Showing 1 changed file with 25 additions and 20 deletions.
45 changes: 25 additions & 20 deletions src/network/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,25 +67,19 @@ impl Address {
}
}

fn addr_to_be(addr: [u16; 8]) -> [u16; 8] {
// consensus_encode always encodes in LE, and we want to encode in BE.
// this utility fn swap bytes before encoding so that the encoded result will be BE
let mut result = addr;
for word in &mut result {
*word = word.swap_bytes();
}
result
}

impl Encodable for Address {
#[inline]
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, io::Error> {
let len = self.services.consensus_encode(&mut s)?
+ addr_to_be(self.address).consensus_encode(&mut s)?
let mut len = self.services.consensus_encode(&mut s)?;

for word in &self.address {
s.write_all(&word.to_be_bytes())?;
len += 2;
}

s.write_all(&self.port.to_be_bytes())?;
len += 2;

// consensus_encode always encodes in LE, and we want to encode in BE.
//TODO `len += io::Write::write(&mut e, &self.port.to_be_bytes())?;` when MSRV >= 1.32
+ self.port.swap_bytes().consensus_encode(s)?;
Ok(len)
}
}
Expand All @@ -95,12 +89,24 @@ impl Decodable for Address {
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
Ok(Address {
services: Decodable::consensus_decode(&mut d)?,
address: addr_to_be(Decodable::consensus_decode(&mut d)?),
address: read_be_address(&mut d)?,
port: u16::swap_bytes(Decodable::consensus_decode(d)?)
})
}
}

/// Read a big-endian address from reader.
fn read_be_address<R: io::Read>(mut r: R) -> Result<[u16; 8], encode::Error> {
let mut address = [0u16; 8];
let mut buf = [0u8; 2];

for word in &mut address {
io::Read::read_exact(&mut r, &mut buf)?;
*word = u16::from_be_bytes(buf)
}
Ok(address)
}

impl fmt::Debug for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ipv6 = Ipv6Addr::from(self.address);
Expand Down Expand Up @@ -180,7 +186,7 @@ impl Decodable for AddrV2 {
if len != 16 {
return Err(encode::Error::ParseFailed("Invalid IPv6 address"));
}
let addr: [u16; 8] = addr_to_be(Decodable::consensus_decode(&mut d)?);
let addr: [u16; 8] = read_be_address(&mut d)?;
if addr[0..3] == ONION {
return Err(encode::Error::ParseFailed("OnionCat address sent with IPv6 network id"));
}
Expand Down Expand Up @@ -214,12 +220,11 @@ impl Decodable for AddrV2 {
if len != 16 {
return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
}
let addr: [u16; 8] = Decodable::consensus_decode(&mut d)?;
let addr: [u16; 8] = read_be_address(&mut d)?;
// check the first byte for the CJDNS marker
if addr[0] as u8 != 0xFC {
if addr[0] != u16::from_be_bytes([0xFC, 0x00]) {
return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
}
let addr = addr_to_be(addr);
AddrV2::Cjdns(Ipv6Addr::new(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7]))
},
_ => {
Expand Down

0 comments on commit 324fa0f

Please sign in to comment.