diff --git a/common/wireaddr.c b/common/wireaddr.c index 71062ab48475..ce7e06e86fe4 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -8,6 +8,7 @@ #include #include #include +#include #include /* Returns false if we didn't parse it, and *cursor == NULL if malformed. */ @@ -22,6 +23,9 @@ bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr) case ADDR_TYPE_IPV6: addr->addrlen = 16; break; + case ADDR_TYPE_PADDING: + addr->addrlen = ((u8)*max) - 2; + break; default: return false; } @@ -33,7 +37,7 @@ bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr) void towire_wireaddr(u8 **pptr, const struct wireaddr *addr) { - if (!addr || addr->type == ADDR_TYPE_PADDING) { + if (!addr) { towire_u8(pptr, ADDR_TYPE_PADDING); return; } @@ -181,6 +185,20 @@ bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 defport, memset(&addr->addr, 0, sizeof(addr->addr)); + /* Addresses starting with '/' are local socket paths */ + if (ip[0] == '/') { + /* Check if the path is too long */ + if (strlen(ip) > sizeof(addr->addr)) { + goto finish; + } + addr->type = ADDR_TYPE_PADDING; + addr->addrlen = strlen(ip); + addr->port = 0; + memcpy(&addr->addr, ip, addr->addrlen); + res = true; + goto finish; + } + if (inet_pton(AF_INET, ip, &v4) == 1) { addr->type = ADDR_TYPE_IPV4; addr->addrlen = 4; diff --git a/common/wireaddr.h b/common/wireaddr.h index ade052295c40..12cc1d896d41 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -33,7 +33,7 @@ enum wire_addr_type { struct wireaddr { enum wire_addr_type type; u8 addrlen; - u8 addr[16]; + u8 addr[108]; u16 port; }; diff --git a/gossipd/gossip.c b/gossipd/gossip.c index 6bb5e126106c..cd16f4784ac2 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -1541,6 +1542,12 @@ static struct io_plan *connection_in(struct io_conn *conn, struct daemon *daemon BUILD_ASSERT(sizeof(s4->sin_addr) <= sizeof(addr.addr)); memcpy(addr.addr, &s4->sin_addr, addr.addrlen); addr.port = ntohs(s4->sin_port); + } else if (s.ss_family == AF_UNIX) { + struct sockaddr_un *sun = (void *)&s; + addr.type = ADDR_TYPE_PADDING; + addr.addrlen = sizeof(sun->sun_path); + memcpy(addr.addr, &sun->sun_path, addr.addrlen); + addr.port = 0; } else { status_broken("Unknown socket type %i for incoming conn", s.ss_family); @@ -1552,12 +1559,13 @@ static struct io_plan *connection_in(struct io_conn *conn, struct daemon *daemon init_new_peer, daemon); } -static void setup_listeners(struct daemon *daemon, u16 portnum) +static void setup_listeners(struct daemon *daemon, u16 portnum, const char *localsocket) { struct sockaddr_in addr; struct sockaddr_in6 addr6; + struct sockaddr_un sun; socklen_t len; - int fd1, fd2; + int fd1, fd2, fd3; if (!portnum) { status_info("Zero portnum, not listening for incoming"); @@ -1615,6 +1623,27 @@ static void setup_listeners(struct daemon *daemon, u16 portnum) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Could not bind to a network address on port %u", portnum); + + /* Optional UNIX socket */ + if (localsocket) { + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + strcpy(sun.sun_path, (char*)localsocket); + + fd3 = make_listen_fd(AF_UNIX, &sun, sizeof(sun), false); + if (fd3 >= 0) { + len = sizeof(sun); + if (getsockname(fd3, (void *)&sun, &len) != 0) { + status_broken("Failed get UNIX sockname: %s", + strerror(errno)); + close_noerr(fd3); + fd3 = -1; + } else { + status_trace("Creating UNIX socket listener"); + io_new_listener(daemon, fd3, connection_in, daemon); + } + } + } } /* Parse an incoming gossip init message and assign config variables @@ -1653,11 +1682,12 @@ static struct io_plan *gossip_activate(struct daemon_conn *master, const u8 *msg) { u16 port; + u8* localsocket; - if (!fromwire_gossipctl_activate(msg, &port)) + if (!fromwire_gossipctl_activate(msg, msg, &port, &localsocket)) master_badmsg(WIRE_GOSSIPCTL_ACTIVATE, msg); - setup_listeners(daemon, port); + setup_listeners(daemon, port, (const char*)localsocket); /* OK, we're ready! */ daemon_conn_send(&daemon->master, @@ -1762,6 +1792,7 @@ static struct io_plan *conn_init(struct io_conn *conn, struct reaching *reach) struct addrinfo ai; struct sockaddr_in sin; struct sockaddr_in6 sin6; + struct sockaddr_un sun; /* FIXME: make generic */ ai.ai_flags = 0; @@ -1789,8 +1820,12 @@ static struct io_plan *conn_init(struct io_conn *conn, struct reaching *reach) ai.ai_addr = (struct sockaddr *)&sin6; break; case ADDR_TYPE_PADDING: - /* Shouldn't happen. */ - return io_close(conn); + ai.ai_family = AF_UNIX; + sun.sun_family = AF_UNIX; + memcpy(&sun.sun_path, reach->addr.addr, sizeof(sun.sun_path)); + ai.ai_addrlen = sizeof(sun); + ai.ai_addr = (struct sockaddr *)&sun; + break; } io_set_finish(conn, connect_failed, reach); @@ -1915,6 +1950,9 @@ static void try_reach_peer(struct daemon *daemon, const struct pubkey *id, case ADDR_TYPE_IPV6: fd = socket(AF_INET6, SOCK_STREAM, 0); break; + case ADDR_TYPE_PADDING: + fd = socket(AF_UNIX, SOCK_STREAM, 0); + break; default: fd = -1; errno = EPROTONOSUPPORT; diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 60e879f14f3a..76b4dbbb2244 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -23,6 +23,8 @@ gossipctl_init,,no_reconnect,bool gossipctl_activate,3025 # If non-zero, port to listen on. gossipctl_activate,,port,u16 +gossipctl_activate,,lslen,u16 +gossipctl_activate,,localsocket,lslen*u8 # Gossipd->master, I am ready. gossipctl_activate_reply,3125 diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 09a3227686cd..181879b0ae09 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -227,7 +227,7 @@ static void gossip_activate_done(struct subd *gossip UNUSED, void gossip_activate(struct lightningd *ld) { - const u8 *msg = towire_gossipctl_activate(NULL, ld->portnum); + const u8 *msg = towire_gossipctl_activate(NULL, ld->portnum, ld->localsocket_filename); subd_req(ld->gossip, ld->gossip, take(msg), -1, 0, gossip_activate_done, NULL); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 8ed43d17822b..3dec72d4686b 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -303,6 +303,9 @@ int main(int argc, char *argv[]) if (!ld->daemon_dir) errx(1, "Could not find daemons"); + /* Don't accept local connections by default */ + ld->localsocket_filename = NULL; + register_opts(ld); /* Handle options and config; move to .lightningd */ diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 0287a612a8ea..2e9273d688b5 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -89,6 +89,9 @@ struct lightningd { char *config_dir; char *rpc_filename; + /* Local socket for incoming connections */ + u8 *localsocket_filename; + /* Configuration settings. */ struct config config; diff --git a/lightningd/options.c b/lightningd/options.c index b35cb8ee3b72..8c3dde132ac9 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -155,6 +155,19 @@ static char *opt_add_ipaddr(const char *arg, struct lightningd *ld) } +static char *opt_add_localsocket(const char *arg, struct lightningd *ld) +{ + assert(arg != NULL); + + ld->localsocket_filename = tal_free(ld->localsocket_filename); + + if (strlen(arg) > 108) + return tal_fmt(NULL, "Local socket path '%s' is over 108 characters", arg); + ld->localsocket_filename = tal_arrz(ld, u8, strlen(arg)); + strncpy((char*)ld->localsocket_filename, arg, strlen(arg)); + return NULL; +} + static void opt_show_u64(char buf[OPT_SHOW_LEN], const u64 *u) { snprintf(buf, OPT_SHOW_LEN, "%"PRIu64, *u); @@ -319,6 +332,9 @@ static void config_register_opts(struct lightningd *ld) opt_register_arg("--ipaddr", opt_add_ipaddr, NULL, ld, "Set the IP address (v4 or v6) to announce to the network for incoming connections"); + opt_register_arg("--local-socket", opt_add_localsocket, NULL, + ld, + "Set a local socket for incoming connections"); opt_register_noarg("--offline", opt_set_offline, ld, "Start in offline-mode (do not automatically reconnect and do not accept incoming connections"); @@ -859,6 +875,8 @@ static void add_config(struct lightningd *ld, answer = tal_hexstr(name0, ld->rgb, 3); } else if (opt->cb_arg == (void *)opt_set_alias) { answer = (const char *)ld->alias; + } else if (opt->cb_arg == (void *)opt_add_localsocket) { + answer = (const char *)ld->localsocket_filename; } else if (opt->cb_arg == (void *)arg_log_to_file) { answer = ld->logfile; } else if (opt->cb_arg == (void *)opt_set_fee_rates) {