1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include <net/if_arp.h>
11 #include <sys/ioctl.h>
12 #include <sys/prctl.h>
13 #include <sys/socket.h>
20 #include "path-util.h"
21 #include "process-util.h"
22 #include "resolved-dns-packet.h"
23 #include "resolved-dns-question.h"
24 #include "resolved-dns-rr.h"
25 #if ENABLE_DNS_OVER_TLS
26 #include "resolved-dnstls.h"
28 #include "resolved-dns-server.h"
29 #include "resolved-dns-stream.h"
30 #include "resolved-manager.h"
32 #include "sparse-endian.h"
35 static union sockaddr_union server_address
;
37 /* Bytes of the questions & answers used in the test, including TCP DNS 2-byte length prefix */
38 static const uint8_t QUESTION_A
[] = {
39 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
40 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x01, 0x00, 0x01
42 static const uint8_t QUESTION_AAAA
[] = {
43 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
44 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x1C, 0x00, 0x01
46 static const uint8_t ANSWER_A
[] = {
47 0x00, 0x2D, 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
48 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0,
49 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x52, 0x8D, 0x00, 0x04, 0x5D, 0xB8, 0xD8, 0x22,
51 static const uint8_t ANSWER_AAAA
[] = {
52 0x00, 0x39, 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
53 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x1C, 0x00, 0x01, 0xC0,
54 0x0C, 0x00, 0x1C, 0x00, 0x01, 0x00, 0x00, 0x54, 0x4B, 0x00, 0x10, 0x26, 0x06, 0x28, 0x00, 0x02,
55 0x20, 0x00, 0x01, 0x02, 0x48, 0x18, 0x93, 0x25, 0xC8, 0x19, 0x46,
59 * A mock TCP DNS server that asserts certain questions are received
60 * and replies with the same answer every time.
62 static void receive_and_check_question(int fd
, const uint8_t *expected_question
,
63 size_t question_size
) {
64 uint8_t *actual_question
;
67 actual_question
= newa(uint8_t, question_size
);
68 while (n_read
< question_size
) {
69 ssize_t r
= read(fd
, actual_question
+ n_read
, question_size
- n_read
);
73 assert_se(n_read
== question_size
);
75 assert_se(memcmp(expected_question
, actual_question
, question_size
) == 0);
78 static void send_answer(int fd
, const uint8_t *answer
, size_t answer_size
) {
79 assert_se(write(fd
, answer
, answer_size
) == (ssize_t
)answer_size
);
82 /* Sends two answers together in a single write operation,
83 * so they hopefully end up in a single TCP packet / TLS record */
84 static void send_answers_together(int fd
,
85 const uint8_t *answer1
, size_t answer1_size
,
86 const uint8_t *answer2
, size_t answer2_size
) {
88 size_t answer_size
= answer1_size
+ answer2_size
;
90 answer
= newa(uint8_t, answer_size
);
91 memcpy(answer
, answer1
, answer1_size
);
92 memcpy(answer
+ answer1_size
, answer2
, answer2_size
);
93 assert_se(write(fd
, answer
, answer_size
) == (ssize_t
)answer_size
);
96 static void server_handle(int fd
) {
97 receive_and_check_question(fd
, QUESTION_A
, sizeof(QUESTION_A
));
98 send_answer(fd
, ANSWER_A
, sizeof(ANSWER_A
));
100 receive_and_check_question(fd
, QUESTION_AAAA
, sizeof(QUESTION_AAAA
));
101 send_answer(fd
, ANSWER_AAAA
, sizeof(ANSWER_AAAA
));
103 receive_and_check_question(fd
, QUESTION_A
, sizeof(QUESTION_A
));
104 receive_and_check_question(fd
, QUESTION_AAAA
, sizeof(QUESTION_AAAA
));
105 send_answers_together(fd
, ANSWER_A
, sizeof(ANSWER_A
),
106 ANSWER_AAAA
, sizeof(ANSWER_AAAA
));
109 static void *tcp_dns_server(void *p
) {
110 _cleanup_close_
int bindfd
= -EBADF
, acceptfd
= -EBADF
;
112 assert_se((bindfd
= socket(AF_INET
, SOCK_STREAM
| SOCK_CLOEXEC
, 0)) >= 0);
113 assert_se(setsockopt(bindfd
, SOL_SOCKET
, SO_REUSEADDR
, &(int){1}, sizeof(int)) >= 0);
114 assert_se(bind(bindfd
, &server_address
.sa
, SOCKADDR_LEN(server_address
)) >= 0);
115 assert_se(listen(bindfd
, 1) >= 0);
116 assert_se((acceptfd
= accept(bindfd
, NULL
, NULL
)) >= 0);
117 server_handle(acceptfd
);
121 #if ENABLE_DNS_OVER_TLS
123 * Spawns a DNS TLS server using the command line "openssl s_server" tool.
125 static void *tls_dns_server(void *p
) {
128 _cleanup_close_
int fd_server
= -EBADF
, fd_tls
= -EBADF
;
129 _cleanup_free_
char *cert_path
= NULL
, *key_path
= NULL
;
130 _cleanup_free_
char *bind_str
= NULL
;
132 assert_se(get_testdata_dir("test-resolve/selfsigned.cert", &cert_path
) >= 0);
133 assert_se(get_testdata_dir("test-resolve/selfsigned.key", &key_path
) >= 0);
135 assert_se(asprintf(&bind_str
, "%s:%d",
136 IN_ADDR_TO_STRING(server_address
.in
.sin_family
,
137 sockaddr_in_addr(&server_address
.sa
)),
138 be16toh(server_address
.in
.sin_port
)) >= 0);
140 /* We will hook one of the socketpair ends to OpenSSL's TLS server
141 * stdin/stdout, so we will be able to read and write plaintext
142 * from the other end's file descriptor like an usual TCP server */
145 assert_se(socketpair(AF_UNIX
, SOCK_STREAM
, 0, fd
) >= 0);
150 r
= safe_fork_full("(test-resolved-stream-tls-openssl)",
152 (int[]) { fd_server
, fd_tls
}, 2,
153 FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_REOPEN_LOG
, &openssl_pid
);
157 assert_se(dup2(fd_tls
, STDIN_FILENO
) >= 0);
158 assert_se(dup2(fd_tls
, STDOUT_FILENO
) >= 0);
159 close(TAKE_FD(fd_server
));
160 close(TAKE_FD(fd_tls
));
162 execlp("openssl", "openssl", "s_server", "-accept", bind_str
,
163 "-key", key_path
, "-cert", cert_path
,
164 "-quiet", "-naccept", "1", NULL
);
165 log_error("exec failed, is something wrong with the 'openssl' command?");
168 pthread_mutex_t
*server_lock
= (pthread_mutex_t
*)p
;
170 server_handle(fd_server
);
172 /* Once the test is done kill the TLS server to release the port */
173 assert_se(pthread_mutex_lock(server_lock
) == 0);
174 assert_se(kill(openssl_pid
, SIGTERM
) >= 0);
175 assert_se(waitpid(openssl_pid
, NULL
, 0) >= 0);
176 assert_se(pthread_mutex_unlock(server_lock
) == 0);
183 static const char *TEST_DOMAIN
= "example.com";
184 static const uint64_t EVENT_TIMEOUT_USEC
= 5 * 1000 * 1000;
186 static void send_simple_question(DnsStream
*stream
, uint16_t type
) {
187 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
188 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
189 _cleanup_(dns_question_unrefp
) DnsQuestion
*question
= NULL
;
191 assert_se(dns_packet_new(&p
, DNS_PROTOCOL_DNS
, 0, DNS_PACKET_SIZE_MAX
) >= 0);
192 assert_se(question
= dns_question_new(1));
193 assert_se(key
= dns_resource_key_new(DNS_CLASS_IN
, type
, TEST_DOMAIN
));
194 assert_se(dns_question_add(question
, key
, 0) >= 0);
195 assert_se(dns_packet_append_question(p
, question
) >= 0);
196 DNS_PACKET_HEADER(p
)->qdcount
= htobe16(dns_question_size(question
));
197 assert_se(dns_stream_write_packet(stream
, p
) >= 0);
200 static const size_t MAX_RECEIVED_PACKETS
= 2;
201 static DnsPacket
*received_packets
[2] = {};
202 static size_t n_received_packets
= 0;
204 static int on_stream_packet(DnsStream
*stream
, DnsPacket
*p
) {
205 assert_se(n_received_packets
< MAX_RECEIVED_PACKETS
);
206 assert_se(received_packets
[n_received_packets
++] = dns_packet_ref(p
));
210 static int on_stream_complete_do_nothing(DnsStream
*s
, int error
) {
214 static void test_dns_stream(bool tls
) {
215 Manager manager
= {};
216 _cleanup_(dns_stream_unrefp
) DnsStream
*stream
= NULL
;
217 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
218 _cleanup_close_
int clientfd
= -EBADF
;
221 void *(*server_entrypoint
)(void *);
222 pthread_t server_thread
;
223 pthread_mutex_t server_lock
;
225 log_info("test-resolved-stream: Started %s test", tls
? "TLS" : "TCP");
227 #if ENABLE_DNS_OVER_TLS
229 /* For TLS mode, use DNS_OVER_TLS_OPPORTUNISTIC instead of DNS_OVER_TLS_YES, just to make
230 * certificate validation more lenient, allowing us to use self-signed certificates. We
231 * never downgrade, everything we test always goes over TLS */
232 manager
.dns_over_tls_mode
= DNS_OVER_TLS_OPPORTUNISTIC
;
235 assert_se(sd_event_new(&event
) >= 0);
236 manager
.event
= event
;
238 /* Set up a mock DNS (over TCP or TLS) server */
239 server_entrypoint
= tcp_dns_server
;
240 #if ENABLE_DNS_OVER_TLS
242 server_entrypoint
= tls_dns_server
;
244 assert_se(pthread_mutex_init(&server_lock
, NULL
) == 0);
245 assert_se(pthread_mutex_lock(&server_lock
) == 0);
246 assert_se(pthread_create(&server_thread
, NULL
, server_entrypoint
, &server_lock
) == 0);
248 /* Create a socket client and connect to the TCP or TLS server
249 * The server may not be up immediately, so try to connect a few times before failing */
250 assert_se((clientfd
= socket(AF_INET
, SOCK_STREAM
| SOCK_CLOEXEC
, 0)) >= 0);
252 for (int i
= 0; i
< 100; i
++) {
253 r
= connect(clientfd
, &server_address
.sa
, SOCKADDR_LEN(server_address
));
256 usleep(EVENT_TIMEOUT_USEC
/ 100);
260 /* systemd-resolved uses (and requires) the socket to be in nonblocking mode */
261 assert_se(fcntl(clientfd
, F_SETFL
, O_NONBLOCK
) >= 0);
263 /* Initialize DNS stream (disabling the default self-destruction
264 behaviour when no complete callback is set) */
265 assert_se(dns_stream_new(&manager
, &stream
, DNS_STREAM_LOOKUP
, DNS_PROTOCOL_DNS
,
266 TAKE_FD(clientfd
), NULL
, on_stream_packet
, on_stream_complete_do_nothing
,
267 DNS_STREAM_DEFAULT_TIMEOUT_USEC
) >= 0);
268 #if ENABLE_DNS_OVER_TLS
272 .family
= server_address
.sa
.sa_family
,
273 .address
= *sockaddr_in_addr(&server_address
.sa
),
276 assert_se(dnstls_manager_init(&manager
) >= 0);
277 assert_se(dnstls_stream_connect_tls(stream
, &server
) >= 0);
281 /* Test: Question of type A and associated answer */
282 log_info("test-resolved-stream: A record");
283 send_simple_question(stream
, DNS_TYPE_A
);
284 while (n_received_packets
!= 1)
285 assert_se(sd_event_run(event
, EVENT_TIMEOUT_USEC
) >= 1);
286 assert_se(DNS_PACKET_DATA(received_packets
[0]));
287 assert_se(memcmp(DNS_PACKET_DATA(received_packets
[0]),
288 ANSWER_A
+ 2, sizeof(ANSWER_A
) - 2) == 0);
289 dns_packet_unref(TAKE_PTR(received_packets
[0]));
290 n_received_packets
= 0;
292 /* Test: Question of type AAAA and associated answer */
293 log_info("test-resolved-stream: AAAA record");
294 send_simple_question(stream
, DNS_TYPE_AAAA
);
295 while (n_received_packets
!= 1)
296 assert_se(sd_event_run(event
, EVENT_TIMEOUT_USEC
) >= 1);
297 assert_se(DNS_PACKET_DATA(received_packets
[0]));
298 assert_se(memcmp(DNS_PACKET_DATA(received_packets
[0]),
299 ANSWER_AAAA
+ 2, sizeof(ANSWER_AAAA
) - 2) == 0);
300 dns_packet_unref(TAKE_PTR(received_packets
[0]));
301 n_received_packets
= 0;
303 /* Test: Question of type A and AAAA and associated answers
304 * Both answers are sent back in a single packet or TLS record
305 * (tests the fix of PR #22132: "Fix DoT timeout on multiple answer records") */
306 log_info("test-resolved-stream: A + AAAA record");
307 send_simple_question(stream
, DNS_TYPE_A
);
308 send_simple_question(stream
, DNS_TYPE_AAAA
);
310 while (n_received_packets
!= 2)
311 assert_se(sd_event_run(event
, EVENT_TIMEOUT_USEC
) >= 1);
312 assert_se(DNS_PACKET_DATA(received_packets
[0]));
313 assert_se(DNS_PACKET_DATA(received_packets
[1]));
314 assert_se(memcmp(DNS_PACKET_DATA(received_packets
[0]),
315 ANSWER_A
+ 2, sizeof(ANSWER_A
) - 2) == 0);
316 assert_se(memcmp(DNS_PACKET_DATA(received_packets
[1]),
317 ANSWER_AAAA
+ 2, sizeof(ANSWER_AAAA
) - 2) == 0);
318 dns_packet_unref(TAKE_PTR(received_packets
[0]));
319 dns_packet_unref(TAKE_PTR(received_packets
[1]));
320 n_received_packets
= 0;
322 #if ENABLE_DNS_OVER_TLS
324 dnstls_manager_free(&manager
);
327 /* Stop the DNS server */
328 assert_se(pthread_mutex_unlock(&server_lock
) == 0);
329 assert_se(pthread_join(server_thread
, NULL
) == 0);
330 assert_se(pthread_mutex_destroy(&server_lock
) == 0);
332 log_info("test-resolved-stream: Finished %s test", tls
? "TLS" : "TCP");
335 static void try_isolate_network(void) {
336 _cleanup_close_
int socket_fd
= -EBADF
;
339 /* First test if CLONE_NEWUSER/CLONE_NEWNET can actually work for us, i.e. we can open the namespaces
340 * and then still access the build dir we are run from. We do that in a child process since it's
341 * nasty if we have to go back from the namespace once we entered it and realized it cannot work. */
342 r
= safe_fork("(usernstest)", FORK_DEATHSIG
|FORK_LOG
|FORK_WAIT
, NULL
);
343 if (r
== 0) { /* child */
344 _cleanup_free_
char *rt
= NULL
, *d
= NULL
;
346 if (unshare(CLONE_NEWUSER
| CLONE_NEWNET
) < 0) {
347 log_warning_errno(errno
, "test-resolved-stream: Can't create user and network ns, running on host: %m");
351 assert_se(get_process_exe(0, &rt
) >= 0);
352 assert_se(path_extract_directory(rt
, &d
) >= 0);
354 if (access(d
, F_OK
) < 0) {
355 log_warning_errno(errno
, "test-resolved-stream: Can't access /proc/self/exe from user/network ns, running on host: %m");
361 if (r
== -EPROTO
) /* EPROTO means nonzero exit code of child, i.e. the tests in the child failed */
365 /* Now that we know that the unshare() is safe, let's actually do it */
366 assert_se(unshare(CLONE_NEWUSER
| CLONE_NEWNET
) >= 0);
368 /* Bring up the loopback interfaceon the newly created network namespace */
369 struct ifreq req
= { .ifr_ifindex
= 1 };
370 assert_se((socket_fd
= socket(AF_INET
, SOCK_STREAM
| SOCK_CLOEXEC
, 0)) >= 0);
371 assert_se(ioctl(socket_fd
, SIOCGIFNAME
, &req
) >= 0);
372 assert_se(ioctl(socket_fd
, SIOCGIFFLAGS
, &req
) >= 0);
373 assert_se(FLAGS_SET(req
.ifr_flags
, IFF_LOOPBACK
));
374 req
.ifr_flags
|= IFF_UP
;
375 assert_se(ioctl(socket_fd
, SIOCSIFFLAGS
, &req
) >= 0);
378 int main(int argc
, char **argv
) {
379 server_address
= (union sockaddr_union
) {
380 .in
.sin_family
= AF_INET
,
381 .in
.sin_port
= htobe16(12345),
382 .in
.sin_addr
.s_addr
= htobe32(INADDR_LOOPBACK
)
385 test_setup_logging(LOG_DEBUG
);
387 try_isolate_network();
389 test_dns_stream(false);
390 #if ENABLE_DNS_OVER_TLS
391 if (system("openssl version >/dev/null 2>&1") != 0)
392 return log_tests_skipped("Skipping TLS test since the 'openssl' command does not seem to be available");
393 test_dns_stream(true);