mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
connectd: add is_websocket and wireaddr to struct listen_fd.
This lets us give a better error message if listen fails, and also moved the callback closer to where it's needed. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
@@ -1023,6 +1023,15 @@ next:
|
|||||||
try_connect_one_addr(connect);
|
try_connect_one_addr(connect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*~ Sometimes it's nice to have an explicit enum instead of a bool to make
|
||||||
|
* arguments clearer: it kind of hacks around C's lack of naming formal
|
||||||
|
* arguments in callers (e.g. in Python we'd simply call func(websocket=False)).
|
||||||
|
*/
|
||||||
|
enum is_websocket {
|
||||||
|
NORMAL_SOCKET,
|
||||||
|
WEBSOCKET,
|
||||||
|
};
|
||||||
|
|
||||||
/*~ connectd is responsible for incoming connections, but it's the process of
|
/*~ connectd is responsible for incoming connections, but it's the process of
|
||||||
* setting up the listening ports which gives us information we need for startup
|
* setting up the listening ports which gives us information we need for startup
|
||||||
* (such as our own address). So we perform setup in two phases: first we bind
|
* (such as our own address). So we perform setup in two phases: first we bind
|
||||||
@@ -1031,28 +1040,33 @@ next:
|
|||||||
*
|
*
|
||||||
* This stores the fds we're going to listen on: */
|
* This stores the fds we're going to listen on: */
|
||||||
struct listen_fd {
|
struct listen_fd {
|
||||||
|
/* This is usually an IPv4/v6 address, but we also support local
|
||||||
|
* domain sockets (i.e. filesystem) */
|
||||||
|
struct wireaddr_internal wi;
|
||||||
|
/* The actual fd, ready to listen() on */
|
||||||
int fd;
|
int fd;
|
||||||
/* If we bind() IPv6 then IPv4 to same port, we *may* fail to listen()
|
/* If we bind() IPv6 then IPv4 to same port, we *may* fail to listen()
|
||||||
* on the IPv4 socket: under Linux, by default, the IPv6 listen()
|
* on the IPv4 socket: under Linux, by default, the IPv6 listen()
|
||||||
* covers IPv4 too. Normally we'd consider failing to listen on a
|
* covers IPv4 too. Normally we'd consider failing to listen on a
|
||||||
* port to be fatal, so we note this when setting up addresses. */
|
* port to be fatal, so we note this when setting up addresses. */
|
||||||
bool mayfail;
|
bool mayfail;
|
||||||
/* Callback to use for the listening: either connection_in, or for
|
/* Is this a websocket? */
|
||||||
* our much-derided WebSocket ability, websocket_connection_in! */
|
enum is_websocket is_websocket;
|
||||||
struct io_plan *(*in_cb)(struct io_conn *conn, struct daemon *daemon);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void add_listen_fd(struct daemon *daemon, int fd, bool mayfail,
|
static void add_listen_fd(struct daemon *daemon,
|
||||||
struct io_plan *(*in_cb)(struct io_conn *,
|
const struct wireaddr_internal *wi,
|
||||||
struct daemon *))
|
int fd, bool mayfail,
|
||||||
|
enum is_websocket is_websocket)
|
||||||
{
|
{
|
||||||
/*~ utils.h contains a convenience macro tal_arr_expand which
|
/*~ utils.h contains a convenience macro tal_arr_expand which
|
||||||
* reallocates a tal_arr to make it one longer, then returns a pointer
|
* reallocates a tal_arr to make it one longer, then returns a pointer
|
||||||
* to the (new) last element. */
|
* to the (new) last element. */
|
||||||
struct listen_fd l;
|
struct listen_fd l;
|
||||||
|
l.wi = *wi;
|
||||||
l.fd = fd;
|
l.fd = fd;
|
||||||
l.mayfail = mayfail;
|
l.mayfail = mayfail;
|
||||||
l.in_cb = in_cb;
|
l.is_websocket = is_websocket;
|
||||||
tal_arr_expand(&daemon->listen_fds, l);
|
tal_arr_expand(&daemon->listen_fds, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1118,19 +1132,13 @@ fail:
|
|||||||
static bool handle_wireaddr_listen(const tal_t *ctx,
|
static bool handle_wireaddr_listen(const tal_t *ctx,
|
||||||
struct daemon *daemon,
|
struct daemon *daemon,
|
||||||
const struct wireaddr_internal *wi,
|
const struct wireaddr_internal *wi,
|
||||||
bool websocket,
|
enum is_websocket is_websocket,
|
||||||
char **errstr)
|
char **errstr)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
struct sockaddr_in6 addr6;
|
struct sockaddr_in6 addr6;
|
||||||
const struct wireaddr *wireaddr;
|
const struct wireaddr *wireaddr;
|
||||||
struct io_plan *(*in_cb)(struct io_conn *, struct daemon *);
|
|
||||||
|
|
||||||
if (websocket)
|
|
||||||
in_cb = websocket_connection_in;
|
|
||||||
else
|
|
||||||
in_cb = connection_in;
|
|
||||||
|
|
||||||
assert(wi->itype == ADDR_INTERNAL_WIREADDR);
|
assert(wi->itype == ADDR_INTERNAL_WIREADDR);
|
||||||
wireaddr = &wi->u.wireaddr;
|
wireaddr = &wi->u.wireaddr;
|
||||||
@@ -1144,9 +1152,9 @@ static bool handle_wireaddr_listen(const tal_t *ctx,
|
|||||||
fd = make_listen_fd(ctx, daemon, wi, AF_INET, &addr, sizeof(addr), errstr);
|
fd = make_listen_fd(ctx, daemon, wi, AF_INET, &addr, sizeof(addr), errstr);
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
status_debug("Created IPv4 %slistener on port %u",
|
status_debug("Created IPv4 %slistener on port %u",
|
||||||
websocket ? "websocket ": "",
|
is_websocket ? "websocket ": "",
|
||||||
wireaddr->port);
|
wireaddr->port);
|
||||||
add_listen_fd(daemon, fd, errstr == NULL, in_cb);
|
add_listen_fd(daemon, wi, fd, errstr == NULL, is_websocket);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -1155,9 +1163,9 @@ static bool handle_wireaddr_listen(const tal_t *ctx,
|
|||||||
fd = make_listen_fd(ctx, daemon, wi, AF_INET6, &addr6, sizeof(addr6), errstr);
|
fd = make_listen_fd(ctx, daemon, wi, AF_INET6, &addr6, sizeof(addr6), errstr);
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
status_debug("Created IPv6 %slistener on port %u",
|
status_debug("Created IPv6 %slistener on port %u",
|
||||||
websocket ? "websocket ": "",
|
is_websocket ? "websocket ": "",
|
||||||
wireaddr->port);
|
wireaddr->port);
|
||||||
add_listen_fd(daemon, fd, errstr == NULL, in_cb);
|
add_listen_fd(daemon, wi, fd, errstr == NULL, is_websocket);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -1288,7 +1296,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx,
|
|||||||
return NULL;
|
return NULL;
|
||||||
status_debug("Created socket listener on file %s",
|
status_debug("Created socket listener on file %s",
|
||||||
addrun.sun_path);
|
addrun.sun_path);
|
||||||
add_listen_fd(daemon, fd, false, connection_in);
|
add_listen_fd(daemon, &wa, fd, false, NORMAL_SOCKET);
|
||||||
/* We don't announce socket names, though we allow
|
/* We don't announce socket names, though we allow
|
||||||
* them to lazily specify --addr=/socket. */
|
* them to lazily specify --addr=/socket. */
|
||||||
add_binding(&binding, &wa);
|
add_binding(&binding, &wa);
|
||||||
@@ -1312,7 +1320,8 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx,
|
|||||||
memset(wa.u.wireaddr.addr, 0,
|
memset(wa.u.wireaddr.addr, 0,
|
||||||
sizeof(wa.u.wireaddr.addr));
|
sizeof(wa.u.wireaddr.addr));
|
||||||
|
|
||||||
ipv6_ok = handle_wireaddr_listen(ctx, daemon, &wa, false, NULL);
|
ipv6_ok = handle_wireaddr_listen(ctx, daemon, &wa,
|
||||||
|
NORMAL_SOCKET, NULL);
|
||||||
if (ipv6_ok) {
|
if (ipv6_ok) {
|
||||||
add_binding(&binding, &wa);
|
add_binding(&binding, &wa);
|
||||||
if (announce
|
if (announce
|
||||||
@@ -1327,7 +1336,8 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx,
|
|||||||
memset(wa.u.wireaddr.addr, 0,
|
memset(wa.u.wireaddr.addr, 0,
|
||||||
sizeof(wa.u.wireaddr.addr));
|
sizeof(wa.u.wireaddr.addr));
|
||||||
/* OK if this fails, as long as one succeeds! */
|
/* OK if this fails, as long as one succeeds! */
|
||||||
if (handle_wireaddr_listen(ctx, daemon, &wa, false, ipv6_ok ? NULL : errstr)) {
|
if (handle_wireaddr_listen(ctx, daemon, &wa,
|
||||||
|
NORMAL_SOCKET, ipv6_ok ? NULL : errstr)) {
|
||||||
add_binding(&binding, &wa);
|
add_binding(&binding, &wa);
|
||||||
if (announce
|
if (announce
|
||||||
&& public_address(daemon, &wa.u.wireaddr))
|
&& public_address(daemon, &wa.u.wireaddr))
|
||||||
@@ -1341,7 +1351,8 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx,
|
|||||||
}
|
}
|
||||||
/* This is a vanilla wireaddr as per BOLT #7 */
|
/* This is a vanilla wireaddr as per BOLT #7 */
|
||||||
case ADDR_INTERNAL_WIREADDR:
|
case ADDR_INTERNAL_WIREADDR:
|
||||||
if (!handle_wireaddr_listen(ctx, daemon, &wa, false, errstr))
|
if (!handle_wireaddr_listen(ctx, daemon, &wa,
|
||||||
|
NORMAL_SOCKET, errstr))
|
||||||
return NULL;
|
return NULL;
|
||||||
add_binding(&binding, &wa);
|
add_binding(&binding, &wa);
|
||||||
if (announce && public_address(daemon, &wa.u.wireaddr))
|
if (announce && public_address(daemon, &wa.u.wireaddr))
|
||||||
@@ -1372,7 +1383,8 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx,
|
|||||||
addr = binding[i];
|
addr = binding[i];
|
||||||
addr.u.wireaddr.port = daemon->websocket_port;
|
addr.u.wireaddr.port = daemon->websocket_port;
|
||||||
/* FIXME: catch errors here! */
|
/* FIXME: catch errors here! */
|
||||||
if (handle_wireaddr_listen(ctx, daemon, &addr, true, NULL))
|
if (handle_wireaddr_listen(ctx, daemon, &addr,
|
||||||
|
WEBSOCKET, NULL))
|
||||||
announced_some = true;
|
announced_some = true;
|
||||||
/* FIXME: We don't report these bindings to
|
/* FIXME: We don't report these bindings to
|
||||||
* lightningd, so they don't appear in
|
* lightningd, so they don't appear in
|
||||||
@@ -1564,6 +1576,22 @@ static void connect_init(struct daemon *daemon, const u8 *msg)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returning functions in C is ugly! */
|
||||||
|
static struct io_plan *(*get_in_cb(enum is_websocket is_websocket))(struct io_conn *, struct daemon *)
|
||||||
|
|
||||||
|
{
|
||||||
|
/*~ This switch and fall pattern serves a specific purpose:
|
||||||
|
* gcc will warn if we don't handle every case! */
|
||||||
|
switch (is_websocket) {
|
||||||
|
case WEBSOCKET:
|
||||||
|
return websocket_connection_in;
|
||||||
|
case NORMAL_SOCKET:
|
||||||
|
return connection_in;
|
||||||
|
}
|
||||||
|
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||||
|
"Invalid is_websocket %u", is_websocket);
|
||||||
|
}
|
||||||
|
|
||||||
/*~ lightningd tells us to go! */
|
/*~ lightningd tells us to go! */
|
||||||
static void connect_activate(struct daemon *daemon, const u8 *msg)
|
static void connect_activate(struct daemon *daemon, const u8 *msg)
|
||||||
{
|
{
|
||||||
@@ -1575,21 +1603,24 @@ static void connect_activate(struct daemon *daemon, const u8 *msg)
|
|||||||
/* If we're --offline, lightningd tells us not to actually listen. */
|
/* If we're --offline, lightningd tells us not to actually listen. */
|
||||||
if (do_listen) {
|
if (do_listen) {
|
||||||
for (size_t i = 0; i < tal_count(daemon->listen_fds); i++) {
|
for (size_t i = 0; i < tal_count(daemon->listen_fds); i++) {
|
||||||
/* On Linux, at least, we may bind to all addresses
|
|
||||||
* for IPv4 and IPv6, but we'll fail to listen. */
|
|
||||||
if (listen(daemon->listen_fds[i].fd, 64) != 0) {
|
if (listen(daemon->listen_fds[i].fd, 64) != 0) {
|
||||||
if (daemon->listen_fds[i].mayfail)
|
if (daemon->listen_fds[i].mayfail)
|
||||||
continue;
|
continue;
|
||||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||||
"Failed to listen on socket: %s",
|
"Failed to listen on socket %s: %s",
|
||||||
|
type_to_string(tmpctx,
|
||||||
|
struct wireaddr_internal,
|
||||||
|
&daemon->listen_fds[i].wi),
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
}
|
}
|
||||||
notleak(io_new_listener(daemon,
|
notleak(io_new_listener(daemon,
|
||||||
daemon->listen_fds[i].fd,
|
daemon->listen_fds[i].fd,
|
||||||
daemon->listen_fds[i].in_cb,
|
get_in_cb(daemon->listen_fds[i]
|
||||||
|
.is_websocket),
|
||||||
daemon));
|
daemon));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free, with NULL assignment just as an extra sanity check. */
|
/* Free, with NULL assignment just as an extra sanity check. */
|
||||||
daemon->listen_fds = tal_free(daemon->listen_fds);
|
daemon->listen_fds = tal_free(daemon->listen_fds);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user