]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-stream.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <netinet/tcp.h>
25 #include "resolved-dns-stream.h"
27 #define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
28 #define DNS_STREAMS_MAX 128
30 static void dns_stream_stop(DnsStream
*s
) {
33 s
->io_event_source
= sd_event_source_unref(s
->io_event_source
);
34 s
->timeout_event_source
= sd_event_source_unref(s
->timeout_event_source
);
35 s
->fd
= safe_close(s
->fd
);
38 static int dns_stream_update_io(DnsStream
*s
) {
43 if (s
->write_packet
&& s
->n_written
< sizeof(s
->write_size
) + s
->write_packet
->size
)
45 if (!s
->read_packet
|| s
->n_read
< sizeof(s
->read_size
) + s
->read_packet
->size
)
48 return sd_event_source_set_io_events(s
->io_event_source
, f
);
51 static int dns_stream_complete(DnsStream
*s
, int error
) {
57 s
->complete(s
, error
);
64 static int dns_stream_identify(DnsStream
*s
) {
66 struct cmsghdr header
; /* For alignment */
67 uint8_t buffer
[CMSG_SPACE(MAXSIZE(struct in_pktinfo
, struct in6_pktinfo
))
68 + EXTRA_CMSG_SPACE
/* kernel appears to require extra space */];
70 struct msghdr mh
= {};
80 /* Query the local side */
81 s
->local_salen
= sizeof(s
->local
);
82 r
= getsockname(s
->fd
, &s
->local
.sa
, &s
->local_salen
);
85 if (s
->local
.sa
.sa_family
== AF_INET6
&& s
->ifindex
<= 0)
86 s
->ifindex
= s
->local
.in6
.sin6_scope_id
;
88 /* Query the remote side */
89 s
->peer_salen
= sizeof(s
->peer
);
90 r
= getpeername(s
->fd
, &s
->peer
.sa
, &s
->peer_salen
);
93 if (s
->peer
.sa
.sa_family
== AF_INET6
&& s
->ifindex
<= 0)
94 s
->ifindex
= s
->peer
.in6
.sin6_scope_id
;
96 /* Check consistency */
97 assert(s
->peer
.sa
.sa_family
== s
->local
.sa
.sa_family
);
98 assert(IN_SET(s
->peer
.sa
.sa_family
, AF_INET
, AF_INET6
));
100 /* Query connection meta information */
101 sl
= sizeof(control
);
102 if (s
->peer
.sa
.sa_family
== AF_INET
) {
103 r
= getsockopt(s
->fd
, IPPROTO_IP
, IP_PKTOPTIONS
, &control
, &sl
);
106 } else if (s
->peer
.sa
.sa_family
== AF_INET6
) {
108 r
= getsockopt(s
->fd
, IPPROTO_IPV6
, IPV6_2292PKTOPTIONS
, &control
, &sl
);
112 return -EAFNOSUPPORT
;
114 mh
.msg_control
= &control
;
115 mh
.msg_controllen
= sl
;
117 CMSG_FOREACH(cmsg
, &mh
) {
119 if (cmsg
->cmsg_level
== IPPROTO_IPV6
) {
120 assert(s
->peer
.sa
.sa_family
== AF_INET6
);
122 switch (cmsg
->cmsg_type
) {
125 struct in6_pktinfo
*i
= (struct in6_pktinfo
*) CMSG_DATA(cmsg
);
128 s
->ifindex
= i
->ipi6_ifindex
;
133 s
->ttl
= *(int *) CMSG_DATA(cmsg
);
137 } else if (cmsg
->cmsg_level
== IPPROTO_IP
) {
138 assert(s
->peer
.sa
.sa_family
== AF_INET
);
140 switch (cmsg
->cmsg_type
) {
143 struct in_pktinfo
*i
= (struct in_pktinfo
*) CMSG_DATA(cmsg
);
146 s
->ifindex
= i
->ipi_ifindex
;
151 s
->ttl
= *(int *) CMSG_DATA(cmsg
);
157 /* The Linux kernel sets the interface index to the loopback
158 * device if the connection came from the local host since it
159 * avoids the routing table in such a case. Let's unset the
160 * interface index in such a case. */
161 if (s
->ifindex
== LOOPBACK_IFINDEX
)
164 /* If we don't know the interface index still, we look for the
165 * first local interface with a matching address. Yuck! */
167 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
);
169 if (s
->protocol
== DNS_PROTOCOL_LLMNR
&& s
->ifindex
> 0) {
170 uint32_t ifindex
= htobe32(s
->ifindex
);
172 /* Make sure all packets for this connection are sent on the same interface */
173 if (s
->local
.sa
.sa_family
== AF_INET
) {
174 r
= setsockopt(s
->fd
, IPPROTO_IP
, IP_UNICAST_IF
, &ifindex
, sizeof(ifindex
));
176 log_debug_errno(errno
, "Failed to invoke IP_UNICAST_IF: %m");
177 } else if (s
->local
.sa
.sa_family
== AF_INET6
) {
178 r
= setsockopt(s
->fd
, IPPROTO_IPV6
, IPV6_UNICAST_IF
, &ifindex
, sizeof(ifindex
));
180 log_debug_errno(errno
, "Failed to invoke IPV6_UNICAST_IF: %m");
184 s
->identified
= true;
189 static int on_stream_timeout(sd_event_source
*es
, usec_t usec
, void *userdata
) {
190 DnsStream
*s
= userdata
;
194 return dns_stream_complete(s
, ETIMEDOUT
);
197 static int on_stream_io(sd_event_source
*es
, int fd
, uint32_t revents
, void *userdata
) {
198 DnsStream
*s
= userdata
;
203 r
= dns_stream_identify(s
);
205 return dns_stream_complete(s
, -r
);
207 if ((revents
& EPOLLOUT
) &&
209 s
->n_written
< sizeof(s
->write_size
) + s
->write_packet
->size
) {
214 iov
[0].iov_base
= &s
->write_size
;
215 iov
[0].iov_len
= sizeof(s
->write_size
);
216 iov
[1].iov_base
= DNS_PACKET_DATA(s
->write_packet
);
217 iov
[1].iov_len
= s
->write_packet
->size
;
219 IOVEC_INCREMENT(iov
, 2, s
->n_written
);
221 ss
= writev(fd
, iov
, 2);
223 if (errno
!= EINTR
&& errno
!= EAGAIN
)
224 return dns_stream_complete(s
, errno
);
228 /* Are we done? If so, disable the event source for EPOLLOUT */
229 if (s
->n_written
>= sizeof(s
->write_size
) + s
->write_packet
->size
) {
230 r
= dns_stream_update_io(s
);
232 return dns_stream_complete(s
, -r
);
236 if ((revents
& (EPOLLIN
|EPOLLHUP
|EPOLLRDHUP
)) &&
238 s
->n_read
< sizeof(s
->read_size
) + s
->read_packet
->size
)) {
240 if (s
->n_read
< sizeof(s
->read_size
)) {
243 ss
= read(fd
, (uint8_t*) &s
->read_size
+ s
->n_read
, sizeof(s
->read_size
) - s
->n_read
);
245 if (errno
!= EINTR
&& errno
!= EAGAIN
)
246 return dns_stream_complete(s
, errno
);
248 return dns_stream_complete(s
, ECONNRESET
);
253 if (s
->n_read
>= sizeof(s
->read_size
)) {
255 if (be16toh(s
->read_size
) < DNS_PACKET_HEADER_SIZE
)
256 return dns_stream_complete(s
, EBADMSG
);
258 if (s
->n_read
< sizeof(s
->read_size
) + be16toh(s
->read_size
)) {
261 if (!s
->read_packet
) {
262 r
= dns_packet_new(&s
->read_packet
, s
->protocol
, be16toh(s
->read_size
));
264 return dns_stream_complete(s
, -r
);
266 s
->read_packet
->size
= be16toh(s
->read_size
);
267 s
->read_packet
->ipproto
= IPPROTO_TCP
;
268 s
->read_packet
->family
= s
->peer
.sa
.sa_family
;
269 s
->read_packet
->ttl
= s
->ttl
;
270 s
->read_packet
->ifindex
= s
->ifindex
;
272 if (s
->read_packet
->family
== AF_INET
) {
273 s
->read_packet
->sender
.in
= s
->peer
.in
.sin_addr
;
274 s
->read_packet
->sender_port
= be16toh(s
->peer
.in
.sin_port
);
275 s
->read_packet
->destination
.in
= s
->local
.in
.sin_addr
;
276 s
->read_packet
->destination_port
= be16toh(s
->local
.in
.sin_port
);
278 assert(s
->read_packet
->family
== AF_INET6
);
279 s
->read_packet
->sender
.in6
= s
->peer
.in6
.sin6_addr
;
280 s
->read_packet
->sender_port
= be16toh(s
->peer
.in6
.sin6_port
);
281 s
->read_packet
->destination
.in6
= s
->local
.in6
.sin6_addr
;
282 s
->read_packet
->destination_port
= be16toh(s
->local
.in6
.sin6_port
);
284 if (s
->read_packet
->ifindex
== 0)
285 s
->read_packet
->ifindex
= s
->peer
.in6
.sin6_scope_id
;
286 if (s
->read_packet
->ifindex
== 0)
287 s
->read_packet
->ifindex
= s
->local
.in6
.sin6_scope_id
;
292 (uint8_t*) DNS_PACKET_DATA(s
->read_packet
) + s
->n_read
- sizeof(s
->read_size
),
293 sizeof(s
->read_size
) + be16toh(s
->read_size
) - s
->n_read
);
295 if (errno
!= EINTR
&& errno
!= EAGAIN
)
296 return dns_stream_complete(s
, errno
);
298 return dns_stream_complete(s
, ECONNRESET
);
303 /* Are we done? If so, disable the event source for EPOLLIN */
304 if (s
->n_read
>= sizeof(s
->read_size
) + be16toh(s
->read_size
)) {
305 r
= dns_stream_update_io(s
);
307 return dns_stream_complete(s
, -r
);
309 /* If there's a packet handler
310 * installed, call that. Note that
311 * this is optional... */
313 return s
->on_packet(s
);
318 if ((s
->write_packet
&& s
->n_written
>= sizeof(s
->write_size
) + s
->write_packet
->size
) &&
319 (s
->read_packet
&& s
->n_read
>= sizeof(s
->read_size
) + s
->read_packet
->size
))
320 return dns_stream_complete(s
, 0);
325 DnsStream
*dns_stream_free(DnsStream
*s
) {
332 LIST_REMOVE(streams
, s
->manager
->dns_streams
, s
);
333 s
->manager
->n_dns_streams
--;
336 dns_packet_unref(s
->write_packet
);
337 dns_packet_unref(s
->read_packet
);
344 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream
*, dns_stream_free
);
346 int dns_stream_new(Manager
*m
, DnsStream
**ret
, DnsProtocol protocol
, int fd
) {
347 static const int one
= 1;
348 _cleanup_(dns_stream_freep
) DnsStream
*s
= NULL
;
354 if (m
->n_dns_streams
> DNS_STREAMS_MAX
)
357 s
= new0(DnsStream
, 1);
362 s
->protocol
= protocol
;
364 r
= setsockopt(fd
, IPPROTO_TCP
, TCP_NODELAY
, &one
, sizeof(one
));
368 r
= sd_event_add_io(m
->event
, &s
->io_event_source
, fd
, EPOLLIN
, on_stream_io
, s
);
372 r
= sd_event_add_time(
374 &s
->timeout_event_source
,
375 clock_boottime_or_monotonic(),
376 now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC
, 0,
377 on_stream_timeout
, s
);
381 LIST_PREPEND(streams
, m
->dns_streams
, s
);
392 int dns_stream_write_packet(DnsStream
*s
, DnsPacket
*p
) {
398 s
->write_packet
= dns_packet_ref(p
);
399 s
->write_size
= htobe16(p
->size
);
402 return dns_stream_update_io(s
);