Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mdns] - Support for long mDNS names (Bug #1232) #1287

Merged
merged 13 commits into from
Oct 31, 2019
Merged

Conversation

peat
Copy link
Contributor

@peat peat commented Oct 24, 2019

Hey folks,

I believe this adds support for long mDNS names, as reported in issue #1232

There are a couple of additional changes that I made in order to test the scenario where long mDNS names are encountered, so I'd love to get feedback about unintended consequences:

  • I added the std feature for ring in the top-level Cargo.toml. I wasn't able to build or test within the mdns directory without it (eg: cd misc/mdns; cargo test would fail on ring error traits).
  • I added support for creating PeerId from the Identity multihash. It's in line with the spec, but I'm not familiar with the nuances. This was done so that I could generate long mDNS names for tests.

Cheers!

@tomaka
Copy link
Member

tomaka commented Oct 25, 2019

I agree that should have this change, but I'm a bit confused by the specs. The fact that you have to modify the code of PeerId in order to be able to generate the long PeerIds that are relevant for this issue kind of shows that this couldn't happen in reality.

@peat
Copy link
Contributor Author

peat commented Oct 25, 2019

I don't think the Rust implementation would ever create an oversized DNS label because it only responds to queries with the locally generated SHA2256 PeerId -- so I'm happy to yank the code for using the Identity multihashes in the query response use case.

I would like to retain the code in service.rs -- the parsing is a little cleaner, and I think it's good to have the capability to parse the long format mDNS query responses for interop with other implementations.

@tomaka, would you be more comfortable seeing those changes, given your concerns about the spec being a bit loose?

@tomaka
Copy link
Member

tomaka commented Oct 25, 2019

would you be more comfortable seeing those changes, given your concerns about the spec being a bit loose?

The scope of the changes is good (IMO). I'll probably review early next week.

core/Cargo.toml Outdated Show resolved Hide resolved
const MAX_INLINE_KEY_LENGTH: usize = 42;
//
// Note: see `from_public_key` for how this value will be used in the future.
// const MAX_INLINE_KEY_LENGTH: usize = 42;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 👍

@@ -211,6 +203,37 @@ fn append_u16(out: &mut Vec<u8>, value: u16) {
out.push((value & 0xff) as u8);
}

/// If a peer ID is longer than 63 characters, split it into segments to
/// be compatible with RFC 1035.
fn segment_peer_id(peer_id: &String) -> String {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fn segment_peer_id(peer_id: &String) -> String {
fn segment_peer_id(peer_id: String) -> String {

It might be premature optimization, but we could add some pass-through:

if peer_id.as_bytes().len() <= 63 { return peer_id }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated 👍

/// If a peer ID is longer than 63 characters, split it into segments to
/// be compatible with RFC 1035.
fn segment_peer_id(peer_id: &String) -> String {
// This will only perform one allocation except in extreme circumstances.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can replace the function's body with something like this:

Suggested change
// This will only perform one allocation except in extreme circumstances.
String::from_utf8(peer_id.as_bytes().chunks(63).join(&b'.')).expect("we copy from a UTF8 string; qed")

Copy link
Contributor Author

@peat peat Oct 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that .chunks can't .join (in 1.38.0) ... I went through several iterations to see if I could make that work elegantly, and ended up with the for ... in loop for clarity and minimal number of allocations.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked further into this as I was curious, I guess not because it really matters in this case. I am sorry if this is just useless noise to the discussion.

Under the assumption that the string only contains ASCII characters, why not do:

    let b = peer_id
        .as_bytes()
        .chunks(MAX_LABEL_LENGTH)
        .collect::<Vec<_>>()
        .join(&b'.');
    String::from_utf8(b).expect("we copy from a UTF8 string; qed")

Using a for-loop and manually preallocating the right amount of memory seems like an early optimization to me. I have benchmarked this here. Using iterators with join allows one to not copy the characters one by one as in the for loop, but instead in chunks (See https://doc.rust-lang.org/src/alloc/slice.rs.html#664).

Running the above linked benchmarks in criterion shows that the above would be twice as fast (on my machine) as the for loop. In addition I find it easier to comprehend.

@peat @tomaka what do you think?

// This will only perform one allocation except in extreme circumstances.
let mut out = String::with_capacity(peer_id.len() + 8);

for (idx, chr) in peer_id.chars().enumerate() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line bothers me a bit. We know the string only contains ASCII characters, so I guess it's fine. I would have preferred using bytes.

@tomaka tomaka merged commit eb7b7bd into libp2p:master Oct 31, 2019
tomaka pushed a commit to tomaka/libp2p-rs that referenced this pull request Nov 6, 2019
* Dead code -- commenting out with a note referencing future implementation

* Adding "std" feature so that cargo can build in other directories (notably `misc/mdns`, so that I could run these tests)

* Permitting `PeerID` to be built from an `Identity` multihash

* The length limit for DNS labels is 63 characters, as per RFC1035

* Allocates the vector with capacity for the service name plus additional QNAME encoding bytes

* Added support for encoding/decoding peer IDs with an encoded length greater than 63 characters

* Removing "std" from ring features

Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com>

* Retaining MAX_INLINE_KEY_LENGTH with comment about future usage

* `segment_peer_id` consumes `peer_id` ... plus an early return for IDs that don't need to be segmented

* Fixing logic
tomaka added a commit that referenced this pull request Nov 6, 2019
* Implement Debug for (ed25519|secp256k1)::(Keypair|SecretKey) (#1285)

* Fix possible arithmetic overflow in libp2p-kad. (#1291)

When the number of active queries exceeds the (internal)
JOBS_MAX_QUERIES limit, which is only supposed to bound
the number of concurrent queries relating to background
jobs, an arithmetic overflow occurs. This is fixed by
using saturating subtraction.

* protocols/plaintext: Add example on how to upgrade with PlainTextConfig1 (#1286)

* [mdns] - Support for long mDNS names (Bug #1232) (#1287)

* Dead code -- commenting out with a note referencing future implementation

* Adding "std" feature so that cargo can build in other directories (notably `misc/mdns`, so that I could run these tests)

* Permitting `PeerID` to be built from an `Identity` multihash

* The length limit for DNS labels is 63 characters, as per RFC1035

* Allocates the vector with capacity for the service name plus additional QNAME encoding bytes

* Added support for encoding/decoding peer IDs with an encoded length greater than 63 characters

* Removing "std" from ring features

Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com>

* Retaining MAX_INLINE_KEY_LENGTH with comment about future usage

* `segment_peer_id` consumes `peer_id` ... plus an early return for IDs that don't need to be segmented

* Fixing logic

* Bump most dependencies (#1268)

* Bump most dependencies

This actually builds 😊.

* Bump all dependencies

Includes the excellent work of @rschulman in #1265.

* Remove use of ed25519-dalek fork

* Monomorphize more dependencies

* Add compatibility hack for rand

Cargo allows a crate to depend on multiple versions of another, but
`cargo-web` panics in that situation.  Use a wrapper crate to work
around the panic.

* Use @tomaka’s idea for using a newer `rand`

instead of my own ugly hack.

* Switch to Parity master

as its dependency-bumping PR has been merged.

* Update some depenendencies again

* Remove unwraps and `#[allow(deprecated)]`.

* Remove spurious changes to dependencies

Bumping minor or patch versions is not needed, and increases likelyhood
of merge conflicts.

* Remove some redundant Cargo.toml changes

* Replace a retry loop with an expect

`ed25519::SecretKey::from_bytes` will never fail for 32-byte inputs.

* Revert changes that don’t belong in this PR

* Remove using void to bypass ICE (#1295)

* Publish 0.13.0 (#1294)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants