• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 20684862027

03 Jan 2026 10:26PM UTC coverage: 72.702% (+0.03%) from 72.677%
20684862027

push

github

web-flow
core/dynamic-user: two trivial modernizations (#40264)

2 of 4 new or added lines in 1 file covered. (50.0%)

215 existing lines in 37 files now uncovered.

310139 of 426587 relevant lines covered (72.7%)

1143601.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.23
/src/resolve/test-resolved-stream.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <net/if.h>
5
#include <pthread.h>
6
#include <signal.h>
7
#include <stdlib.h>
8
#include <string.h>
9
#include <sys/ioctl.h>
10
#include <sys/socket.h>
11
#include <unistd.h>
12

13
#include "sd-event.h"
14

15
#include "dns-packet.h"
16
#include "dns-question.h"
17
#include "dns-rr.h"
18
#include "errno-util.h"
19
#include "fd-util.h"
20
#include "log.h"
21
#include "path-util.h"
22
#include "pidref.h"
23
#include "process-util.h"
24
#include "random-util.h"
25
#include "resolved-dns-server.h"
26
#include "resolved-dns-stream.h"
27
#include "resolved-dnstls.h"
28
#include "resolved-manager.h"
29
#include "sparse-endian.h"
30
#include "tests.h"
31
#include "time-util.h"
32

33
static union sockaddr_union server_address;
34

35
/* Bytes of the questions & answers used in the test, including TCP DNS 2-byte length prefix */
36
static const uint8_t QUESTION_A[] =  {
37
        0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
38
        'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x01, 0x00, 0x01
39
};
40
static const uint8_t QUESTION_AAAA[] =  {
41
        0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
42
        'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x1C, 0x00, 0x01
43
};
44
static const uint8_t ANSWER_A[] =  {
45
        0x00, 0x2D, 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
46
        'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0,
47
        0x0C, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x52, 0x8D, 0x00, 0x04, 0x5D, 0xB8, 0xD8, 0x22,
48
};
49
static const uint8_t ANSWER_AAAA[] =  {
50
        0x00, 0x39, 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
51
        'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x1C, 0x00, 0x01, 0xC0,
52
        0x0C, 0x00, 0x1C, 0x00, 0x01, 0x00, 0x00, 0x54, 0x4B, 0x00, 0x10, 0x26, 0x06, 0x28, 0x00, 0x02,
53
        0x20, 0x00, 0x01, 0x02, 0x48, 0x18, 0x93, 0x25, 0xC8, 0x19, 0x46,
54
};
55

56
/**
57
 * A mock TCP DNS server that asserts certain questions are received
58
 * and replies with the same answer every time.
59
 */
60
static void receive_and_check_question(int fd, const uint8_t *expected_question,
8✔
61
                                       size_t question_size) {
62
        uint8_t *actual_question;
8✔
63
        size_t n_read = 0;
8✔
64

65
        actual_question = newa(uint8_t, question_size);
8✔
66
        while (n_read < question_size) {
16✔
67
                ssize_t r = read(fd, actual_question + n_read, question_size - n_read);
8✔
68
                assert_se(r >= 0);
8✔
69
                n_read += (size_t)r;
8✔
70
        }
71
        assert_se(n_read == question_size);
8✔
72

73
        assert_se(memcmp(expected_question, actual_question, question_size) == 0);
8✔
74
}
8✔
75

76
static void send_answer(int fd, const uint8_t *answer, size_t answer_size) {
4✔
77
        assert_se(write(fd, answer, answer_size) == (ssize_t)answer_size);
4✔
78
}
4✔
79

80
/* Sends two answers together in a single write operation,
81
 * so they hopefully end up in a single TCP packet / TLS record */
82
static void send_answers_together(int fd,
2✔
83
                                  const uint8_t *answer1, size_t answer1_size,
84
                                  const uint8_t *answer2, size_t answer2_size) {
85
        uint8_t *answer;
2✔
86
        size_t answer_size = answer1_size + answer2_size;
2✔
87

88
        answer = newa(uint8_t, answer_size);
2✔
89
        memcpy(answer, answer1, answer1_size);
2✔
90
        memcpy(answer + answer1_size, answer2, answer2_size);
2✔
91
        assert_se(write(fd, answer, answer_size) == (ssize_t)answer_size);
2✔
92
}
2✔
93

94
static void server_handle(int fd) {
2✔
95
        receive_and_check_question(fd, QUESTION_A, sizeof(QUESTION_A));
2✔
96
        send_answer(fd, ANSWER_A, sizeof(ANSWER_A));
2✔
97

98
        receive_and_check_question(fd, QUESTION_AAAA, sizeof(QUESTION_AAAA));
2✔
99
        send_answer(fd, ANSWER_AAAA, sizeof(ANSWER_AAAA));
2✔
100

101
        receive_and_check_question(fd, QUESTION_A, sizeof(QUESTION_A));
2✔
102
        receive_and_check_question(fd, QUESTION_AAAA, sizeof(QUESTION_AAAA));
2✔
103
        send_answers_together(fd, ANSWER_A, sizeof(ANSWER_A),
2✔
104
                                  ANSWER_AAAA, sizeof(ANSWER_AAAA));
105
}
2✔
106

107
static void *tcp_dns_server(void *p) {
1✔
108
        _cleanup_close_ int bindfd = -EBADF, acceptfd = -EBADF;
1✔
109

110
        assert_se((bindfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) >= 0);
1✔
111
        assert_se(setsockopt(bindfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) >= 0);
1✔
112
        assert_se(bind(bindfd, &server_address.sa, sockaddr_len(&server_address)) >= 0);
1✔
113
        assert_se(listen(bindfd, 1) >= 0);
1✔
114
        assert_se((acceptfd = accept(bindfd, NULL, NULL)) >= 0);
1✔
115
        server_handle(acceptfd);
1✔
116
        return NULL;
2✔
117
}
118

119
#if ENABLE_DNS_OVER_TLS
120
/*
121
 * Spawns a DNS TLS server using the command line "openssl s_server" tool.
122
 */
123
static void *tls_dns_server(void *p) {
1✔
124
        int r;
1✔
125
        _cleanup_close_ int fd_server = -EBADF, fd_tls = -EBADF;
1✔
126
        _cleanup_free_ char *cert_path = NULL, *key_path = NULL;
×
127
        _cleanup_free_ char *bind_str = NULL;
1✔
128

129
        assert_se(get_testdata_dir("test-resolve/selfsigned.cert", &cert_path) >= 0);
1✔
130
        assert_se(get_testdata_dir("test-resolve/selfsigned.key", &key_path) >= 0);
1✔
131

132
        assert_se(asprintf(&bind_str, "%s:%d",
1✔
133
                           IN_ADDR_TO_STRING(server_address.in.sin_family,
134
                                             sockaddr_in_addr(&server_address.sa)),
135
                           be16toh(server_address.in.sin_port)) >= 0);
136

137
        /* We will hook one of the socketpair ends to OpenSSL's TLS server
138
         * stdin/stdout, so we will be able to read and write plaintext
139
         * from the other end's file descriptor like an usual TCP server */
140
        {
141
                int fd[2];
1✔
142
                assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) >= 0);
1✔
143
                fd_server = fd[0];
1✔
144
                fd_tls = fd[1];
1✔
145
        }
146

147
        _cleanup_(pidref_done) PidRef openssl_pidref = PIDREF_NULL;
×
148
        r = ASSERT_OK(pidref_safe_fork_full(
1✔
149
                        "(test-resolved-stream-tls-openssl)",
150
                        (int[]) { fd_tls, fd_tls, STDOUT_FILENO },
151
                        NULL, 0,
152
                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG|FORK_REOPEN_LOG,
153
                        &openssl_pidref));
154
        if (r == 0) {
2✔
155
                /* Child */
156
                execlp("openssl", "openssl", "s_server", "-accept", bind_str,
1✔
157
                       "-key", key_path, "-cert", cert_path,
158
                       "-quiet", "-naccept", "1", NULL);
159
                log_error("exec failed, is something wrong with the 'openssl' command?");
1✔
160
                _exit(EXIT_FAILURE);
1✔
161
        } else {
162
                pthread_mutex_t *server_lock = (pthread_mutex_t *)p;
1✔
163

164
                server_handle(fd_server);
1✔
165

166
                /* Once the test is done kill the TLS server to release the port */
167
                assert_se(pthread_mutex_lock(server_lock) == 0);
1✔
168
                assert_se(pidref_kill(&openssl_pidref, SIGTERM) >= 0);
1✔
169
                assert_se(pidref_wait_for_terminate(&openssl_pidref, NULL) >= 0);
1✔
170
                assert_se(pthread_mutex_unlock(server_lock) == 0);
1✔
171
        }
