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

Add libp2p-mdns #590

Merged
merged 20 commits into from
Nov 24, 2018
Merged

Add libp2p-mdns #590

merged 20 commits into from
Nov 24, 2018

Conversation

tomaka
Copy link
Member

@tomaka tomaka commented Oct 27, 2018

Implements libp2p/specs#80

Allows automatically discovering other nodes on the same local network.

@tomaka
Copy link
Member Author

tomaka commented Oct 28, 2018

Disabled libp2p-mdns on emscripten, as it's not available in the browser.

// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

Copy link
Contributor

Choose a reason for hiding this comment

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

Module docs?

misc/mdns/src/dns.rs Show resolved Hide resolved
append_txt_record(&mut out, &peer_id_bytes, ttl, Some(&txt_to_send_bytes[..]))?;
}

if out.len() > 9000 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is 9000 from the spec or simply an empirically found reasonable upper limit?

Copy link
Member Author

Choose a reason for hiding this comment

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

From the specs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Then I'd make it a const. Or a comment.

cmp::min(secs, From::from(u32::max_value())) as u32
}

/// Appends a big-endian u32 to `out`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do you prefer to not use the byteorder crate for the append_*methods?

Copy link
Member Author

@tomaka tomaka Oct 29, 2018

Choose a reason for hiding this comment

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

The methods of byteorder return a Result, which is not great.

Copy link
Contributor

Choose a reason for hiding this comment

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

How about:

fn append_u32(out: &mut Vec<u8>, value: u32) {
    let n = out.len();
    out.resize(n + 4, 0);
    BE::write_u32(&mut out[n ..], value)
}

BE is an alias for byteorder::BigEndian.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, to me the point of using byteorder is to make the code more straight-forward to read, and that doesn't.

let builder = net2::UdpBuilder::new_v4()?;
builder.reuse_address(true)?;
platform_specific(&builder)?;
builder.bind(("0.0.0.0", 5353))?
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider using a const MDNS_PORT?

Copy link
Member Author

Choose a reason for hiding this comment

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

mDNS is an old and well-established standard, and there's 0 chance that the port changes in the future.

Copy link
Contributor

Choose a reason for hiding this comment

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

It was more of a readability concern – easier to read than just the number imo.

Copy link
Contributor

Choose a reason for hiding this comment

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

RFC 6762 states that "[...] using UDP source port 5353 signals the presence of a fully compliant Multicast DNS querier [...]" which this crate is not. Queriers issuing one-shot queries must not use source port 5353.

let socket = UdpSocket::from_std(socket, &Handle::default())?;
socket.set_multicast_loop_v4(true)?;
socket.set_multicast_ttl_v4(255)?;
// TODO: correct interfaces?
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, 224.0.0.251 should be right.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's not about the IP here but about 0.0.0.0.
Some mDNS examples enumerate the interfaces available on the machine and create a socket for each one of them. However there is no cross-platform way to do that at the moment.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it should be fine. Going to remove that TODO.

pub enum MdnsPacket<'a> {
/// A query made by a remote.
Query(MdnsQuery<'a>),
/// A response received by a remote to one of our queries.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it A response **sent** by a remote in answer to one of our queries.?

///
/// Pass the ID of the local peer, and the list o addresses we're listening on.
///
/// If there are more than 2^16-1 addresses, ignores the other.
Copy link
Contributor

Choose a reason for hiding this comment

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

s/other/others/

misc/mdns/src/lib.rs Show resolved Hide resolved

/// Decodes a `<character-string>` (as defined by RFC1035) into a `Vec` of ASCII characters.
// TODO: better error type?
pub fn decode_character_string(mut from: &[u8]) -> Result<Vec<u8>, ()> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why allocating a vector? As currently implemented, this could just return the sub-slice. The current use site parses the result as a str and turns it into a Multiaddr which will allocate another vector.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's not implemented, but we need to turn \\ into \ and \" into ".
Since it's quite unlikely to happen in practice, I'm going to use a Cow though.

cmp::min(secs, From::from(u32::max_value())) as u32
}

/// Appends a big-endian u32 to `out`.
Copy link
Contributor

