1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #if !ENABLE_DNS_OVER_TLS || !HAVE_GNUTLS
4 #error This source file requires DNS-over-TLS to be enabled and GnuTLS to be available.
7 #include "resolved-dnstls.h"
8 #include "resolved-dns-stream.h"
10 #include <gnutls/socket.h>
12 DEFINE_TRIVIAL_CLEANUP_FUNC(gnutls_session_t
, gnutls_deinit
);
14 static ssize_t
dnstls_stream_writev(gnutls_transport_ptr_t p
, const giovec_t
*iov
, int iovcnt
) {
19 r
= dns_stream_writev((DnsStream
*) p
, (const struct iovec
*) iov
, iovcnt
, DNS_STREAM_WRITE_TLS_DATA
);
28 int dnstls_stream_connect_tls(DnsStream
*stream
, DnsServer
*server
) {
29 _cleanup_(gnutls_deinitp
) gnutls_session_t gs
;
35 r
= gnutls_init(&gs
, GNUTLS_CLIENT
| GNUTLS_ENABLE_FALSE_START
| GNUTLS_NONBLOCK
);
39 /* As DNS-over-TLS is a recent protocol, older TLS versions can be disabled */
40 r
= gnutls_priority_set_direct(gs
, "NORMAL:-VERS-ALL:+VERS-TLS1.2", NULL
);
44 r
= gnutls_credentials_set(gs
, GNUTLS_CRD_CERTIFICATE
, server
->dnstls_data
.cert_cred
);
48 if (server
->dnstls_data
.session_data
.size
> 0) {
49 gnutls_session_set_data(gs
, server
->dnstls_data
.session_data
.data
, server
->dnstls_data
.session_data
.size
);
51 // Clear old session ticket
52 gnutls_free(server
->dnstls_data
.session_data
.data
);
53 server
->dnstls_data
.session_data
.data
= NULL
;
54 server
->dnstls_data
.session_data
.size
= 0;
57 gnutls_handshake_set_timeout(gs
, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT
);
59 gnutls_transport_set_ptr2(gs
, (gnutls_transport_ptr_t
) (long) stream
->fd
, stream
);
60 gnutls_transport_set_vec_push_function(gs
, &dnstls_stream_writev
);
62 stream
->encrypted
= true;
63 stream
->dnstls_data
.handshake
= gnutls_handshake(gs
);
64 if (stream
->dnstls_data
.handshake
< 0 && gnutls_error_is_fatal(stream
->dnstls_data
.handshake
))
67 stream
->dnstls_data
.session
= TAKE_PTR(gs
);
72 void dnstls_stream_free(DnsStream
*stream
) {
74 assert(stream
->encrypted
);
76 if (stream
->dnstls_data
.session
)
77 gnutls_deinit(stream
->dnstls_data
.session
);
80 int dnstls_stream_on_io(DnsStream
*stream
) {
84 assert(stream
->encrypted
);
85 assert(stream
->dnstls_data
.session
);
87 if (stream
->dnstls_data
.shutdown
) {
88 r
= gnutls_bye(stream
->dnstls_data
.session
, GNUTLS_SHUT_RDWR
);
89 if (r
== GNUTLS_E_AGAIN
)
92 log_debug("Failed to invoke gnutls_bye: %s", gnutls_strerror(r
));
94 stream
->dnstls_data
.shutdown
= false;
95 dns_stream_unref(stream
);
96 return DNSTLS_STREAM_CLOSED
;
97 } else if (stream
->dnstls_data
.handshake
< 0) {
98 stream
->dnstls_data
.handshake
= gnutls_handshake(stream
->dnstls_data
.session
);
99 if (stream
->dnstls_data
.handshake
== GNUTLS_E_AGAIN
)
101 else if (stream
->dnstls_data
.handshake
< 0) {
102 log_debug("Failed to invoke gnutls_handshake: %s", gnutls_strerror(stream
->dnstls_data
.handshake
));
103 if (gnutls_error_is_fatal(stream
->dnstls_data
.handshake
))
104 return -ECONNREFUSED
;
111 int dnstls_stream_shutdown(DnsStream
*stream
, int error
) {
115 assert(stream
->encrypted
);
116 assert(stream
->dnstls_data
.session
);
118 /* Store TLS Ticket for faster succesive TLS handshakes */
119 if (stream
->server
&& stream
->server
->dnstls_data
.session_data
.size
== 0 && stream
->dnstls_data
.handshake
== GNUTLS_E_SUCCESS
)
120 gnutls_session_get_data2(stream
->dnstls_data
.session
, &stream
->server
->dnstls_data
.session_data
);
122 if (IN_SET(error
, ETIMEDOUT
, 0)) {
123 r
= gnutls_bye(stream
->dnstls_data
.session
, GNUTLS_SHUT_RDWR
);
124 if (r
== GNUTLS_E_AGAIN
) {
125 if (!stream
->dnstls_data
.shutdown
) {
126 stream
->dnstls_data
.shutdown
= true;
127 dns_stream_ref(stream
);
131 log_debug("Failed to invoke gnutls_bye: %s", gnutls_strerror(r
));
137 ssize_t
dnstls_stream_write(DnsStream
*stream
, const char *buf
, size_t count
) {
141 assert(stream
->encrypted
);
142 assert(stream
->dnstls_data
.session
);
145 ss
= gnutls_record_send(stream
->dnstls_data
.session
, buf
, count
);
148 case GNUTLS_E_INTERRUPTED
:
153 log_debug("Failed to invoke gnutls_record_send: %s", gnutls_strerror(ss
));
160 ssize_t
dnstls_stream_read(DnsStream
*stream
, void *buf
, size_t count
) {
164 assert(stream
->encrypted
);
165 assert(stream
->dnstls_data
.session
);
168 ss
= gnutls_record_recv(stream
->dnstls_data
.session
, buf
, count
);
171 case GNUTLS_E_INTERRUPTED
:
176 log_debug("Failed to invoke gnutls_record_recv: %s", gnutls_strerror(ss
));
183 void dnstls_server_init(DnsServer
*server
) {
186 /* Do not verify cerificate */
187 gnutls_certificate_allocate_credentials(&server
->dnstls_data
.cert_cred
);
190 void dnstls_server_free(DnsServer
*server
) {
193 if (server
->dnstls_data
.cert_cred
)
194 gnutls_certificate_free_credentials(server
->dnstls_data
.cert_cred
);
196 if (server
->dnstls_data
.session_data
.data
)
197 gnutls_free(server
->dnstls_data
.session_data
.data
);