bendy icon indicating copy to clipboard operation
bendy copied to clipboard

Trying to deserialize bytes as SocketAddr

Open nrempel opened this issue 5 years ago • 1 comments

Hi there,

Thanks for the handy library!

I'm trying to deserialize an ip block from the Bittorrent DHT according to BEP 5.

I can use the following to deserialize the ip block into a byte vector:

#[derive(Debug, Deserialize)]
struct KRPCMessage {
    #[serde(rename = "t")]
    transaction_id: String,
    #[serde(rename = "y")]
    type_code: String,
    #[serde(rename = "v")]
    version: Option<String>,
    #[serde(rename = "r")]
    response: HashMap<String, ByteBuf>,
    #[serde(with = "serde_bytes")]
    ip: Vec<u8>,
}
from_bytes::<KRPCMessage>(&data).unwrap();

This works and gives me:

KRPCMessage { transaction_id: "aa", type_code: "r", version: None, response: {"id": [50, 245, 78, 105, 115, 81, 255, 74, 236, 41, 205, 186, 171, 242, 251, 227, 70, 124, 194, 103]}, ip: [70, 66, 178, 80, 26, 225] }

But what I would like to do is deserialize the ip block into a std::net::SocketAddr struct. I've tried various approaches including using the "new type" pattern over SocketAddr but I can't seem to figure out how to get it to work:


fn deserialize<'de, D>(deserializer: D) -> Result<SocketAddr, D::Error>
where
    D: Deserializer<'de>,
{
    Ok(SocketAddr::new(
        IpAddr::V4(Ipv4Addr::new(67, 215, 246, 10)),
        6881,
    ))
}

#[derive(Debug, Deserialize)]
struct KRPCMessage {
    #[serde(rename = "t")]
    transaction_id: String,
    #[serde(rename = "y")]
    type_code: String,
    #[serde(rename = "v")]
    version: Option<String>,
    #[serde(rename = "r")]
    response: HashMap<String, ByteBuf>,
    #[serde(with = "self")]
    ip: SocketAddr,
}
from_bytes::<KRPCMessage>(&data).unwrap();

I always get the error:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Decode(Error { context: None, error: MalformedContent(Utf8Error { valid_up_to: 2, error_len: Some(1) }) })'

It seems that it always try to decode the block as a utf-8 string.

Can anyone guide me on how to accomplish this? Please let me know if there any obvious errors—I'm new to Rust.

nrempel avatar Dec 27 '20 18:12 nrempel

Hi,

It seems that the problem is that serde's deserializer for SocketAddr expects a string of the form 1.2.3.4:5678, whereas BEP-0005 uses what it calls "compact IP address/port info", which is just 6 binary bytes. You'll need to write a custom serializer and deserializer that know how to work with this format and then tell serde to use it with the #[serde(with=...)] attribute (documented at https://serde.rs/field-attrs.html).

You may also be happier with the bendy-specific API; while it's a little bit more verbose than serde is when everything works, it is much easier to write custom encoding and decoding functions using the bendy API.

I hope that that helps.

thequux avatar Jan 07 '21 11:01 thequux