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

systemd / systemd / 14630481637

23 Apr 2025 07:04PM UTC coverage: 72.178% (-0.002%) from 72.18%
14630481637

push

github

DaanDeMeyer
mkosi: Run clangd within the tools tree instead of the build container

Running within the build sandbox has a number of disadvantages:
- We have a separate clangd cache for each distribution/release combo
- It requires to build the full image before clangd can be used
- It breaks every time the image becomes out of date and requires a
  rebuild
- We can't look at system headers as we don't have the knowledge to map
  them from inside the build sandbox to the corresponding path on the host

Instead, let's have mkosi.clangd run clangd within the tools tree. We
already require building systemd for both the host and the target anyway,
and all the dependencies to build systemd are installed in the tools tree
already for that, as well as clangd since it's installed together with the
other clang tooling we install in the tools tree. Unlike the previous approach,
this approach only requires the mkosi tools tree to be built upfront, which has
a much higher chance of not invalidating its cache. We can also trivially map
system header lookups from within the sandbox to the path within mkosi.tools
on the host so that starts working as well.

297054 of 411557 relevant lines covered (72.18%)

686269.58 hits per line

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

59.76
/src/resolve/resolved-dnstls.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#if !ENABLE_DNS_OVER_TLS || !HAVE_OPENSSL
4
#error This source file requires DNS-over-TLS to be enabled and OpenSSL to be available.
5
#endif
6

7
#include <openssl/bio.h>
8
#include <openssl/err.h>
9
#include <openssl/x509v3.h>
10

11
#include "io-util.h"
12
#include "openssl-util.h"
13
#include "resolved-dns-server.h"
14
#include "resolved-dns-stream.h"
15
#include "resolved-dnstls.h"
16
#include "resolved-manager.h"
17

18
static char *dnstls_error_string(int ssl_error, char *buf, size_t count) {
×
19
        assert(buf || count == 0);
×
20
        if (ssl_error == SSL_ERROR_SSL)
×
21
                ERR_error_string_n(ERR_get_error(), buf, count);
×
22
        else
23
                snprintf(buf, count, "SSL_get_error()=%d", ssl_error);
×
24
        return buf;
×
25
}
26

27
#define DNSTLS_ERROR_BUFSIZE 256
28
#define DNSTLS_ERROR_STRING(error) \
29
        dnstls_error_string((error), (char[DNSTLS_ERROR_BUFSIZE]){}, DNSTLS_ERROR_BUFSIZE)
30

31
static int dnstls_flush_write_buffer(DnsStream *stream) {
24✔
32
        ssize_t ss;
24✔
33

34
        assert(stream);
24✔
35
        assert(stream->encrypted);
24✔
36

37
        if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) {
24✔
38
                assert(stream->dnstls_data.write_buffer->data);
10✔
39

40
                struct iovec iov[1];
10✔
41
                iov[0] = IOVEC_MAKE(stream->dnstls_data.write_buffer->data + stream->dnstls_data.buffer_offset,
10✔
42
                                    stream->dnstls_data.write_buffer->length - stream->dnstls_data.buffer_offset);
43
                ss = dns_stream_writev(stream, iov, 1, DNS_STREAM_WRITE_TLS_DATA);
10✔
44
                if (ss < 0) {
10✔
45
                        if (ss == -EAGAIN)
4✔
46
                                stream->dnstls_events |= EPOLLOUT;
2✔
47

48
                        return ss;
4✔
49
                } else {
50
                        stream->dnstls_data.buffer_offset += ss;
6✔
51

52
                        if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) {
6✔
53
                                stream->dnstls_events |= EPOLLOUT;
×
54
                                return -EAGAIN;
×
55
                        } else {
56
                                BIO_reset(SSL_get_wbio(stream->dnstls_data.ssl));
6✔
57
                                stream->dnstls_data.buffer_offset = 0;
6✔
58
                        }
59
                }
60
        }
61

62
        return 0;
63
}
64

65
int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
3✔
66
        _cleanup_(BIO_freep) BIO *rb = NULL, *wb = NULL;
3✔
67
        _cleanup_(SSL_freep) SSL *s = NULL;
6✔
68
        int error, r;
3✔
69

70
        assert(stream);
3✔
71
        assert(stream->manager);
3✔
72
        assert(server);
3✔
73

74
        rb = BIO_new_socket(stream->fd, 0);
3✔
75
        if (!rb)
3✔
76
                return -ENOMEM;
77

78
        wb = BIO_new(BIO_s_mem());
3✔
79
        if (!wb)
3✔
80
                return -ENOMEM;
81

82
        BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer);
3✔
83
        stream->dnstls_data.buffer_offset = 0;
3✔
84

