From 3d5367278b5acfb2a1bda1f1b651ab4fd1e6c770 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 May 2023 13:58:18 +0930 Subject: [PATCH] lightningd: allow --bind=ws: Changelog-Added: Config: `bind=ws:...` to explicitly listen on a websocket. Signed-off-by: Rusty Russell --- common/test/run-wireaddr.c | 44 ++++++++++++++++++++++++++++++++++++++ common/wireaddr.c | 17 ++++++++++++--- lightningd/options.c | 7 ++++++ 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/common/test/run-wireaddr.c b/common/test/run-wireaddr.c index cb26f3a9e..d5f72b251 100644 --- a/common/test/run-wireaddr.c +++ b/common/test/run-wireaddr.c @@ -232,6 +232,50 @@ int main(int argc, char *argv[]) strcpy(expect->u.sockname, "/tmp/foo.sock"); assert(wireaddr_internal_eq(&addr, expect)); + /* Websocket (only for IP addresses) */ + assert(parse_wireaddr_internal(tmpctx, "ws:/tmp/foo.sock", DEFAULT_PORT, false, &addr) != NULL); + + assert(parse_wireaddr_internal(tmpctx, "ws:127.0.0.1", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_WIREADDR; + expect->u.wireaddr.is_websocket = true; + assert(parse_wireaddr(tmpctx, "127.0.0.1:9735", 0, NULL, &expect->u.wireaddr.wireaddr) == NULL); + assert(wireaddr_internal_eq(&addr, expect)); + + /* Websocket: IPv4 address with port. */ + assert(parse_wireaddr_internal(tmpctx, "ws:127.0.0.1:1", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_WIREADDR; + expect->u.wireaddr.is_websocket = true; + assert(parse_wireaddr(tmpctx, "127.0.0.1:1", 0, NULL, &expect->u.wireaddr.wireaddr) == NULL); + assert(wireaddr_internal_eq(&addr, expect)); + + /* Websocket: Simple IPv6 address. */ + assert(parse_wireaddr_internal(tmpctx, "ws:::1", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_WIREADDR; + expect->u.wireaddr.is_websocket = true; + assert(parse_wireaddr(tmpctx, "::1", DEFAULT_PORT, NULL, &expect->u.wireaddr.wireaddr) == NULL); + assert(wireaddr_internal_eq(&addr, expect)); + + /* Websocket: IPv6 address with port. */ + assert(parse_wireaddr_internal(tmpctx, "ws:[::1]:1", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_WIREADDR; + expect->u.wireaddr.is_websocket = true; + assert(parse_wireaddr(tmpctx, "::1", 1, NULL, &expect->u.wireaddr.wireaddr) == NULL); + assert(wireaddr_internal_eq(&addr, expect)); + + /* Websocket: IPv4 & v6 address. */ + assert(parse_wireaddr_internal(tmpctx, "ws:", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_ALLPROTO; + expect->u.allproto.is_websocket = true; + expect->u.allproto.port = DEFAULT_PORT; + assert(wireaddr_internal_eq(&addr, expect)); + + /* Websocket: IPv4 & v6 address with port */ + assert(parse_wireaddr_internal(tmpctx, "ws::1", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_ALLPROTO; + expect->u.allproto.is_websocket = true; + expect->u.allproto.port = 1; + assert(wireaddr_internal_eq(&addr, expect)); + /* Unresolved */ assert(parse_wireaddr_internal(tmpctx, "ozlabs.org", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_FORPROXY; diff --git a/common/wireaddr.c b/common/wireaddr.c index c9fc18aff..174c5c22c 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -614,7 +614,7 @@ const char *parse_wireaddr_internal(const tal_t *ctx, char *ip; char *service_addr; const char *err; - bool needed_dns; + bool needed_dns, is_websocket; /* Addresses starting with '/' are local socket paths */ if (arg[0] == '/') { @@ -697,6 +697,14 @@ const char *parse_wireaddr_internal(const tal_t *ctx, &addr->u.torservice.address); } + /* Do this before we get to separate_address_and_port! */ + if (strstarts(arg, "ws:")) { + arg += 3; + is_websocket = true; + } else { + is_websocket = false; + } + /* This can fail, but that may be OK! */ splitport = default_port; if (!separate_address_and_port(tmpctx, arg, &ip, &splitport)) @@ -706,7 +714,7 @@ const char *parse_wireaddr_internal(const tal_t *ctx, * means just IPv6, and IPv4 gets autobound). */ if (ip && streq(ip, "")) { addr->itype = ADDR_INTERNAL_ALLPROTO; - addr->u.allproto.is_websocket = false; + addr->u.allproto.is_websocket = is_websocket; addr->u.allproto.port = splitport; return NULL; } @@ -717,7 +725,7 @@ const char *parse_wireaddr_internal(const tal_t *ctx, &addr->u.wireaddr.wireaddr); if (!err) { addr->itype = ADDR_INTERNAL_WIREADDR; - addr->u.wireaddr.is_websocket = false; + addr->u.wireaddr.is_websocket = is_websocket; return NULL; } @@ -729,6 +737,9 @@ const char *parse_wireaddr_internal(const tal_t *ctx, if (!ip) return "Malformed port"; + if (is_websocket) + return "Could not resolve websocket address"; + /* Keep unresolved. */ tal_free(err); if (!wireaddr_from_unresolved(addr, ip, splitport)) diff --git a/lightningd/options.c b/lightningd/options.c index 6a59c5ca9..3d112b657 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -271,6 +271,9 @@ static char *opt_add_addr_withtype(const char *arg, switch (wi.u.wireaddr.wireaddr.type) { case ADDR_TYPE_IPV4: case ADDR_TYPE_IPV6: + if ((ala & ADDR_ANNOUNCE) && wi.u.allproto.is_websocket) + return tal_fmt(NULL, + "Cannot announce websocket address, use --bind-addr=%s", arg); /* These can be either bind or announce */ break; case ADDR_TYPE_TOR_V2_REMOVED: @@ -363,6 +366,10 @@ static char *opt_add_addr_withtype(const char *arg, case ADDR_ANNOUNCE: return tal_fmt(NULL, "Cannot use wildcard address '%s'", arg); case ADDR_LISTEN_AND_ANNOUNCE: + if (wi.u.allproto.is_websocket) + return tal_fmt(NULL, + "Cannot announce websocket address, use --bind-addr=%s", arg); + /* fall thru */ case ADDR_LISTEN: break; }