From 926b41b7da8e4506da6a507c7f2068ee6e88381a Mon Sep 17 00:00:00 2001 From: ZmnSCPxj Date: Sat, 5 May 2018 04:57:58 +0000 Subject: [PATCH] closingd: Ensure proper closing of TCP socket. Fixes: #1457 --- closingd/Makefile | 1 + closingd/closing.c | 6 ++++++ common/Makefile | 1 + common/socket_close.c | 47 +++++++++++++++++++++++++++++++++++++++++++ common/socket_close.h | 23 +++++++++++++++++++++ 5 files changed, 78 insertions(+) create mode 100644 common/socket_close.c create mode 100644 common/socket_close.h diff --git a/closingd/Makefile b/closingd/Makefile index 3834f5074f7a..2e4e93191fd8 100644 --- a/closingd/Makefile +++ b/closingd/Makefile @@ -60,6 +60,7 @@ CLOSINGD_COMMON_OBJS := \ common/permute_tx.o \ common/ping.o \ common/read_peer_msg.o \ + common/socket_close.o \ common/status.o \ common/status_wire.o \ common/subdaemon.o \ diff --git a/closingd/closing.c b/closingd/closing.c index d60cdbfb41f2..54ada2ce4780 100644 --- a/closingd/closing.c +++ b/closingd/closing.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -581,6 +582,11 @@ int main(int argc, char *argv[]) offer[LOCAL]); /* We're done! */ + /* Properly close the channel first. */ + if (!socket_close(PEER_FD)) + status_unusual("Closing and draining peerfd gave error: %s", + strerror(errno)); + /* Sending the below will kill us! */ wire_sync_write(REQ_FD, take(towire_closing_complete(NULL))); tal_free(ctx); daemon_shutdown(); diff --git a/common/Makefile b/common/Makefile index 94d4aa2dbca4..be000c92ec2b 100644 --- a/common/Makefile +++ b/common/Makefile @@ -33,6 +33,7 @@ COMMON_SRC_NOGEN := \ common/ping.c \ common/pseudorand.c \ common/read_peer_msg.c \ + common/socket_close.c \ common/sphinx.c \ common/status.c \ common/status_wire.c \ diff --git a/common/socket_close.c b/common/socket_close.c new file mode 100644 index 000000000000..cf9cbcf98180 --- /dev/null +++ b/common/socket_close.c @@ -0,0 +1,47 @@ +#include "socket_close.h" +#include +#include +#include +#include + +/* +Simplified (minus all the error checks): + + shutdown(fd, SHUT_WR); + for (;;) { + char unused[64] + sys_res = read(fd, unused, 64); + if (sys_res == 0) + break; + } + close(fd); +*/ + +bool socket_close(int fd) +{ + char unused[64]; + int sys_res; + + sys_res = shutdown(fd, SHUT_WR); + if (sys_res < 0) { + close_noerr(fd); + return false; + } + + for (;;) { + do { + sys_res = read(fd, unused, sizeof(unused)); + } while (sys_res < 0 && errno == EINTR); + if (sys_res < 0) { + close_noerr(fd); + return false; + } + if (sys_res == 0) + break; + } + + if (close(fd) < 0) + return false; + else + return true; +} diff --git a/common/socket_close.h b/common/socket_close.h new file mode 100644 index 000000000000..8fc8030f9511 --- /dev/null +++ b/common/socket_close.h @@ -0,0 +1,23 @@ +/* common/socket_close - Properly close a socket, + * ensuring that any data we write just before + * the close has been transmitted to the other + * side, and ignoring any data the other side + * has sent at the time the close was started. + * + * Reference: + * + * http://ia800504.us.archive.org/3/items/TheUltimateSo_lingerPageOrWhyIsMyTcpNotReliable/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable.html + */ +#ifndef LIGHTNING_COMMON_SOCKET_CLOSE_H +#define LIGHTNING_COMMON_SOCKET_CLOSE_H +#include "config.h" +#include + +/* Return false if something failed, true if + * nothing failed. + * If something failed, error is stored in + * `errno. + */ +bool socket_close(int fd); + +#endif /* LIGHTNING_COMMON_SOCKET_CLOSE_H */