172

173
        return NULL;
2✔
174
}
175
#endif
176

177
static const char *TEST_DOMAIN = "example.com";
178
static const uint64_t EVENT_TIMEOUT_USEC = 5 * 1000 * 1000;
179

180
static void send_simple_question(DnsStream *stream, uint16_t type) {
8✔
181
        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
8✔
182
        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
8✔
183
        _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
×
184

185
        assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX) >= 0);
8✔
186
        assert_se(question = dns_question_new(1));
8✔
187
        assert_se(key = dns_resource_key_new(DNS_CLASS_IN, type, TEST_DOMAIN));
8✔
188
        assert_se(dns_question_add(question, key, 0) >= 0);
8✔
189
        assert_se(dns_packet_append_question(p, question) >= 0);
8✔
190
        DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(question));
24✔
191
        assert_se(dns_stream_write_packet(stream, p) >= 0);
8✔
192
}
8✔
193

194
static const size_t MAX_RECEIVED_PACKETS = 2;
195
static DnsPacket *received_packets[2] = {};
196
static size_t n_received_packets = 0;
197

198
static int on_stream_packet(DnsStream *stream, DnsPacket *p) {
8✔
199
        assert_se(n_received_packets < MAX_RECEIVED_PACKETS);
8✔
200
        assert_se(received_packets[n_received_packets++] = dns_packet_ref(p));
8✔
201
        return 0;
8✔
202
}
203

UNCOV
204
static int on_stream_complete_do_nothing(DnsStream *s, int error) {
×
UNCOV
205
        return 0;
×
206
}
207

208
static void test_dns_stream(bool tls) {
2✔
209
        Manager manager = {};
2✔
210
         _cleanup_(dns_stream_unrefp) DnsStream *stream = NULL;
×
211
        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
2✔
212
        _cleanup_close_ int clientfd = -EBADF;
2✔
213
        int r;
2✔
214

215
        void *(*server_entrypoint)(void *);
2✔
216
        pthread_t server_thread;
2✔
217
        pthread_mutex_t server_lock;
2✔
218

219
        log_info("test-resolved-stream: Started %s test", tls ? "TLS" : "TCP");
3✔
220

221
#if ENABLE_DNS_OVER_TLS
222
        if (tls)
2✔
223
                /* For TLS mode, use DNS_OVER_TLS_OPPORTUNISTIC instead of DNS_OVER_TLS_YES, just to make
224
                 * certificate validation more lenient, allowing us to use self-signed certificates.  We
225
                 * never downgrade, everything we test always goes over TLS */
226
                manager.dns_over_tls_mode = DNS_OVER_TLS_OPPORTUNISTIC;
1✔
227
#endif
228

229
        assert_se(sd_event_new(&event) >= 0);
2✔
230
        manager.event = event;
2✔
231

232
        /* Set up a mock DNS (over TCP or TLS) server */
233
        server_entrypoint = tcp_dns_server;
2✔
234
#if ENABLE_DNS_OVER_TLS
235
        if (tls)
2✔
236
                server_entrypoint = tls_dns_server;
1✔
237
#endif
238
        assert_se(pthread_mutex_init(&server_lock, NULL) == 0);
2✔
239
        assert_se(pthread_mutex_lock(&server_lock) == 0);
2✔
240
        assert_se(pthread_create(&server_thread, NULL, server_entrypoint, &server_lock) == 0);
2✔
241

242
        /* Create a socket client and connect to the TCP or TLS server
243
         * The server may not be up immediately, so try to connect a few times before failing */
244
        assert_se((clientfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) >= 0);
2✔
245

246
        for (int i = 0; i < 100; i++) {
4✔
247
                r = connect(clientfd, &server_address.sa, sockaddr_len(&server_address));
4✔
248
                if (r >= 0)
4✔
249
                        break;
250
                usleep_safe(EVENT_TIMEOUT_USEC / 100);
2✔
251
        }
252
        assert_se(r >= 0);
2✔
253

254
        /* systemd-resolved uses (and requires) the socket to be in nonblocking mode */
255
        assert_se(fcntl(clientfd, F_SETFL, O_NONBLOCK) >= 0);
2✔
256

257
        /* Initialize DNS stream (disabling the default self-destruction
258
           behaviour when no complete callback is set) */
259
        assert_se(dns_stream_new(&manager, &stream, DNS_STREAM_LOOKUP, DNS_PROTOCOL_DNS,
2✔
260
                                 TAKE_FD(clientfd), NULL, on_stream_packet, on_stream_complete_do_nothing,
261
                                 DNS_STREAM_DEFAULT_TIMEOUT_USEC) >= 0);
262
#if ENABLE_DNS_OVER_TLS
263
        if (tls) {
2✔
264
                DnsServer server = {
2✔
265
                        .manager = &manager,
266
                        .family = server_address.sa.sa_family,
1✔
267
                        .address = *sockaddr_in_addr(&server_address.sa),
1✔
268
                };
269

270
                assert_se(dnstls_manager_init(&manager) >= 0);
1✔
271
                assert_se(dnstls_stream_connect_tls(stream, &server) >= 0);
1✔
272
        }
273
#endif
274

275
        /* Test: Question of type A and associated answer */
276
        log_info("test-resolved-stream: A record");
2✔
277
        send_simple_question(stream, DNS_TYPE_A);
2✔
278
        while (n_received_packets != 1)
9✔
279
                assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1);
