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

bolt7: dns support #4829

Merged
merged 9 commits into from
Nov 29, 2021
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ contrib/pyln-*/.eggs/
contrib/pyln-*/pyln/*/__version__.py
release/
tests/plugins/test_selfdisable_after_getmanifest
.DS_Store

# Ignore generated files
devtools/features
Expand All @@ -60,3 +59,7 @@ doc/lightning*.[1578]
*_wiregen.[ch]
*_printgen.[ch]
*_gettextgen.po

# Ignore unrelated stuff
.DS_Store
.gdb_history
4 changes: 4 additions & 0 deletions common/json_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ void json_add_address(struct json_stream *response, const char *fieldname,
json_add_string(response, "type", "torv3");
json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr));
json_add_num(response, "port", addr->port);
} else if (addr->type == ADDR_TYPE_DNS) {
json_add_string(response, "type", "dns");
json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr));
json_add_num(response, "port", addr->port);
} else if (addr->type == ADDR_TYPE_WEBSOCKET) {
json_add_string(response, "type", "websocket");
json_add_num(response, "port", addr->port);
Expand Down
33 changes: 33 additions & 0 deletions common/test/run-ip_port_parsing.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,26 @@ int main(int argc, char *argv[])

common_setup(argv[0]);

/* Check IP/TOR/DNS parser */
assert(is_ipaddr("192.168.1.2"));
assert(!is_ipaddr("foo.bar.1.2"));
assert(is_toraddr("qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion"));
assert(is_toraddr("qubesos4rrrrz6n4.onion"));
assert(!is_toraddr("QUBESOSfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion"));
assert(!is_toraddr("QUBESOS4rrrrz6n4.onion"));
assert(!is_toraddr("qubesos-asa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion"));
assert(is_dnsaddr("example.com"));
assert(is_dnsaddr("example.digits123.com"));
assert(is_dnsaddr("example-hyphen.com"));
assert(is_dnsaddr("123example.com"));
assert(is_dnsaddr("example123.com"));
assert(is_dnsaddr("is-valid.3hostname123.com"));
assert(!is_dnsaddr("UPPERCASE.invalid.com"));
assert(!is_dnsaddr("-.invalid.com"));
assert(!is_dnsaddr("invalid.-example.com"));
assert(!is_dnsaddr("invalid.example-.com"));
assert(!is_dnsaddr("invalid..example.com"));

/* Grossly invalid. */
assert(!separate_address_and_port(tmpctx, "[", &ip, &port));
assert(!separate_address_and_port(tmpctx, "[123", &ip, &port));
Expand Down Expand Up @@ -148,6 +168,19 @@ int main(int argc, char *argv[])
assert(streq(ip, "192.168.2.255"));
assert(port == 0);

/* DNS types */
assert(separate_address_and_port(tmpctx, "example.com:42", &ip, &port));
assert(streq(ip, "example.com"));
assert(port == 42);
assert(separate_address_and_port(tmpctx, "sub.example.com:21", &ip, &port));
assert(streq(ip, "sub.example.com"));
assert(port == 21);
port = 123;
assert(separate_address_and_port(tmpctx, "sub.example.com", &ip, &port));
assert(streq(ip, "sub.example.com"));
assert(port == 123);
port = 0;

