]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-socket-graveyard.c
tree-wide: use ASSERT_PTR more
[thirdparty/systemd.git] / src / resolve / resolved-socket-graveyard.c
CommitLineData
948def4a 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
80710ade
LP
2
3#include "resolved-socket-graveyard.h"
4
5#define SOCKET_GRAVEYARD_USEC (5 * USEC_PER_SEC)
6#define SOCKET_GRAVEYARD_MAX 100
7
8/* This implements a socket "graveyard" for UDP sockets. If a socket fd is added to the graveyard it is kept
9 * open for a couple of more seconds, expecting one reply. Once the reply is received the fd is closed
10 * immediately, or if none is received it is closed after the timeout. Why all this? So that if we contact a
11 * DNS server, and it doesn't reply instantly, and we lose interest in the response and thus close the fd, we
12 * don't end up sending back an ICMP error once the server responds but we aren't listening anymore. (See
13 * https://github.com/systemd/systemd/issues/17421 for further information.)
14 *
15 * Note that we don't allocate any timer event source to clear up the graveyard once the socket's timeout is
16 * reached. Instead we operate lazily: we close old entries when adding a new fd to the graveyard, or
17 * whenever any code runs manager_socket_graveyard_process() — which the DNS transaction code does right
18 * before allocating a new UDP socket. */
19
20static SocketGraveyard* socket_graveyard_free(SocketGraveyard *g) {
21 if (!g)
22 return NULL;
23
24 if (g->manager) {
25 assert(g->manager->n_socket_graveyard > 0);
26 g->manager->n_socket_graveyard--;
27
28 if (g->manager->socket_graveyard_oldest == g)
29 g->manager->socket_graveyard_oldest = g->graveyard_prev;
30
31 LIST_REMOVE(graveyard, g->manager->socket_graveyard, g);
32
33 assert((g->manager->n_socket_graveyard > 0) == !!g->manager->socket_graveyard);
34 assert((g->manager->n_socket_graveyard > 0) == !!g->manager->socket_graveyard_oldest);
35 }
36
37 if (g->io_event_source) {
38 log_debug("Closing graveyard socket fd %i", sd_event_source_get_io_fd(g->io_event_source));
97935302 39 sd_event_source_disable_unref(g->io_event_source);
80710ade
LP
40 }
41
42 return mfree(g);
43}
44
45DEFINE_TRIVIAL_CLEANUP_FUNC(SocketGraveyard*, socket_graveyard_free);
46
47void manager_socket_graveyard_process(Manager *m) {
48 usec_t n = USEC_INFINITY;
49
50 assert(m);
51
52 while (m->socket_graveyard_oldest) {
53 SocketGraveyard *g = m->socket_graveyard_oldest;
54
55 if (n == USEC_INFINITY)
ba4e0427 56 assert_se(sd_event_now(m->event, CLOCK_BOOTTIME, &n) >= 0);
80710ade
LP
57
58 if (g->deadline > n)
59 break;
60
61 socket_graveyard_free(g);
62 }
63}
64
65void manager_socket_graveyard_clear(Manager *m) {
66 assert(m);
67
68 while (m->socket_graveyard)
69 socket_graveyard_free(m->socket_graveyard);
70}
71
72static int on_io_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
99534007 73 SocketGraveyard *g = ASSERT_PTR(userdata);
80710ade
LP
74
75 /* An IO event happened on the graveyard fd. We don't actually care which event that is, and we don't
76 * read any incoming packet off the socket. We just close the fd, that's enough to not trigger the
77 * ICMP unreachable port event */
78
79 socket_graveyard_free(g);
80 return 0;
81}
82
83static void manager_socket_graveyard_make_room(Manager *m) {
84 assert(m);
85
86 while (m->n_socket_graveyard >= SOCKET_GRAVEYARD_MAX)
87 socket_graveyard_free(m->socket_graveyard_oldest);
88}
89
90int manager_add_socket_to_graveyard(Manager *m, int fd) {
91 _cleanup_(socket_graveyard_freep) SocketGraveyard *g = NULL;
92 int r;
93
94 assert(m);
95 assert(fd >= 0);
96
97 manager_socket_graveyard_process(m);
98 manager_socket_graveyard_make_room(m);
99
100 g = new(SocketGraveyard, 1);
101 if (!g)
102 return log_oom();
103
104 *g = (SocketGraveyard) {
105 .manager = m,
106 };
107
108 LIST_PREPEND(graveyard, m->socket_graveyard, g);
109 if (!m->socket_graveyard_oldest)
110 m->socket_graveyard_oldest = g;
111
112 m->n_socket_graveyard++;
113
ba4e0427 114 assert_se(sd_event_now(m->event, CLOCK_BOOTTIME, &g->deadline) >= 0);
80710ade
LP
115 g->deadline += SOCKET_GRAVEYARD_USEC;
116
117 r = sd_event_add_io(m->event, &g->io_event_source, fd, EPOLLIN, on_io_event, g);
118 if (r < 0)
119 return log_error_errno(r, "Failed to create graveyard IO source: %m");
120
121 r = sd_event_source_set_io_fd_own(g->io_event_source, true);
122 if (r < 0)
123 return log_error_errno(r, "Failed to enable graveyard IO source fd ownership: %m");
124
125 (void) sd_event_source_set_description(g->io_event_source, "graveyard");
126
127 log_debug("Added socket %i to graveyard", fd);
128
129 TAKE_PTR(g);
130 return 0;
131}