85
        s = SSL_new(stream->manager->dnstls_data.ctx);
3✔
86
        if (!s)
3✔
87
                return -ENOMEM;
88

89
        SSL_set_connect_state(s);
3✔
90
        r = SSL_set_session(s, server->dnstls_data.session);
3✔
91
        if (r == 0)
3✔
92
                return -EIO;
93
        SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
3✔
94

95
        if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
3✔
96
                X509_VERIFY_PARAM *v;
×
97

98
                SSL_set_verify(s, SSL_VERIFY_PEER, NULL);
×
99
                v = SSL_get0_param(s);
×
100
                if (server->server_name) {
×
101
                        X509_VERIFY_PARAM_set_hostflags(v, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
×
102
                        if (X509_VERIFY_PARAM_set1_host(v, server->server_name, 0) == 0)
×
103
                                return -ECONNREFUSED;
104
                } else {
105
                        const unsigned char *ip;
×
106
                        ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
×
107
                        if (X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)) == 0)
×
108
                                return -ECONNREFUSED;
109
                }
110
        }
111

112
        if (server->server_name) {
3✔
113
                r = SSL_set_tlsext_host_name(s, server->server_name);
×
114
                if (r <= 0)
×
115
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
×
116
                                               "Failed to set server name: %s", DNSTLS_ERROR_STRING(SSL_ERROR_SSL));
117
        }
118

119
        ERR_clear_error();
3✔
120
        stream->dnstls_data.handshake = SSL_do_handshake(s);
3✔
121
        if (stream->dnstls_data.handshake <= 0) {
3✔
122
                error = SSL_get_error(s, stream->dnstls_data.handshake);
3✔
123
                if (!IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE))
3✔
124
                        return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
×
125
                                               "Failed to invoke SSL_do_handshake: %s", DNSTLS_ERROR_STRING(error));
126
        }
127

128
        stream->encrypted = true;
3✔
129
        stream->dnstls_data.ssl = TAKE_PTR(s);
3✔
130

131
        r = dnstls_flush_write_buffer(stream);
3✔
132
        if (r < 0 && r != -EAGAIN) {
3✔
133
                SSL_free(TAKE_PTR(stream->dnstls_data.ssl));
×
134
                return r;
135
        }
136

137
        return 0;
138
}
139

140
void dnstls_stream_free(DnsStream *stream) {
3✔
141
        assert(stream);
3✔
142
        assert(stream->encrypted);
3✔
143

144
        if (stream->dnstls_data.ssl)
3✔
145
                SSL_free(stream->dnstls_data.ssl);
3✔
146
}
3✔
147

148
int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
12✔
149
        int error, r;
12✔
150

151
        assert(stream);
12✔
152
        assert(stream->encrypted);
12✔
153
        assert(stream->dnstls_data.ssl);
12✔
154

155
        /* Flush write buffer when requested by OpenSSL */
156
        if ((revents & EPOLLOUT) && (stream->dnstls_events & EPOLLOUT)) {
12✔
157
                r = dnstls_flush_write_buffer(stream);
2✔
158
                if (r < 0)
2✔
159
                        return r;
160
        }
161

162
        if (stream->dnstls_data.shutdown) {
10✔
163
                ERR_clear_error();
×
164
                r = SSL_shutdown(stream->dnstls_data.ssl);
×
165
                if (r == 0) {
×
166
                        stream->dnstls_events = 0;
×
167

168
                        r = dnstls_flush_write_buffer(stream);
×
169
                        if (r < 0)
×
170
                                return r;
171

172
                        return -EAGAIN;
×
173
                } else if (r < 0) {
×
174
                        error = SSL_get_error(stream->dnstls_data.ssl, r);
×
175
                        if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
×
176
                                stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
×
177

178
                                r = dnstls_flush_write_buffer(stream);
×
179
                                if (r < 0)
×
180
                                        return r;
181

182
                                return -EAGAIN;
×
183
                        } else if (error == SSL_ERROR_SYSCALL) {
×
184
                                if (errno > 0)
×
185
                                        log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
×
186
                        } else
187
                                log_debug("Failed to invoke SSL_shutdown, ignoring: %s", DNSTLS_ERROR_STRING(error));
×
188
                }
189

190
                stream->dnstls_events = 0;
×
191
                stream->dnstls_data.shutdown = false;
×
192

193
                r = dnstls_flush_write_buffer(stream);
×
194
                if (r < 0)
×
195
                        return r;
196

197
                dns_stream_unref(stream);
×
198
                return DNSTLS_STREAM_CLOSED;
