diff --git a/common/wireaddr.c b/common/wireaddr.c index 5b454d7f0..82aa68931 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -63,6 +63,9 @@ void towire_wireaddr_internal(u8 **pptr, const struct wireaddr_internal *addr) towire_u8_array(pptr, (const u8 *)addr->u.sockname, sizeof(addr->u.sockname)); return; + case ADDR_INTERNAL_ALLPROTO: + towire_u16(pptr, addr->u.port); + return; case ADDR_INTERNAL_WIREADDR: towire_wireaddr(pptr, &addr->u.wireaddr); return; @@ -82,6 +85,9 @@ bool fromwire_wireaddr_internal(const u8 **cursor, size_t *max, if (!memchr(addr->u.sockname, 0, sizeof(addr->u.sockname))) fromwire_fail(cursor, max); return *cursor != NULL; + case ADDR_INTERNAL_ALLPROTO: + addr->u.port = fromwire_u16(cursor, max); + return *cursor != NULL; case ADDR_INTERNAL_WIREADDR: return fromwire_wireaddr(cursor, max, &addr->u.wireaddr); } @@ -164,6 +170,8 @@ char *fmt_wireaddr_internal(const tal_t *ctx, switch (a->itype) { case ADDR_INTERNAL_SOCKNAME: return tal_fmt(ctx, "%s", a->u.sockname); + case ADDR_INTERNAL_ALLPROTO: + return tal_fmt(ctx, ":%u", a->u.port); case ADDR_INTERNAL_WIREADDR: return fmt_wireaddr(ctx, &a->u.wireaddr); } @@ -297,8 +305,12 @@ finish: return res; } -bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, const char **err_msg) +bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, + u16 port, bool wildcard_ok, const char **err_msg) { + u16 wildport; + char *ip; + /* Addresses starting with '/' are local socket paths */ if (arg[0] == '/') { addr->itype = ADDR_INTERNAL_SOCKNAME; @@ -313,6 +325,16 @@ bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u1 return true; } + /* An empty string means IPv4 and IPv6 (which under Linux by default + * means just IPv6, and IPv4 gets autobound). */ + if (wildcard_ok + && separate_address_and_port(tmpctx, arg, &ip, &wildport) + && streq(ip, "")) { + addr->itype = ADDR_INTERNAL_ALLPROTO; + addr->u.port = wildport; + return true; + } + addr->itype = ADDR_INTERNAL_WIREADDR; return parse_wireaddr(arg, &addr->u.wireaddr, port, err_msg); } diff --git a/common/wireaddr.h b/common/wireaddr.h index 3b202f9ae..19a3f0905 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -77,6 +77,7 @@ bool wireaddr_to_ipv6(const struct wireaddr *addr, struct sockaddr_in6 *s6); enum wireaddr_internal_type { ADDR_INTERNAL_SOCKNAME, + ADDR_INTERNAL_ALLPROTO, ADDR_INTERNAL_WIREADDR, }; @@ -84,11 +85,15 @@ enum wireaddr_internal_type { struct wireaddr_internal { enum wireaddr_internal_type itype; union { + /* ADDR_INTERNAL_SOCKNAME */ struct wireaddr wireaddr; + /* ADDR_INTERNAL_ALLPROTO */ + u16 port; + /* ADDR_INTERNAL_WIREADDR */ char sockname[108]; } u; }; -bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, const char **err_msg); +bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, bool wildcard_ok, const char **err_msg); void towire_wireaddr_internal(u8 **pptr, const struct wireaddr_internal *addr); diff --git a/gossipd/gossip.c b/gossipd/gossip.c index 5e5883485..a84efcd93 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -570,7 +570,8 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, if (!(daemon->listen_announce[i] & ADDR_ANNOUNCE)) continue; /* You can only announce wiretypes! */ - assert(daemon->wireaddrs[i].itype == ADDR_INTERNAL_WIREADDR); + if (daemon->wireaddrs[i].itype != ADDR_INTERNAL_WIREADDR) + continue; towire_wireaddr(&addresses, &daemon->wireaddrs[i].u.wireaddr); } @@ -1389,10 +1390,14 @@ out: return daemon_conn_read_next(conn, &daemon->master); } -static int make_listen_fd(int domain, void *addr, socklen_t len, bool reportfail) +static int make_listen_fd(int domain, void *addr, socklen_t len, bool mayfail) { int fd = socket(domain, SOCK_STREAM, 0); if (fd < 0) { + if (!mayfail) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Failed to create %u socket: %s", + domain, strerror(errno)); status_trace("Failed to create %u socket: %s", domain, strerror(errno)); return -1; @@ -1407,17 +1412,20 @@ static int make_listen_fd(int domain, void *addr, socklen_t len, bool reportfail strerror(errno)); if (bind(fd, addr, len) != 0) { - if (reportfail) - status_broken("Failed to bind on %u socket: %s", + if (!mayfail) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Failed to bind on %u socket: %s", domain, strerror(errno)); + status_trace("Failed to create %u socket: %s", + domain, strerror(errno)); goto fail; } } if (listen(fd, 5) != 0) { - status_broken("Failed to listen on %u socket: %s", + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Failed to listen on %u socket: %s", domain, strerror(errno)); - goto fail; } return fd; @@ -1561,10 +1569,10 @@ static struct io_plan *connection_in(struct io_conn *conn, struct daemon *daemon init_new_peer, daemon); } -/* Returns true if it was an IPv6 wildcard (as inserted by guess_addresses) */ +/* Return true if it created socket successfully. */ static bool handle_wireaddr_listen(struct daemon *daemon, const struct wireaddr *wireaddr, - bool had_ipv6_wildcard) + bool mayfail) { int fd; struct sockaddr_in addr; @@ -1574,27 +1582,24 @@ static bool handle_wireaddr_listen(struct daemon *daemon, case ADDR_TYPE_IPV4: wireaddr_to_ipv4(wireaddr, &addr); /* We might fail if IPv6 bound to port first */ - fd = make_listen_fd(AF_INET, &addr, sizeof(addr), - !had_ipv6_wildcard); + fd = make_listen_fd(AF_INET, &addr, sizeof(addr), mayfail); if (fd >= 0) { status_trace("Created IPv4 listener on port %u", wireaddr->port); io_new_listener(daemon, fd, connection_in, daemon); + return true; } return false; case ADDR_TYPE_IPV6: wireaddr_to_ipv6(wireaddr, &addr6); - if (memeqzero(&addr6.sin6_addr, sizeof(addr6.sin6_addr))) - had_ipv6_wildcard = true; - else - had_ipv6_wildcard = false; - fd = make_listen_fd(AF_INET6, &addr6, sizeof(addr6), true); + fd = make_listen_fd(AF_INET6, &addr6, sizeof(addr6), mayfail); if (fd >= 0) { status_trace("Created IPv6 listener on port %u", wireaddr->port); io_new_listener(daemon, fd, connection_in, daemon); + return true; } - return had_ipv6_wildcard; + return false; case ADDR_TYPE_PADDING: break; } @@ -1604,11 +1609,12 @@ static bool handle_wireaddr_listen(struct daemon *daemon, static void setup_listeners(struct daemon *daemon) { - bool had_ipv6_wildcard = false; struct sockaddr_un addrun; int fd; for (size_t i = 0; i < tal_count(daemon->wireaddrs); i++) { + struct wireaddr wa = daemon->wireaddrs[i].u.wireaddr; + if (!(daemon->listen_announce[i] & ADDR_LISTEN)) continue; @@ -1623,10 +1629,22 @@ static void setup_listeners(struct daemon *daemon) addrun.sun_path); io_new_listener(daemon, fd, connection_in, daemon); continue; + case ADDR_INTERNAL_ALLPROTO: { + bool ipv6_ok; + + memset(wa.addr, 0, sizeof(wa.addr)); + wa.type = ADDR_TYPE_IPV6; + wa.addrlen = 16; + + ipv6_ok = handle_wireaddr_listen(daemon, &wa, true); + wa.type = ADDR_TYPE_IPV4; + wa.addrlen = 4; + /* OK if this fails, as long as one succeeds! */ + handle_wireaddr_listen(daemon, &wa, ipv6_ok); + continue; + } case ADDR_INTERNAL_WIREADDR: - had_ipv6_wildcard = handle_wireaddr_listen( - daemon, &daemon->wireaddrs[i].u.wireaddr, - had_ipv6_wildcard); + handle_wireaddr_listen(daemon, &wa, false); continue; } /* Shouldn't happen. */ @@ -1673,19 +1691,12 @@ static struct io_plan *gossip_activate(struct daemon_conn *master, const u8 *msg) { bool listen; - bool guess_addrs; - u16 port; - if (!fromwire_gossipctl_activate(msg, &listen, &guess_addrs, &port)) + if (!fromwire_gossipctl_activate(msg, &listen)) master_badmsg(WIRE_GOSSIPCTL_ACTIVATE, msg); - if (listen) { - if (guess_addrs) - guess_addresses(&daemon->wireaddrs, - &daemon->listen_announce, - port); + if (listen) setup_listeners(daemon); - } /* OK, we're ready! */ daemon_conn_send(&daemon->master, @@ -1808,6 +1819,10 @@ static struct io_plan *conn_init(struct io_conn *conn, struct reaching *reach) ai.ai_addrlen = sizeof(sin); ai.ai_addr = (struct sockaddr *)&sun; break; + case ADDR_INTERNAL_ALLPROTO: + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Can't reach to all protocols"); + break; case ADDR_INTERNAL_WIREADDR: switch (reach->addr.u.wireaddr.type) { case ADDR_TYPE_IPV4: @@ -1950,6 +1965,9 @@ static void try_reach_peer(struct daemon *daemon, const struct pubkey *id, case ADDR_INTERNAL_SOCKNAME: af = AF_LOCAL; break; + case ADDR_INTERNAL_ALLPROTO: + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Can't reach ALLPROTO"); case ADDR_INTERNAL_WIREADDR: switch (a->addr.u.wireaddr.type) { case ADDR_TYPE_IPV4: diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index c87a8a2e7..030c499e8 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -23,9 +23,6 @@ gossipctl_init,,reconnect,bool gossipctl_activate,3025 # Do we listen? gossipctl_activate,,listen,bool -gossipctl_activate,,guess_addresses,bool -# FIXME: Hack for deprecated --port option. -gossipctl_activate,,port,u16 # Gossipd->master, I am ready, here are the final addresses. gossipctl_activate_reply,3125 diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 05354922d..9ee955b38 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -151,7 +151,8 @@ static void json_connect(struct command *cmd, } else { port = DEFAULT_PORT; } - if (!parse_wireaddr_internal(name, &addr, port, &err_msg)) { + if (!parse_wireaddr_internal(name, &addr, port, false, + &err_msg)) { command_fail(cmd, "Host %s:%u not valid: %s", name, port, err_msg ? err_msg : "port is 0"); return; diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 8010622dc..897a58528 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -183,6 +183,8 @@ void gossip_init(struct lightningd *ld) u8 *msg; int hsmfd; u64 capabilities = HSM_CAP_ECDH | HSM_CAP_SIGN_GOSSIP; + struct wireaddr_internal *wireaddrs = ld->wireaddrs; + enum addr_listen_announce *listen_announce = ld->listen_announce; msg = towire_hsm_client_hsmfd(tmpctx, &ld->id, capabilities); if (!wire_sync_write(ld->hsm_fd, msg)) @@ -202,12 +204,21 @@ void gossip_init(struct lightningd *ld) if (!ld->gossip) err(1, "Could not subdaemon gossip"); + /* If no addr specified, hand wildcard to gossipd */ + if (tal_count(wireaddrs) == 0 && ld->autolisten) { + wireaddrs = tal_arrz(tmpctx, struct wireaddr_internal, 1); + listen_announce = tal_arr(tmpctx, enum addr_listen_announce, 1); + wireaddrs->itype = ADDR_INTERNAL_ALLPROTO; + wireaddrs->u.port = ld->portnum; + *listen_announce = ADDR_LISTEN_AND_ANNOUNCE; + } + msg = towire_gossipctl_init( tmpctx, ld->config.broadcast_interval, &get_chainparams(ld)->genesis_blockhash, &ld->id, get_offered_global_features(tmpctx), - get_offered_local_features(tmpctx), ld->wireaddrs, - ld->listen_announce, ld->rgb, + get_offered_local_features(tmpctx), wireaddrs, + listen_announce, ld->rgb, ld->alias, ld->config.channel_update_interval, ld->reconnect); subd_send_msg(ld->gossip, msg); } @@ -234,8 +245,7 @@ static void gossip_activate_done(struct subd *gossip UNUSED, void gossip_activate(struct lightningd *ld) { - const u8 *msg = towire_gossipctl_activate(NULL, ld->listen, ld->autolisten, - ld->portnum); + const u8 *msg = towire_gossipctl_activate(NULL, ld->listen); subd_req(ld->gossip, ld->gossip, take(msg), -1, 0, gossip_activate_done, NULL); diff --git a/lightningd/json.c b/lightningd/json.c index 35b5c3c03..b401ca00a 100644 --- a/lightningd/json.c +++ b/lightningd/json.c @@ -154,6 +154,12 @@ void json_add_address_internal(struct json_result *response, json_add_string(response, "socket", addr->u.sockname); json_object_end(response); return; + case ADDR_INTERNAL_ALLPROTO: + json_object_start(response, fieldname); + json_add_string(response, "type", "any protocol"); + json_add_num(response, "port", addr->u.port); + json_object_end(response); + return; case ADDR_INTERNAL_WIREADDR: json_add_address(response, fieldname, &addr->u.wireaddr); return; diff --git a/lightningd/options.c b/lightningd/options.c index 7c32c9dfd..b461d0396 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -158,7 +158,8 @@ static char *opt_add_addr_withtype(const char *arg, tal_resize(&ld->listen_announce, n+1); ld->listen_announce[n] = ala; - if (!parse_wireaddr_internal(arg, &ld->wireaddrs[n], ld->portnum, &err_msg)) { + if (!parse_wireaddr_internal(arg, &ld->wireaddrs[n], ld->portnum, + true, &err_msg)) { return tal_fmt(NULL, "Unable to parse address '%s': %s", arg, err_msg); } diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index bdf4d738a..1385c784d 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -4281,6 +4281,13 @@ class LightningDTests(BaseLightningDTests): bitcoind.generate_block(1) l1.daemon.wait_for_log('ONCHAIN') + def test_address(self): + l1 = self.node_factory.get_node() + assert len(l1.rpc.getinfo()['address']) == 1 + assert l1.rpc.getinfo()['address'][0]['type'] == 'ipv4' + assert l1.rpc.getinfo()['address'][0]['address'] == '127.0.0.1' + assert int(l1.rpc.getinfo()['address'][0]['port']) == l1.port + def test_listconfigs(self): l1 = self.node_factory.get_node() diff --git a/wallet/wallet.c b/wallet/wallet.c index 548fe3f24..cead5db5d 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -510,7 +510,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_internal((const char*)addrstr, addrp, DEFAULT_PORT, NULL)) { + if (!parse_wireaddr_internal((const char*)addrstr, addrp, DEFAULT_PORT, false, NULL)) { db_stmt_done(stmt); return NULL; }