9✔
280
        assert_se(DNS_PACKET_DATA(received_packets[0]));
2✔
281
        assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]),
4✔
282
                         ANSWER_A + 2, sizeof(ANSWER_A) - 2) == 0);
283
        dns_packet_unref(TAKE_PTR(received_packets[0]));
2✔
284
        n_received_packets = 0;
2✔
285

286
        /* Test: Question of type AAAA and associated answer */
287
        log_info("test-resolved-stream: AAAA record");
2✔
288
        send_simple_question(stream, DNS_TYPE_AAAA);
2✔
289
        while (n_received_packets != 1)
6✔
290
                assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1);
6✔
291
        assert_se(DNS_PACKET_DATA(received_packets[0]));
2✔
292
        assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]),
4✔
293
                         ANSWER_AAAA + 2, sizeof(ANSWER_AAAA) - 2) == 0);
294
        dns_packet_unref(TAKE_PTR(received_packets[0]));
2✔
295
        n_received_packets = 0;
2✔
296

297
        /* Test: Question of type A and AAAA and associated answers
298
         * Both answers are sent back in a single packet or TLS record
299
         * (tests the fix of PR #22132: "Fix DoT timeout on multiple answer records") */
300
        log_info("test-resolved-stream: A + AAAA record");
2✔
301
        send_simple_question(stream, DNS_TYPE_A);
2✔
302
        send_simple_question(stream, DNS_TYPE_AAAA);
2✔
303

304
        while (n_received_packets != 2)
8✔
305
                assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1);
8✔
306
        assert_se(DNS_PACKET_DATA(received_packets[0]));
2✔
307
        assert_se(DNS_PACKET_DATA(received_packets[1]));
2✔
308
        assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]),
4✔
309
                         ANSWER_A + 2, sizeof(ANSWER_A) - 2) == 0);
310
        assert_se(memcmp(DNS_PACKET_DATA(received_packets[1]),
4✔
311
                         ANSWER_AAAA + 2, sizeof(ANSWER_AAAA) - 2) == 0);
312
        dns_packet_unref(TAKE_PTR(received_packets[0]));
2✔
313
        dns_packet_unref(TAKE_PTR(received_packets[1]));
2✔
314
        n_received_packets = 0;
2✔
315

316
#if ENABLE_DNS_OVER_TLS
317
        if (tls)
2✔
318
                dnstls_manager_free(&manager);
1✔
319
#endif
320

321
        /* Stop the DNS server */
322
        assert_se(pthread_mutex_unlock(&server_lock) == 0);