×
199
        } else if (stream->dnstls_data.handshake <= 0) {
10✔
200
                ERR_clear_error();
2✔
201
                stream->dnstls_data.handshake = SSL_do_handshake(stream->dnstls_data.ssl);
2✔
202
                if (stream->dnstls_data.handshake <= 0) {
2✔
203
                        error = SSL_get_error(stream->dnstls_data.ssl, stream->dnstls_data.handshake);
1✔
204
                        if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
1✔
205
                                stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
1✔
206
                                r = dnstls_flush_write_buffer(stream);
1✔
207
                                if (r < 0)
1✔
208
                                        return r;
209

210
                                return -EAGAIN;
1✔
211
                        } else
212
                                return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
×
213
                                                       "Failed to invoke SSL_do_handshake: %s",
214
                                                       DNSTLS_ERROR_STRING(error));
215
                }
216

217
                stream->dnstls_events = 0;
1✔
218
                r = dnstls_flush_write_buffer(stream);
1✔
219
                if (r < 0)
1✔
220
                        return r;
×
221
        }
222

223
        return 0;
224
}
225

226
int dnstls_stream_shutdown(DnsStream *stream, int error) {
2✔
227
        int ssl_error, r;
2✔
228
        SSL_SESSION *s;
2✔
229

230
        assert(stream);
2✔
231
        assert(stream->encrypted);
2✔
232
        assert(stream->dnstls_data.ssl);
2✔
233

234
        if (stream->server) {
2✔
235
                s = SSL_get1_session(stream->dnstls_data.ssl);
2✔
236
                if (s) {
2✔
237
                        if (stream->server->dnstls_data.session)
2✔
238
                                SSL_SESSION_free(stream->server->dnstls_data.session);
×
239

240
                        stream->server->dnstls_data.session = s;
2✔
241
                }
242
        }
243

244
        if (error == ETIMEDOUT) {
2✔
245
                ERR_clear_error();
×
246
                r = SSL_shutdown(stream->dnstls_data.ssl);
×
247
                if (r == 0) {
×
248
                        if (!stream->dnstls_data.shutdown) {
×
249
                                stream->dnstls_data.shutdown = true;
×
250
                                dns_stream_ref(stream);
×
251
                        }
252

253
                        stream->dnstls_events = 0;
×
254

255
                        r = dnstls_flush_write_buffer(stream);
×
256
                        if (r < 0)
×
257
                                return r;
258

259
                        return -EAGAIN;
×
260
                } else if (r < 0) {
×
261
                        ssl_error = SSL_get_error(stream->dnstls_data.ssl, r);
×
262
                        if (IN_SET(ssl_error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
×
263
                                stream->dnstls_events = ssl_error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
×
264
                                r = dnstls_flush_write_buffer(stream);
×
265
                                if (r < 0 && r != -EAGAIN)
×
266
                                        return r;
267

268
                                if (!stream->dnstls_data.shutdown) {
×
269
                                        stream->dnstls_data.shutdown = true;
×
270
                                        dns_stream_ref(stream);
×
271
                                }
272
                                return -EAGAIN;
×
273
                        } else if (ssl_error == SSL_ERROR_SYSCALL) {
×
274
                                if (errno > 0)
×
275
                                        log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
×
276
                        } else
277
                                log_debug("Failed to invoke SSL_shutdown, ignoring: %s", DNSTLS_ERROR_STRING(ssl_error));
×
278
                }
279

280
                stream->dnstls_events = 0;
×
281
                r = dnstls_flush_write_buffer(stream);
×
282
                if (r < 0)
×
283
                        return r;
×
284
        }
285

286
        return 0;
287
}
288

289
static ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
4✔
290
        int error, r;
4✔
291
        ssize_t ss;
4✔
292

293
        ERR_clear_error();
4✔
294
        ss = r = SSL_write(stream->dnstls_data.ssl, buf, count);
4✔
295
        if (r <= 0) {
4✔
296
                error = SSL_get_error(stream->dnstls_data.ssl, r);
×
297
                if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
×
298
                        stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
×
299
                        ss = -EAGAIN;
×
300
                } else if (error == SSL_ERROR_ZERO_RETURN) {
×
301
                        stream->dnstls_events = 0;
×
302
                        ss = 0;
×
303
                } else {
304
                        log_debug("Failed to invoke SSL_write: %s", DNSTLS_ERROR_STRING(error));
×
305
                        stream->dnstls_events = 0;
×
306
                        ss = -EPIPE;
×
307
                }
308
        } else
309
                stream->dnstls_events = 0;
4✔
310

311
        r = dnstls_flush_write_buffer(stream);
4✔
312
        if (r < 0)
4✔
313
                return r;
×
314

315
        return ss;
316
}
317