// unusual but possibly valid case
assert(separate_address_and_port(tmpctx, "[::1]", &ip, &port));
assert(streq(ip, "::1"));
Expand Down
91 changes: 89 additions & 2 deletions common/wireaddr.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr)
case ADDR_TYPE_TOR_V3:
addr->addrlen = TOR_V3_ADDRLEN;
break;
case ADDR_TYPE_DNS:
addr->addrlen = fromwire_u8(cursor, max);
memset(&addr->addr, 0, sizeof(addr->addr));
addr->addr[addr->addrlen] = 0;
break;
case ADDR_TYPE_WEBSOCKET:
addr->addrlen = 0;
break;
Expand All @@ -52,6 +57,8 @@ bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr)
void towire_wireaddr(u8 **pptr, const struct wireaddr *addr)
{
towire_u8(pptr, addr->type);
if (addr->type == ADDR_TYPE_DNS)
towire_u8(pptr, addr->addrlen);
towire(pptr, addr->addr, addr->addrlen);
towire_u16(pptr, addr->port);
}
Expand Down Expand Up @@ -211,6 +218,7 @@ bool wireaddr_is_wildcard(const struct wireaddr *addr)
return memeqzero(addr->addr, addr->addrlen);
case ADDR_TYPE_TOR_V2_REMOVED:
case ADDR_TYPE_TOR_V3:
case ADDR_TYPE_DNS:
case ADDR_TYPE_WEBSOCKET:
return false;
}
Expand Down Expand Up @@ -259,6 +267,8 @@ char *fmt_wireaddr_without_port(const tal_t * ctx, const struct wireaddr *a)
case ADDR_TYPE_TOR_V3:
return tal_fmt(ctx, "%s.onion",
b32_encode(tmpctx, a->addr, a->addrlen));
case ADDR_TYPE_DNS:
return tal_fmt(ctx, "%s", a->addr);
case ADDR_TYPE_WEBSOCKET:
return tal_strdup(ctx, "websocket");
}
Expand Down Expand Up @@ -287,8 +297,8 @@ REGISTER_TYPE_TO_STRING(wireaddr, fmt_wireaddr);
* Returns false if it wasn't one of these forms. If it returns true,
* it only overwrites *port if it was specified by <number> above.
*/
static bool separate_address_and_port(const tal_t *ctx, const char *arg,
char **addr, u16 *port)
bool separate_address_and_port(const tal_t *ctx, const char *arg,
char **addr, u16 *port)
{
char *portcolon;

Expand Down Expand Up @@ -322,6 +332,81 @@ static bool separate_address_and_port(const tal_t *ctx, const char *arg,
return true;
}

bool is_ipaddr(const char *arg)
{
struct in_addr v4;
struct in6_addr v6;
if (inet_pton(AF_INET, arg, &v4))
return true;
if (inet_pton(AF_INET6, arg, &v6))
return true;
return false;
}

bool is_toraddr(const char *arg)
{
size_t i, arglen;
arglen = strlen(arg);
if (!strends(arg, ".onion"))
return false;
if (arglen != 16 + 6 && arglen != 56 + 6)
return false;
for (i = 0; i < arglen - 6; i++) {
if (arg[i] >= 'a' && arg[i] <= 'z')
continue;
if (arg[i] >= '0' && arg[i] <= '9')
continue;
return false;
}
return true;
}

/* Rules:
*
* - not longer than 255
* - segments are separated with . dot
* - segments do not start or end with - hyphen
* - segments must be longer thant zero
* - lowercase a-z and digits 0-9 and - hyphen
*/
bool is_dnsaddr(const char *arg)
{
size_t i, arglen;
int lastdot;

if (is_ipaddr(arg) || is_toraddr(arg))
return false;

/* now that its not IP or TOR, check its a DNS name */
arglen = strlen(arg);
if (arglen > 255)
return false;
lastdot = -1;
for (i = 0; i < arglen; i++) {
if (arg[i] == '.') {
/* segment must be longer than zero */
if (i - lastdot == 1)
return false;
/* last segment can not end with hypen */
if (i != 0 && arg[i-1] == '-')
return false;
lastdot = i;
continue;
}
/* segment cannot start with hyphen */
if (i == lastdot + 1 && arg[i] == '-')
return false;
if (arg[i] >= 'a' && arg[i] <= 'z')
continue;
if (arg[i] >= '0' && arg[i] <= '9')
continue;
if (arg[i] == '-')
continue;
return false;
}
return true;
}

struct wireaddr *
wireaddr_from_hostname(const tal_t *ctx,
const char *hostname,
Expand Down Expand Up @@ -714,6 +799,7 @@ struct addrinfo *wireaddr_to_addrinfo(const tal_t *ctx,
return ai;
case ADDR_TYPE_TOR_V2_REMOVED:
case ADDR_TYPE_TOR_V3:
case ADDR_TYPE_DNS:
case ADDR_TYPE_WEBSOCKET:
break;
}
Expand Down Expand Up @@ -766,6 +852,7 @@ bool all_tor_addresses(const struct wireaddr_internal *wireaddr)
switch (wireaddr[i].u.wireaddr.type) {
case ADDR_TYPE_IPV4:
case ADDR_TYPE_IPV6:
case ADDR_TYPE_DNS:
return false;
case ADDR_TYPE_TOR_V2_REMOVED:
case ADDR_TYPE_TOR_V3:
Expand Down
21 changes: 18 additions & 3 deletions common/wireaddr.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,18 @@ struct sockaddr_un;
* where `checksum = sha3(".onion checksum" | pubkey || version)[:2]`
*/

/* BOLT-hostnames #7:
* * `5`: DNS hostname; data = `[byte:len][len*byte:hostname][u16:port]` (length up to 258)
*/

/* BOLT-websockets #7:
* * `6`: WebSocket port; data = `[2:port]` (length 2)
*/

#define TOR_V2_ADDRLEN 10
#define TOR_V3_ADDRLEN 35
#define LARGEST_ADDRLEN TOR_V3_ADDRLEN
#define DNS_ADDRLEN 255
#define LARGEST_ADDRLEN DNS_ADDRLEN
#define TOR_V3_BLOBLEN 64
#define STATIC_TOR_MAGIC_STRING "gen-default-toraddress"

Expand All @@ -52,10 +57,10 @@ enum wire_addr_type {
ADDR_TYPE_IPV6 = 2,
ADDR_TYPE_TOR_V2_REMOVED = 3,
ADDR_TYPE_TOR_V3 = 4,
ADDR_TYPE_WEBSOCKET = 6,
ADDR_TYPE_DNS = 5,
ADDR_TYPE_WEBSOCKET = 6
};

/* Structure now fit for tor support */
struct wireaddr {
enum wire_addr_type type;
u8 addrlen;
Expand Down Expand Up @@ -149,6 +154,16 @@ struct wireaddr_internal {

bool wireaddr_internal_eq(const struct wireaddr_internal *a,
const struct wireaddr_internal *b);

bool separate_address_and_port(const tal_t *ctx, const char *arg,
char **addr, u16 *port);

bool is_ipaddr(const char *arg);

bool is_toraddr(const char *arg);

bool is_dnsaddr(const char *arg);

bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr,
u16 port, bool wildcard_ok, bool dns_ok,
bool unresolved_ok, bool allow_deprecated,
Expand Down
61 changes: 61 additions & 0 deletions connectd/connectd.c
Original file line number Diff line number Diff line change
Expand Up @@ -874,7 +874,10 @@ static struct io_plan *conn_init(struct io_conn *conn,
"Can't connect to forproxy address");
break;
case ADDR_INTERNAL_WIREADDR:
/* DNS should have been resolved before */
assert(addr->u.wireaddr.type != ADDR_TYPE_DNS);
/* If it was a Tor address, we wouldn't be here. */
assert(!is_toraddr((char*)addr->u.wireaddr.addr));
ai = wireaddr_to_addrinfo(tmpctx, &addr->u.wireaddr);
break;
}
Expand Down Expand Up @@ -925,6 +928,14 @@ static void try_connect_one_addr(struct connecting *connect)
bool use_proxy = connect->daemon->always_use_proxy;
const struct wireaddr_internal *addr = &connect->addrs[connect->addrnum];
struct io_conn *conn;
#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */
bool use_dns = connect->daemon->use_dns;
struct addrinfo hints, *ais, *aii;
struct wireaddr_internal addrhint;
int gai_err;
struct sockaddr_in *sa4;
struct sockaddr_in6 *sa6;
#endif

/* In case we fail without a connection, make destroy_io_conn happy */
connect->conn = NULL;
Expand Down Expand Up @@ -974,6 +985,51 @@ static void try_connect_one_addr(struct connecting *connect)
case ADDR_TYPE_IPV6:
af = AF_INET6;
break;
case ADDR_TYPE_DNS:
#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */
if (use_proxy) /* hand it to the proxy */
break;
if (!use_dns) /* ignore DNS when we can't use it */
goto next;
/* Resolve with getaddrinfo */
memset(&hints, 0, sizeof(hints));
m-schmoock marked this conversation as resolved.
Show resolved Hide resolved
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
hints.ai_protocol = 0;
hints.ai_flags = AI_ADDRCONFIG;
gai_err = getaddrinfo((char *)addr->u.wireaddr.addr,
tal_fmt(tmpctx, "%d",
addr->u.wireaddr.port),
&hints, &ais);
if (gai_err != 0) {
status_debug("DNS with getaddrinfo gave: %s",
gai_strerror(gai_err));
goto next;
}
/* create new addrhints on-the-fly per result ... */
for (aii = ais; aii; aii = aii->ai_next) {
addrhint.itype = ADDR_INTERNAL_WIREADDR;
if (aii->ai_family == AF_INET) {
sa4 = (struct sockaddr_in *) aii->ai_addr;
wireaddr_from_ipv4(&addrhint.u.wireaddr,
&sa4->sin_addr,
addr->u.wireaddr.port);
} else if (aii->ai_family == AF_INET6) {
sa6 = (struct sockaddr_in6 *) aii->ai_addr;
wireaddr_from_ipv6(&addrhint.u.wireaddr,
&sa6->sin6_addr,
addr->u.wireaddr.port);
} else {
/* skip unsupported ai_family */
continue;
}
tal_arr_expand(&connect->addrs, addrhint);
/* don't forget to update convenience pointer */
addr = &connect->addrs[connect->addrnum];
m-schmoock marked this conversation as resolved.
Show resolved Hide resolved
}
freeaddrinfo(ais);
#endif
goto next;
case ADDR_TYPE_WEBSOCKET:
af = -1;
break;
Expand Down Expand Up @@ -1158,6 +1214,7 @@ static bool handle_wireaddr_listen(struct daemon *daemon,
case ADDR_TYPE_WEBSOCKET:
case ADDR_TYPE_TOR_V2_REMOVED:
case ADDR_TYPE_TOR_V3:
case ADDR_TYPE_DNS:
break;
}
status_failed(STATUS_FAIL_INTERNAL_ERROR,
Expand Down Expand Up @@ -1613,6 +1670,10 @@ static void add_seed_addrs(struct wireaddr_internal **addrs,
NULL, broken_reply, NULL);
if (new_addrs) {
for (size_t j = 0; j < tal_count(new_addrs); j++) {
#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */
if (new_addrs[j].type == ADDR_TYPE_DNS)
continue;
#endif
struct wireaddr_internal a;
a.itype = ADDR_INTERNAL_WIREADDR;
a.u.wireaddr = new_addrs[j];
Expand Down
1 change: 1 addition & 0 deletions connectd/netaddress.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ bool guess_address(struct wireaddr *addr)
}
case ADDR_TYPE_TOR_V2_REMOVED:
case ADDR_TYPE_TOR_V3:
case ADDR_TYPE_DNS:
case ADDR_TYPE_WEBSOCKET:
status_broken("Cannot guess address type %u", addr->type);
break;
Expand Down
3 changes: 3 additions & 0 deletions devtools/gossipwith.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@ int main(int argc, char *argv[])
case ADDR_TYPE_WEBSOCKET:
opt_usage_exit_fail("Don't support websockets");
break;
case ADDR_TYPE_DNS:
opt_usage_exit_fail("Don't support DNS");
break;
case ADDR_TYPE_IPV4:
af = AF_INET;
break;
Expand Down
Loading