2✔
323
        assert_se(pthread_join(server_thread, NULL) == 0);
2✔
324
        assert_se(pthread_mutex_destroy(&server_lock) == 0);
2✔
325

326
        log_info("test-resolved-stream: Finished %s test", tls ? "TLS" : "TCP");
3✔
327
}
2✔
328

329
static int try_isolate_network(void) {
1✔
330
        _cleanup_close_ int socket_fd = -EBADF;
1✔
331
        int r;
1✔
332

333
        /* First test if CLONE_NEWUSER/CLONE_NEWNET can actually work for us, i.e. we can open the namespaces
334
         * and then still access the build dir we are run from. We do that in a child process since it's
335
         * nasty if we have to go back from the namespace once we entered it and realized it cannot work. */
336
        r = pidref_safe_fork("(usernstest)", FORK_DEATHSIG_SIGKILL|FORK_LOG|FORK_WAIT, NULL);
1✔
337
        if (r == 0) { /* child */
2✔
338
                _cleanup_free_ char *rt = NULL, *d = NULL;
×
339

340
                if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) {
1✔
341
                        log_warning_errno(errno, "test-resolved-stream: Can't create user and network ns, running on host: %m");
×
342
                        _exit(EXIT_FAILURE);
×
343
                }
344

345
                assert_se(get_process_exe(0, &rt) >= 0);
1✔
346
                assert_se(path_extract_directory(rt, &d) >= 0);
1✔
347

348
                if (access(d, F_OK) < 0) {
1✔
349
                        log_warning_errno(errno, "test-resolved-stream: Can't access /proc/self/exe from user/network ns, running on host: %m");
×
350
                        _exit(EXIT_FAILURE);
×
351
                }
352

353
                _exit(EXIT_SUCCESS);
1✔
354
        }
355
        if (r == -EPROTO) /* EPROTO means nonzero exit code of child, i.e. the tests in the child failed */
1✔
356
                return 0;
357
        assert_se(r > 0);
1✔
358

359
        /* Now that we know that the unshare() is safe, let's actually do it */
360
        assert_se(unshare(CLONE_NEWUSER | CLONE_NEWNET) >= 0);
1✔
361

362
        /* Bring up the loopback interface on the newly created network namespace */
363
        struct ifreq req = { .ifr_ifindex = 1 };
1✔
364
        assert_se((socket_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) >= 0);
1✔
365
        assert_se(ioctl(socket_fd, SIOCGIFNAME, &req) >= 0);
1✔
366
        assert_se(ioctl(socket_fd, SIOCGIFFLAGS, &req) >= 0);
1✔
367
        assert_se(FLAGS_SET(req.ifr_flags, IFF_LOOPBACK));
1✔
368
        req.ifr_flags |= IFF_UP;
1✔
369
        /* Do not assert on this, fails in the Ubuntu 24.04 CI environment */
370
        r = RET_NERRNO(ioctl(socket_fd, SIOCSIFFLAGS, &req));
1✔
371
        if (r < 0)
×
372
                return r;
×
373

374
        return 0;
375
}
376

377
int main(int argc, char **argv) {
1✔
378
        server_address = (union sockaddr_union) {
2✔
379
                .in.sin_family = AF_INET,
380
                .in.sin_port = htobe16(random_u64_range(UINT16_MAX - 1024) + 1024),
1✔
381
                .in.sin_addr.s_addr = htobe32(INADDR_LOOPBACK)
1✔
382
        };
383
        int r;
1✔
384

385
        test_setup_logging(LOG_DEBUG);
1✔
386

387
        r = try_isolate_network();
1✔
388
        if (ERRNO_IS_NEG_PRIVILEGE(r))
1✔
389
                return log_tests_skipped("lacking privileges");
×
390
        assert_se(r >= 0);
1✔
391

392
        test_dns_stream(false);
1✔
393
#if ENABLE_DNS_OVER_TLS
394
        if (system("openssl version >/dev/null 2>&1") != 0)
1✔
395
                return log_tests_skipped("Skipping TLS test since the 'openssl' command does not seem to be available");
×
396
        test_dns_stream(true);
1✔
397
#endif
398

399
        return 0;
400
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc