mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-17 19:03:42 +01:00
More flexible address wildcards, only add wildcard if nothing else.
1. Add special option where an empty host means 'wildcard for IPv4 and/or IPv6' which means ':1234' can be used to set only the portnum. 2. Only add this protocol wildcard if --autolisten=1 (default) and no other addresses specified. 3. Pass it down to gossipd, so it can handle errors correctly: in most cases, it's fatal not to be able to bind to a port, but for this case, it's OK if we can only bind to one of IPv4/v6 (fatal iff neither). Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
73cd009a4c
commit
52917ff6c9
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user