diff --git a/common/test/run-ip_port_parsing.c b/common/test/run-ip_port_parsing.c index 6b8665563..8176408c4 100644 --- a/common/test/run-ip_port_parsing.c +++ b/common/test/run-ip_port_parsing.c @@ -72,23 +72,23 @@ int main(void) assert(!separate_address_and_port(ctx, "[::1]:http", &ip, &port)); // localhost hostnames for backward compat - parse_wireaddr("localhost", &addr, 200); + parse_wireaddr("localhost", &addr, 200, NULL); assert(addr.port == 200); // string should win the port battle - parse_wireaddr("[::1]:9735", &addr, 500); + parse_wireaddr("[::1]:9735", &addr, 500, NULL); assert(addr.port == 9735); ip = fmt_wireaddr(ctx, &addr); assert(streq(ip, "[::1]:9735")); // should use argument if we have no port in string - parse_wireaddr("2001:db8:85a3::8a2e:370:7334", &addr, 9777); + parse_wireaddr("2001:db8:85a3::8a2e:370:7334", &addr, 9777, NULL); assert(addr.port == 9777); ip = fmt_wireaddr(ctx, &addr); assert(streq(ip, "[2001:db8:85a3::8a2e:370:7334]:9777")); - assert(parse_wireaddr("[::ffff:127.0.0.1]:49150", &addr, 1)); + assert(parse_wireaddr("[::ffff:127.0.0.1]:49150", &addr, 1, NULL)); assert(addr.port == 49150); tal_free(ctx); return 0; diff --git a/common/wireaddr.c b/common/wireaddr.c index 4985e36d3..4d68a469d 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -4,8 +4,10 @@ #include #include #include +#include #include #include +#include #include /* Returns false if we didn't parse it, and *cursor == NULL if malformed. */ @@ -111,10 +113,16 @@ static bool separate_address_and_port(tal_t *ctx, const char *arg, return true; } -bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 defport) +bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 defport, + const char **err_msg) { struct in6_addr v6; struct in_addr v4; + struct sockaddr_in6 *sa6; + struct sockaddr_in *sa4; + struct addrinfo *addrinfo; + struct addrinfo hints; + int gai_err; u16 port; char *ip; bool res; @@ -122,13 +130,12 @@ bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 defport) res = false; port = defport; + if (err_msg) + *err_msg = NULL; - if (!separate_address_and_port(tmpctx, arg, &ip, &port)) { - tal_free(tmpctx); - return false; - } + if (!separate_address_and_port(tmpctx, arg, &ip, &port)) + goto finish; - /* FIXME: change arg to addr[:port] and use getaddrinfo? */ if (streq(ip, "localhost")) ip = "127.0.0.1"; else if (streq(ip, "ip6-localhost")) @@ -150,6 +157,44 @@ bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 defport) res = true; } + /* Resolve with getaddrinfo */ + if (!res) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_ADDRCONFIG; + gai_err = getaddrinfo(ip, tal_fmt(tmpctx, "%d", port), + &hints, &addrinfo); + if (gai_err != 0) { + if (err_msg) + *err_msg = gai_strerror(gai_err); + goto finish; + } + /* Use only the first found address */ + if (addrinfo->ai_family == AF_INET) { + addr->type = ADDR_TYPE_IPV4; + addr->addrlen = 4; + addr->port = port; + sa4 = (struct sockaddr_in *) addrinfo->ai_addr; + memcpy(&addr->addr, &sa4->sin_addr, addr->addrlen); + res = true; + } else if (addrinfo->ai_family == AF_INET6) { + addr->type = ADDR_TYPE_IPV6; + addr->addrlen = 16; + addr->port = port; + sa6 = (struct sockaddr_in6 *) addrinfo->ai_addr; + memcpy(&addr->addr, &sa6->sin6_addr, addr->addrlen); + res = true; + } + + /* Clean up */ + freeaddrinfo(addrinfo); + } + +finish: + if (!res && err_msg && !*err_msg) + *err_msg = "Error parsing hostname"; tal_free(tmpctx); return res; } diff --git a/common/wireaddr.h b/common/wireaddr.h index 69f1a9225..30f42cbde 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -41,7 +41,7 @@ struct wireaddr { void towire_wireaddr(u8 **pptr, const struct wireaddr *addr); bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr); -bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 port); +bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 port, const char **err_msg); char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a); diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 877f9bf06..0a01bd940 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -114,6 +114,7 @@ static void json_connect(struct command *cmd, const char *name; struct wireaddr addr; u8 *msg; + const char *err_msg; if (!json_get_params(cmd, buffer, params, "id", &idtok, @@ -178,9 +179,9 @@ static void json_connect(struct command *cmd, } else { addr.port = DEFAULT_PORT; } - if (!parse_wireaddr(name, &addr, addr.port) || !addr.port) { - command_fail(cmd, "Host %s:%u not valid", - name, addr.port); + if (!parse_wireaddr(name, &addr, addr.port, &err_msg) || !addr.port) { + command_fail(cmd, "Host %s:%u not valid: %s", + name, addr.port, err_msg ? err_msg : "port is 0"); return; } diff --git a/lightningd/options.c b/lightningd/options.c index 38914847b..d3932fcf2 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -117,11 +117,12 @@ static char *opt_set_s32(const char *arg, s32 *u) static char *opt_add_ipaddr(const char *arg, struct lightningd *ld) { size_t n = tal_count(ld->wireaddrs); + char const *err_msg; tal_resize(&ld->wireaddrs, n+1); - if (!parse_wireaddr(arg, &ld->wireaddrs[n], ld->portnum)) { - return tal_fmt(NULL, "Unable to parse IP address '%s'", arg); + if (!parse_wireaddr(arg, &ld->wireaddrs[n], ld->portnum, &err_msg)) { + return tal_fmt(NULL, "Unable to parse IP address '%s': %s", arg, err_msg); } return NULL; diff --git a/wallet/wallet.c b/wallet/wallet.c index b34ba2c42..3b6684187 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -436,7 +436,7 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) addrstr = sqlite3_column_text(stmt, 2); if (addrstr) { addrp = &addr; - if (!parse_wireaddr((const char*)addrstr, addrp, DEFAULT_PORT)) { + if (!parse_wireaddr((const char*)addrstr, addrp, DEFAULT_PORT, NULL)) { sqlite3_finalize(stmt); return NULL; } @@ -466,7 +466,7 @@ bool wallet_peer_by_nodeid(struct wallet *w, const struct pubkey *nodeid, addrstr = sqlite3_column_text(stmt, 2); if (addrstr) - parse_wireaddr((const char*)addrstr, &peer->addr, DEFAULT_PORT); + parse_wireaddr((const char*)addrstr, &peer->addr, DEFAULT_PORT, NULL); } else { /* Make sure we mark this as a new peer */ peer->dbid = 0;