Specifically, I believe the problem lies inside daemon.c (however, I may be wrong as I have not tested)
because reading less bytes than the provided buffer size does not seem to mean that the next read will return EAGAIN (hence the socket could very well remain read-ready). This can be shown with the following program exhibiting the same connection leak.
#include <sys/socket.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <unistd.h>
union sockaddr_any
{
struct sockaddr addr;
struct sockaddr_in ipv4;
struct sockaddr_in6 ipv6;
};
static int xsocket(int flags)
{
int sock = socket(AF_INET, SOCK_STREAM | flags, 0);
if (sock < 0)
{
exit(EXIT_FAILURE);
}
return sock;
}
static int xepoll_create1(int flags)
{
int epfd = epoll_create1(flags);
if (epfd < 0)
{
exit(EXIT_FAILURE);
}
return epfd;
}
static void xepoll_add(int epfd, int fd, uint32_t events)
{
struct epoll_event event;
int error;
event.events = events;
event.data.fd = fd;
error = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
if (error)
{
exit(EXIT_FAILURE);
}
}
static void xreuseaddr(int sock, int on)
{
int err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
if (err) exit(EXIT_FAILURE);
}
static void xbind(int sock, uint32_t addr, in_port_t port)
{
union sockaddr_any any;
int err;
any.ipv4.sin_family = AF_INET;
any.ipv4.sin_addr.s_addr = htonl(addr);
any.ipv4.sin_port = htons(port);
err = bind(sock, &any.addr, sizeof any.ipv4);
if (err) exit(EXIT_FAILURE);
}
static void xlisten(int sock, int backlog)
{
int err = listen(sock, backlog);
if (err) exit(EXIT_FAILURE);
}
static void slurp(int fd)
{
static unsigned char buffer [4096];
ssize_t nbytes = sizeof buffer;
while (nbytes == sizeof buffer)
{
nbytes = read(fd, buffer, sizeof buffer);
}
if (!nbytes) close(fd);
}
int main(void)
{
int sock = xsocket(SOCK_NONBLOCK);
int epfd = xepoll_create1(0);
xreuseaddr(sock, 1);
xbind(sock, 0x7f000001, 8080);
xepoll_add(epfd, sock, EPOLLIN | EPOLLET);
xlisten(sock, 32);
for (;;)
{
struct epoll_event ev;
if (epoll_wait(epfd, &ev, 1, -1) != 1)
{
continue;
}
if (ev.data.fd == sock)
{
int conn = accept(sock, 0, 0);
xepoll_add(epfd, conn, EPOLLIN | EPOLLET);
continue;
}
slurp(ev.data.fd);
}
return EXIT_SUCCESS;
}
Closed connections are properly terminated.