Choose a reason for hiding this comment

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

How about:

fn append_u32(out: &mut Vec<u8>, value: u32) {
    let n = out.len();
    out.resize(n + 4, 0);
    BE::write_u32(&mut out[n ..], value)
}

BE is an alias for byteorder::BigEndian.

let builder = net2::UdpBuilder::new_v4()?;
builder.reuse_address(true)?;
platform_specific(&builder)?;
builder.bind(("0.0.0.0", 5353))?
Copy link
Contributor

Choose a reason for hiding this comment

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

RFC 6762 states that "[...] using UDP source port 5353 signals the presence of a fully compliant Multicast DNS querier [...]" which this crate is not. Queriers issuing one-shot queries must not use source port 5353.

misc/mdns/src/dns.rs Show resolved Hide resolved

append_u16(&mut out, id);
// Flags ; 0x80 for an answer.
append_u16(&mut out, 0x8000);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should AA not be set to 1?


// Flags.
out.push(0x00);
out.push(0x0c); // PTR record.
Copy link
Contributor

Choose a reason for hiding this comment

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

Since type is 16bit, why not append_u16(&mut out, 0xc) as you did elsewhere?

out.push(0x00);
out.push(0x0c); // PTR record.
out.push(0x80);
out.push(0x01);
Copy link
Contributor

Choose a reason for hiding this comment

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

What class value is this?


/// Polls the service for packets.
pub fn poll(&mut self) -> Async<MdnsPacket> {
// Send a query every time `query_interval` fires.
Copy link
Contributor

Choose a reason for hiding this comment

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

According to RFC 6762, exponential delays should be used for continuous querying and queries should include known answers to not cause unnecessary re-transmissions and elevated network traffic.

peer_id: PeerId,
addresses: TAddresses,
ttl: Duration,
) -> Result<(), MdnsResponseError>
Copy link
Contributor

Choose a reason for hiding this comment

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

Should responders not probe and announce on startup (cf. RFC 6762, section 8)?

Copy link
Member Author

Choose a reason for hiding this comment

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

It would be really great to avoid having to figure out the IP address of our network interfaces, as there's no easy cross-platform way to do that.
If the specs allow avoiding that, it would be great.

return Async::NotReady;
}
} else {
return Async::Ready(MdnsPacket::Response(MdnsResponse {
Copy link
Contributor

Choose a reason for hiding this comment

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

IIUC, it is trivial for an adversary to inject itself into to the set of listen addresses of another peer just by answering queries.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, by design it's not secure.

@ghost ghost assigned tomaka Nov 21, 2018
@ghost ghost added the in progress label Nov 21, 2018
@tomaka tomaka requested a review from twittner November 21, 2018 14:06
socket.set_multicast_loop_v4(true)?;
socket.set_multicast_ttl_v4(255)?;
// TODO: correct interfaces?
socket.join_multicast_v4(&From::from([224, 0, 0, 251]), &From::from([0, 0, 0, 0]))?;
Copy link
Contributor

Choose a reason for hiding this comment

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

You could use std::net::Ipv4Addr::UNSPECIFIED here.


// Flags.
append_u16(&mut out, 0x000c);
append_u16(&mut out, 0x8001);
Copy link
Contributor

Choose a reason for hiding this comment

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

IIUC this sets the flush-cache bit saying that this record is the sole truth. I guess this implies that this responder owns the name and no other responder is expected to answer that query.

Copy link
Member Author

Choose a reason for hiding this comment

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

If I set this to 0x8000, then the dnsparser crate is incapable of parsing the response w generate. Not sure why.

Copy link
Contributor

Choose a reason for hiding this comment

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

Without the flush-cache-bit you would use 1 (internet class).

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, ok!

@ghost ghost added the in progress label Nov 23, 2018
@tomaka tomaka merged commit 1b05132 into libp2p:master Nov 24, 2018
@ghost ghost removed the in progress label Nov 24, 2018
@tomaka tomaka deleted the mdns branch November 24, 2018 12:55
@tomaka tomaka mentioned this pull request Dec 5, 2018
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.

3 participants