318
ssize_t dnstls_stream_writev(DnsStream *stream, const struct iovec *iov, size_t iovcnt) {
4✔
319
        _cleanup_free_ char *buf = NULL;
4✔
320
        size_t count;
4✔
321

322
        assert(stream);
4✔
323
        assert(stream->encrypted);
4✔
324
        assert(stream->dnstls_data.ssl);
4✔
325
        assert(iov);
4✔
326
        assert(iovec_total_size(iov, iovcnt) > 0);
4✔
327

328
        if (iovcnt == 1)
4✔
329
                return dnstls_stream_write(stream, iov[0].iov_base, iov[0].iov_len);
×
330

331
        /* As of now, OpenSSL cannot accumulate multiple writes, so join into a
332
           single buffer. Suboptimal, but better than multiple SSL_write calls. */
333
        count = iovec_total_size(iov, iovcnt);
4✔
334
        buf = new(char, count);
4✔
335
        for (size_t i = 0, pos = 0; i < iovcnt; pos += iov[i].iov_len, i++)
12✔
336
                memcpy(buf + pos, iov[i].iov_base, iov[i].iov_len);
8✔
337

338
        return dnstls_stream_write(stream, buf, count);
4✔
339
}
340

341
ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
13✔
342
        int error, r;
13✔
343
        ssize_t ss;
13✔
344

345
        assert(stream);
13✔
346
        assert(stream->encrypted);
13✔
347
        assert(stream->dnstls_data.ssl);
13✔
348
        assert(buf);
13✔
349

350
        ERR_clear_error();
13✔
351
        ss = r = SSL_read(stream->dnstls_data.ssl, buf, count);
13✔
352
        if (r <= 0) {
13✔
353
                error = SSL_get_error(stream->dnstls_data.ssl, r);
5✔
354
                if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
5✔
355
                        /* If we receive SSL_ERROR_WANT_READ here, there are two possible scenarios:
356
                           * OpenSSL needs to renegotiate (so we want to get an EPOLLIN event), or
357
                           * There is no more application data is available, so we can just return
358
                           And apparently there's no nice way to distinguish between the two.
359
                           To handle this, never set EPOLLIN and just continue as usual.
360
                           If OpenSSL really wants to read due to renegotiation, it will tell us
361
                           again on SSL_write (at which point we will request EPOLLIN force a read);
362
                           or we will just eventually read data anyway while we wait for a packet */
363
                        stream->dnstls_events = error == SSL_ERROR_WANT_READ ? 0 : EPOLLOUT;
5✔
364
                        ss = -EAGAIN;
5✔
365
                } else if (error == SSL_ERROR_ZERO_RETURN) {
×
366
                        stream->dnstls_events = 0;
×
367
                        ss = 0;
×
368
                } else {
369
                        log_debug("Failed to invoke SSL_read: %s", DNSTLS_ERROR_STRING(error));
×
370
                        stream->dnstls_events = 0;
×
371
                        ss = -EPIPE;
×
372
                }
373
        } else
374
                stream->dnstls_events = 0;
8✔
375

376
        /* flush write buffer in cache of renegotiation */
377
        r = dnstls_flush_write_buffer(stream);
13✔
378
        if (r < 0)
13✔
379
                return r;
×
380

381
        return ss;
382
}
383

384
void dnstls_server_free(DnsServer *server) {
1,211✔
385
        assert(server);
1,211✔
386

387
        if (server->dnstls_data.session)
1,211✔
388
                SSL_SESSION_free(server->dnstls_data.session);
2✔
389
}
1,211✔
390

391
int dnstls_manager_init(Manager *manager) {
163✔
392
        int r;
163✔
393

394
        assert(manager);
163✔
395

396
        manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
163✔
397
        if (!manager->dnstls_data.ctx)
163✔
398
                return log_warning_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
×
399
                                         "Failed to create SSL context: %s",
400
                                         ERR_error_string(ERR_get_error(), NULL));
401

402
        r = SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION);
163✔
403
        if (r == 0)
163✔
404
                return log_warning_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
×
405
                                         "Failed to set protocol version on SSL context: %s",
406
                                         ERR_error_string(ERR_get_error(), NULL));
407

408
        (void) SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
163✔
409

410
        r = SSL_CTX_set_default_verify_paths(manager->dnstls_data.ctx);
163✔
411
        if (r == 0)
163✔
412
                return log_warning_errno(SYNTHETIC_ERRNO(EIO),
×
413
                                         "Failed to load system trust store: %s",
414
                                         ERR_error_string(ERR_get_error(), NULL));
415
        return 0;
416
}
417

418
void dnstls_manager_free(Manager *manager) {
163✔
419
        assert(manager);
163✔
420

421
        if (manager->dnstls_data.ctx)
163✔
422
                SSL_CTX_free(manager->dnstls_data.ctx);
163✔
423
}
163✔
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