Skip to content

Adds DNS hostname to NetAddress - #1553

Merged
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
wvanlint:dns_hostname
Jul 5, 2022
Merged

Adds DNS hostname to NetAddress#1553
TheBlueMatt merged 1 commit into
lightningdevkit:mainfrom
wvanlint:dns_hostname

Conversation

@wvanlint

Copy link
Copy Markdown
Contributor

Supports Bolt 7 DNS hostnames specified by lightning/bolts#911.

Comment threadlightning/src/util/ser.rs Outdated
type Error = ConversionError;

fn try_from(s: String) -> Result<Self, Self::Error> {
if s.is_ascii() && s.len() <= u8::MAX.into() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should also check that all characters are in the printable range (and maybe no spaces? probably no special chars? Dunno what's valid in DNS, we should check). With that in mind, maybe we should name the type something in reference to DNS?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

It seems DNS permits any sequence of bytes but has a preferred format. I implemented the preferred format based on RFC 3696 and others linked there.

Comment threadlightning/src/ln/msgs.rs Outdated
port: u16,
},
/// A DNS hostname/port on which the peer is listening.
DNSHostname {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we just call it hostname? DNS is kinda obvious? Am I missing something?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Adjusted that, I was mainly mirroring the BOLT PR.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Nice, thanks! Looks like this is failing CI.

@wvanlint
wvanlintforce-pushed the dns_hostname branch 4 times, most recently from 9518a45 to 8892a48CompareJune 23, 2022 07:21
@tnulltnull linked an issue Jun 23, 2022 that may be closed by this pull request
Comment threadlightning/src/util/ser.rs Outdated

fn try_from(s: String) -> Result<Self, Self::Error> {
// Regular expressions can't be used with no-std.
let labels: Vec<&str> = s.split(".").collect();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You should be able to avoid the collect here. In general, allocating a Vec via collect is not super trivial, and we should avoid it if at all possible, preferring to just keep and work with the generated iterator directly.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Fixed.

Comment threadlightning/src/util/errors.rs Outdated

/// Indicates an error on conversions by core::convert traits.
#[derive(Debug)]
pub struct ConversionError;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this worth it? If its just a dummy type can we not just use () instead? It seems basically as clear either way?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I initially wanted to implement the Error trait for boxing purposes but I wasn't sure if it made sense to do this conditionally only when the standard library is used. Changed to () but let me know if you prefer a type with an Error trait instead.

@codecov-commenter

codecov-commenter commented Jun 24, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1553 (c30dcf1) into main (abf6564) will increase coverage by 0.03%.
The diff coverage is 80.00%.

@@ Coverage Diff @@## main #1553 +/- ##
==========================================
+ Coverage 90.96% 91.00% +0.03% 
==========================================
Files 80 80 Lines 43610 44197 +587 Branches 43610 44197 +587 ==========================================
+ Hits 39670 40221 +551 - Misses 3940 3976 +36 
Impacted FilesCoverage Δ
lightning/src/ln/msgs.rs86.49% <75.00%> (-0.32%)⬇️
lightning/src/util/ser.rs90.96% <82.60%> (-0.32%)⬇️
lightning/src/ln/channelmanager.rs84.73% <100.00%> (+0.34%)⬆️
lightning/src/util/chacha20poly1305rfc.rs96.25% <0.00%> (-1.71%)⬇️
lightning/src/chain/onchaintx.rs93.98% <0.00%> (-0.93%)⬇️
lightning/src/chain/channelmonitor.rs90.91% <0.00%> (-0.33%)⬇️
lightning/src/routing/scoring.rs96.80% <0.00%> (-0.29%)⬇️
lightning/src/ln/functional_tests.rs96.81% <0.00%> (-0.25%)⬇️
lightning-invoice/src/lib.rs87.39% <0.00%> (ø)
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (ø)
... and 16 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update abf6564...c30dcf1. Read the comment docs.

@wvanlint
wvanlint marked this pull request as ready for review June 24, 2022 06:25
Comment threadlightning/src/util/ser.rs Outdated
// Trailing period for fully-qualified name.
if bytes.len() == 0 {
if !last_label || label_idx == 0 {
break false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Instead of breaking out of the loop with a validity flag, the structure here would be more readable if we just returned the error directly if something is invalid.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Done.

Comment threadlightning/src/util/ser.rs Outdated
Comment on lines +954 to +959
let last_label;
if let None = labels.peek() {
last_label = true;
} else {
last_label = false;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let last_label;
ifletNone = labels.peek(){
last_label = true;
}else{
last_label = false;
}
let last_label = labels.peek().is_none();

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Done.

Comment threadlightning/src/util/ser.rs Outdated
Comment on lines +948 to +953
let valid_labels = loop {
let label;
match labels.next() {
Some(x) => label = x,
None => break true,
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let valid_labels = loop{
let label;
match labels.next(){
Some(x) => label = x,
None => breaktrue,
}
whileletSome(label) = labels.next(){

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Done.

Comment threadlightning/src/util/ser.rs Outdated

fn try_from(s: String) -> Result<Self, Self::Error> {
let mut labels = s.split('.').peekable();
let mut label_idx = 0;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Instead of tracking the idx explicitly, maybe just wrap labels with an .enumerate().

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Done.

Comment threadlightning/src/util/ser.rs Outdated
}
}
}
impl TryFrom<&str> for Hostname {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This gives the (incorrect) impression that From<&str> is relatively efficient, but its not. Its maybe simpler to force the user to do the String::from() or .to_string() explicitly themselves to communicate what's really going on here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

That's true, removed this implementation.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

It looks like somehow your rebase ended up reverting several recent PRs.

@TheBlueMatt

TheBlueMatt commented Jun 25, 2022

Copy link
Copy Markdown
Collaborator

It looks like somehow your rebase ended up reverting several recent PRs.

Oops, sorry, it looks like the GitHub mobile app just...shows incorrect diffs sometimes?

This basically LGTM, lets get another reviewer on it.

TheBlueMatt
TheBlueMatt previously approved these changes Jun 25, 2022

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Basically LGTM.

Comment threadlightning/src/util/ser.rs Outdated
#[derive(Clone, Debug, PartialEq)]
pub struct Hostname(String);
impl Hostname {
/// Returns the length of the hostname with an appropriate return type.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

s/with an appropriate return type//?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Done.

@tnulltnull self-assigned this Jun 27, 2022
@tnulltnull removed their assignment Jun 27, 2022
@tnull
tnull self-requested a review June 27, 2022 07:51

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you for having a go at this! Generally looks good, only a few nits and questions.

Comment threadlightning/src/util/ser.rs Outdated
Comment threadlightning/src/util/ser.rs Outdated
Comment threadlightning/src/ln/msgs.rs
Comment threadlightning/src/util/ser.rs Outdated
Comment threadlightning/src/util/ser.rs Outdated
Comment threadlightning/src/util/ser.rs
Comment threadlightning/src/util/ser.rs Outdated
Comment threadlightning/src/ln/msgs.rs Outdated
},
/// A hostname/port on which the peer is listening.
Hostname {
/// The hostname on which the port is listening.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Think "port" should be "node" or "peer"?

Suggested change
/// The hostname on which the port is listening.
/// The hostname on which the node is listening.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Done.

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Generally LGTM.

One note: it's easier for reviewers to follow the changes if you do not immediately squash them, but leave individual commits.

@wvanlint
wvanlint requested a review from TheBlueMattJuly 1, 2022 17:47
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Sorry for the delay, been a super busy week.

Generally, now that we have all the logic for it, and now that I think about it a bit more, do we really need to do this much verification of a hostname? Like, yea, the DNS won't resolve if the hostname is just one long string, but what happens if users start shoving non-hostname things in the hostname field (ie treating it as a second node alias, listing some policy info, etc) - should we outright drop their gossip, or simply fail to connect to the hostname and try the IP addresses instead? I do think we should ensure the strong is ASCII, is all in the printable range (ie no control chars) but do we need to care about label length?

@wvanlint

Copy link
Copy Markdown
ContributorAuthor

@TheBlueMatt I agree, the concern of hostname validation can be left to DNS resolution and separated from the serialization concern here which was my original intent. I reverted back to the first approach with generic serialization prior to #1553 (comment).

Extending this rationale, I would also be open to relaxing validation further and allowing any UTF-8 string within the length constraint (although this is not included in the BOLT). Libraries are able to handle Punycode encoding e.g. "münchen.de:443".to_socket_addrs().unwrap(). I think the field could be misused no matter how strict the validation is.

Out of curiosity, are terminal escape sequence injections the main concern with control characters? I am unsure about their severity for modern terminals.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Extending this rationale, I would also be open to relaxing validation further and allowing any UTF-8 string within the length constraint (although this is not included in the BOLT)

Hmm, there's two sides to this, though - we have to support reading hostnames in announcements and be kinda liberal there, but also want to be restrictive as to what users include as others may do strict validation. In theory we could split the two but I'm not sure it's really worth it - our users are developers, and can manage to just put a hostname.

Out of curiosity, are terminal escape sequence injections the main concern with control characters? I am unsure about their severity for modern terminals.

Yea, you never know, users of our devlopers' software will grep things and pass them to dig and...boom. With that in mind I'd suggest we also reject chars like $, ", ', backtick, etc. Probably easiest to just accept alphanumeric plus . and -. If its easy its best to be as conservative as possible but not bother doing strict hostname validation so much as just strict character set validation.

@wvanlint
wvanlintforce-pushed the dns_hostname branch 2 times, most recently from fc3c757 to 2767a41CompareJuly 3, 2022 21:58

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Oooooooo-Kkkkkkkk. Thanks for sticking with me on this one, this LGTM as-is. Will let the other reviewers chime in, but feel free to squash down the fixups once they do.

@wvanlint
wvanlint requested a review from tnullJuly 4, 2022 04:52
tnull
tnull previously approved these changes Jul 4, 2022

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. I just restarted the one stuck CI job.

@wvanlint

Copy link
Copy Markdown
ContributorAuthor

Thanks for sticking with me on this one, this LGTM as-is. Will let the other reviewers chime in, but feel free to squash down the fixups once they do.

Thanks for the review as well! Squashed down the fixups.

@TheBlueMatt
TheBlueMatt merged commit daeb5a6 into lightningdevkit:mainJul 5, 2022
@wvanlint
wvanlint deleted the dns_hostname branch July 7, 2022 03:59
tnull added a commit to tnull/rust-lightning that referenced this pull request Jul 25, 2022
Fixes a deserialization incompatibility introduced with lightningdevkit#1553.
Sign up for freeto 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.

Support DNS hostnames in gossip

5 participants

@wvanlint@TheBlueMatt@codecov-commenter@tnull@dunxen