]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-stream.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <netinet/tcp.h>
23 #include "alloc-util.h"
27 #include "resolved-dns-stream.h"
29 #define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
30 #define DNS_STREAMS_MAX 128
32 static void dns_stream_stop(DnsStream
*s
) {
35 s
->io_event_source
= sd_event_source_unref(s
->io_event_source
);
36 s
->timeout_event_source
= sd_event_source_unref(s
->timeout_event_source
);
37 s
->fd
= safe_close(s
->fd
);
40 static int dns_stream_update_io(DnsStream
*s
) {
45 if (s
->write_packet
&& s
->n_written
< sizeof(s
->write_size
) + s
->write_packet
->size
)
47 if (!s
->read_packet
|| s
->n_read
< sizeof(s
->read_size
) + s
->read_packet
->size
)
50 return sd_event_source_set_io_events(s
->io_event_source
, f
);
53 static int dns_stream_complete(DnsStream
*s
, int error
) {
59 s
->complete(s
, error
);
60 else /* the default action if no completion function is set is to close the stream */
66 static int dns_stream_identify(DnsStream
*s
) {
68 struct cmsghdr header
; /* For alignment */
69 uint8_t buffer
[CMSG_SPACE(MAXSIZE(struct in_pktinfo
, struct in6_pktinfo
))
70 + EXTRA_CMSG_SPACE
/* kernel appears to require extra space */];
72 struct msghdr mh
= {};
82 /* Query the local side */
83 s
->local_salen
= sizeof(s
->local
);
84 r
= getsockname(s
->fd
, &s
->local
.sa
, &s
->local_salen
);
87 if (s
->local
.sa
.sa_family
== AF_INET6
&& s
->ifindex
<= 0)
88 s
->ifindex
= s
->local
.in6
.sin6_scope_id
;
90 /* Query the remote side */
91 s
->peer_salen
= sizeof(s
->peer
);
92 r
= getpeername(s
->fd
, &s
->peer
.sa
, &s
->peer_salen
);
95 if (s
->peer
.sa
.sa_family
== AF_INET6
&& s
->ifindex
<= 0)
96 s
->ifindex
= s
->peer
.in6
.sin6_scope_id
;
98 /* Check consistency */
99 assert(s
->peer
.sa
.sa_family
== s
->local
.sa
.sa_family
);
100 assert(IN_SET(s
->peer
.sa
.sa_family
, AF_INET
, AF_INET6
));
102 /* Query connection meta information */
103 sl
= sizeof(control
);
104 if (s
->peer
.sa
.sa_family
== AF_INET
) {
105 r
= getsockopt(s
->fd
, IPPROTO_IP
, IP_PKTOPTIONS
, &control
, &sl
);
108 } else if (s
->peer
.sa
.sa_family
== AF_INET6
) {
110 r
= getsockopt(s
->fd
, IPPROTO_IPV6
, IPV6_2292PKTOPTIONS
, &control
, &sl
);
114 return -EAFNOSUPPORT
;
116 mh
.msg_control
= &control
;
117 mh
.msg_controllen
= sl
;
119 CMSG_FOREACH(cmsg
, &mh
) {
121 if (cmsg
->cmsg_level
== IPPROTO_IPV6
) {
122 assert(s
->peer
.sa
.sa_family
== AF_INET6
);
124 switch (cmsg
->cmsg_type
) {
127 struct in6_pktinfo
*i
= (struct in6_pktinfo
*) CMSG_DATA(cmsg
);
130 s
->ifindex
= i
->ipi6_ifindex
;
135 s
->ttl
= *(int *) CMSG_DATA(cmsg
);
139 } else if (cmsg
->cmsg_level
== IPPROTO_IP
) {
140 assert(s
->peer
.sa
.sa_family
== AF_INET
);
142 switch (cmsg
->cmsg_type
) {
145 struct in_pktinfo
*i
= (struct in_pktinfo
*) CMSG_DATA(cmsg
);
148 s
->ifindex
= i
->ipi_ifindex
;
153 s
->ttl
= *(int *) CMSG_DATA(cmsg
);
159 /* The Linux kernel sets the interface index to the loopback
160 * device if the connection came from the local host since it
161 * avoids the routing table in such a case. Let's unset the
162 * interface index in such a case. */
163 if (s
->ifindex
== LOOPBACK_IFINDEX
)
166 /* If we don't know the interface index still, we look for the
167 * first local interface with a matching address. Yuck! */
169 s
->ifindex
= manager_find_ifindex(s
->manager
, s
->local
.sa
.sa_family
, s
->local
.sa
.sa_family
== AF_INET
? (union in_addr_union
*) &s
->local
.in
.sin_addr
: (union in_addr_union
*) &s
->local
.in6
.sin6_addr
);
171 if (s
->protocol
== DNS_PROTOCOL_LLMNR
&& s
->ifindex
> 0) {
172 uint32_t ifindex
= htobe32(s
->ifindex
);
174 /* Make sure all packets for this connection are sent on the same interface */
175 if (s
->local
.sa
.sa_family
== AF_INET
) {
176 r
= setsockopt(s
->fd
, IPPROTO_IP
, IP_UNICAST_IF
, &ifindex
, sizeof(ifindex
));
178 log_debug_errno(errno
, "Failed to invoke IP_UNICAST_IF: %m");
179 } else if (s
->local
.sa
.sa_family
== AF_INET6
) {
180 r
= setsockopt(s
->fd
, IPPROTO_IPV6
, IPV6_UNICAST_IF
, &ifindex
, sizeof(ifindex
));
182 log_debug_errno(errno
, "Failed to invoke IPV6_UNICAST_IF: %m");
186 s
->identified
= true;
191 static int on_stream_timeout(sd_event_source
*es
, usec_t usec
, void *userdata
) {
192 DnsStream
*s
= userdata
;
196 return dns_stream_complete(s
, ETIMEDOUT
);
199 static int on_stream_io(sd_event_source
*es
, int fd
, uint32_t revents
, void *userdata
) {
200 DnsStream
*s
= userdata
;
205 r
= dns_stream_identify(s
);
207 return dns_stream_complete(s
, -r
);
209 if ((revents
& EPOLLOUT
) &&
211 s
->n_written
< sizeof(s
->write_size
) + s
->write_packet
->size
) {
216 iov
[0].iov_base
= &s
->write_size
;
217 iov
[0].iov_len
= sizeof(s
->write_size
);
218 iov
[1].iov_base
= DNS_PACKET_DATA(s
->write_packet
);
219 iov
[1].iov_len
= s
->write_packet
->size
;
221 IOVEC_INCREMENT(iov
, 2, s
->n_written
);
223 ss
= writev(fd
, iov
, 2);
225 if (!IN_SET(errno
, EINTR
, EAGAIN
))
226 return dns_stream_complete(s
, errno
);
230 /* Are we done? If so, disable the event source for EPOLLOUT */
231 if (s
->n_written
>= sizeof(s
->write_size
) + s
->write_packet
->size
) {
232 r
= dns_stream_update_io(s
);
234 return dns_stream_complete(s
, -r
);
238 if ((revents
& (EPOLLIN
|EPOLLHUP
|EPOLLRDHUP
)) &&
240 s
->n_read
< sizeof(s
->read_size
) + s
->read_packet
->size
)) {
242 if (s
->n_read
< sizeof(s
->read_size
)) {
245 ss
= read(fd
, (uint8_t*) &s
->read_size
+ s
->n_read
, sizeof(s
->read_size
) - s
->n_read
);
247 if (!IN_SET(errno
, EINTR
, EAGAIN
))
248 return dns_stream_complete(s
, errno
);
250 return dns_stream_complete(s
, ECONNRESET
);
255 if (s
->n_read
>= sizeof(s
->read_size
)) {
257 if (be16toh(s
->read_size
) < DNS_PACKET_HEADER_SIZE
)
258 return dns_stream_complete(s
, EBADMSG
);
260 if (s
->n_read
< sizeof(s
->read_size
) + be16toh(s
->read_size
)) {
263 if (!s
->read_packet
) {
264 r
= dns_packet_new(&s
->read_packet
, s
->protocol
, be16toh(s
->read_size
), DNS_PACKET_SIZE_MAX
);
266 return dns_stream_complete(s
, -r
);
268 s
->read_packet
->size
= be16toh(s
->read_size
);
269 s
->read_packet
->ipproto
= IPPROTO_TCP
;
270 s
->read_packet
->family
= s
->peer
.sa
.sa_family
;
271 s
->read_packet
->ttl
= s
->ttl
;
272 s
->read_packet
->ifindex
= s
->ifindex
;
274 if (s
->read_packet
->family
== AF_INET
) {
275 s
->read_packet
->sender
.in
= s
->peer
.in
.sin_addr
;
276 s
->read_packet
->sender_port
= be16toh(s
->peer
.in
.sin_port
);
277 s
->read_packet
->destination
.in
= s
->local
.in
.sin_addr
;
278 s
->read_packet
->destination_port
= be16toh(s
->local
.in
.sin_port
);
280 assert(s
->read_packet
->family
== AF_INET6
);
281 s
->read_packet
->sender
.in6
= s
->peer
.in6
.sin6_addr
;
282 s
->read_packet
->sender_port
= be16toh(s
->peer
.in6
.sin6_port
);
283 s
->read_packet
->destination
.in6
= s
->local
.in6
.sin6_addr
;
284 s
->read_packet
->destination_port
= be16toh(s
->local
.in6
.sin6_port
);
286 if (s
->read_packet
->ifindex
== 0)
287 s
->read_packet
->ifindex
= s
->peer
.in6
.sin6_scope_id
;
288 if (s
->read_packet
->ifindex
== 0)
289 s
->read_packet
->ifindex
= s
->local
.in6
.sin6_scope_id
;
294 (uint8_t*) DNS_PACKET_DATA(s
->read_packet
) + s
->n_read
- sizeof(s
->read_size
),
295 sizeof(s
->read_size
) + be16toh(s
->read_size
) - s
->n_read
);
297 if (!IN_SET(errno
, EINTR
, EAGAIN
))
298 return dns_stream_complete(s
, errno
);
300 return dns_stream_complete(s
, ECONNRESET
);
305 /* Are we done? If so, disable the event source for EPOLLIN */
306 if (s
->n_read
>= sizeof(s
->read_size
) + be16toh(s
->read_size
)) {
307 r
= dns_stream_update_io(s
);
309 return dns_stream_complete(s
, -r
);
311 /* If there's a packet handler
312 * installed, call that. Note that
313 * this is optional... */
315 return s
->on_packet(s
);
320 if ((s
->write_packet
&& s
->n_written
>= sizeof(s
->write_size
) + s
->write_packet
->size
) &&
321 (s
->read_packet
&& s
->n_read
>= sizeof(s
->read_size
) + s
->read_packet
->size
))
322 return dns_stream_complete(s
, 0);
327 DnsStream
*dns_stream_unref(DnsStream
*s
) {
331 assert(s
->n_ref
> 0);
340 LIST_REMOVE(streams
, s
->manager
->dns_streams
, s
);
341 s
->manager
->n_dns_streams
--;
344 dns_packet_unref(s
->write_packet
);
345 dns_packet_unref(s
->read_packet
);
350 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream
*, dns_stream_unref
);
352 DnsStream
*dns_stream_ref(DnsStream
*s
) {
356 assert(s
->n_ref
> 0);
362 int dns_stream_new(Manager
*m
, DnsStream
**ret
, DnsProtocol protocol
, int fd
) {
363 _cleanup_(dns_stream_unrefp
) DnsStream
*s
= NULL
;
369 if (m
->n_dns_streams
> DNS_STREAMS_MAX
)
372 s
= new0(DnsStream
, 1);
378 s
->protocol
= protocol
;
380 r
= sd_event_add_io(m
->event
, &s
->io_event_source
, fd
, EPOLLIN
, on_stream_io
, s
);
384 (void) sd_event_source_set_description(s
->io_event_source
, "dns-stream-io");
386 r
= sd_event_add_time(
388 &s
->timeout_event_source
,
389 clock_boottime_or_monotonic(),
390 now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC
, 0,
391 on_stream_timeout
, s
);
395 (void) sd_event_source_set_description(s
->timeout_event_source
, "dns-stream-timeout");
397 LIST_PREPEND(streams
, m
->dns_streams
, s
);
408 int dns_stream_write_packet(DnsStream
*s
, DnsPacket
*p
) {
414 s
->write_packet
= dns_packet_ref(p
);
415 s
->write_size
= htobe16(p
->size
);
418 return dns_stream_update_io(s
);