]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-stream.c
2 This file is part of systemd.
4 Copyright 2014 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include <netinet/tcp.h>
22 #include "alloc-util.h"
26 #include "resolved-dns-stream.h"
28 #define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
29 #define DNS_STREAMS_MAX 128
31 static void dns_stream_stop(DnsStream
*s
) {
34 s
->io_event_source
= sd_event_source_unref(s
->io_event_source
);
35 s
->timeout_event_source
= sd_event_source_unref(s
->timeout_event_source
);
36 s
->fd
= safe_close(s
->fd
);
39 static int dns_stream_update_io(DnsStream
*s
) {
44 if (s
->write_packet
&& s
->n_written
< sizeof(s
->write_size
) + s
->write_packet
->size
)
46 if (!s
->read_packet
|| s
->n_read
< sizeof(s
->read_size
) + s
->read_packet
->size
)
49 return sd_event_source_set_io_events(s
->io_event_source
, f
);
52 static int dns_stream_complete(DnsStream
*s
, int error
) {
58 s
->complete(s
, error
);
65 static int dns_stream_identify(DnsStream
*s
) {
67 struct cmsghdr header
; /* For alignment */
68 uint8_t buffer
[CMSG_SPACE(MAXSIZE(struct in_pktinfo
, struct in6_pktinfo
))
69 + EXTRA_CMSG_SPACE
/* kernel appears to require extra space */];
71 struct msghdr mh
= {};
81 /* Query the local side */
82 s
->local_salen
= sizeof(s
->local
);
83 r
= getsockname(s
->fd
, &s
->local
.sa
, &s
->local_salen
);
86 if (s
->local
.sa
.sa_family
== AF_INET6
&& s
->ifindex
<= 0)
87 s
->ifindex
= s
->local
.in6
.sin6_scope_id
;
89 /* Query the remote side */
90 s
->peer_salen
= sizeof(s
->peer
);
91 r
= getpeername(s
->fd
, &s
->peer
.sa
, &s
->peer_salen
);
94 if (s
->peer
.sa
.sa_family
== AF_INET6
&& s
->ifindex
<= 0)
95 s
->ifindex
= s
->peer
.in6
.sin6_scope_id
;
97 /* Check consistency */
98 assert(s
->peer
.sa
.sa_family
== s
->local
.sa
.sa_family
);
99 assert(IN_SET(s
->peer
.sa
.sa_family
, AF_INET
, AF_INET6
));
101 /* Query connection meta information */
102 sl
= sizeof(control
);
103 if (s
->peer
.sa
.sa_family
== AF_INET
) {
104 r
= getsockopt(s
->fd
, IPPROTO_IP
, IP_PKTOPTIONS
, &control
, &sl
);
107 } else if (s
->peer
.sa
.sa_family
== AF_INET6
) {
109 r
= getsockopt(s
->fd
, IPPROTO_IPV6
, IPV6_2292PKTOPTIONS
, &control
, &sl
);
113 return -EAFNOSUPPORT
;
115 mh
.msg_control
= &control
;
116 mh
.msg_controllen
= sl
;
118 CMSG_FOREACH(cmsg
, &mh
) {
120 if (cmsg
->cmsg_level
== IPPROTO_IPV6
) {
121 assert(s
->peer
.sa
.sa_family
== AF_INET6
);
123 switch (cmsg
->cmsg_type
) {
126 struct in6_pktinfo
*i
= (struct in6_pktinfo
*) CMSG_DATA(cmsg
);
129 s
->ifindex
= i
->ipi6_ifindex
;
134 s
->ttl
= *(int *) CMSG_DATA(cmsg
);
138 } else if (cmsg
->cmsg_level
== IPPROTO_IP
) {
139 assert(s
->peer
.sa
.sa_family
== AF_INET
);
141 switch (cmsg
->cmsg_type
) {
144 struct in_pktinfo
*i
= (struct in_pktinfo
*) CMSG_DATA(cmsg
);
147 s
->ifindex
= i
->ipi_ifindex
;
152 s
->ttl
= *(int *) CMSG_DATA(cmsg
);
158 /* The Linux kernel sets the interface index to the loopback
159 * device if the connection came from the local host since it
160 * avoids the routing table in such a case. Let's unset the
161 * interface index in such a case. */
162 if (s
->ifindex
== LOOPBACK_IFINDEX
)
165 /* If we don't know the interface index still, we look for the
166 * first local interface with a matching address. Yuck! */
168 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
);
170 if (s
->protocol
== DNS_PROTOCOL_LLMNR
&& s
->ifindex
> 0) {
171 uint32_t ifindex
= htobe32(s
->ifindex
);
173 /* Make sure all packets for this connection are sent on the same interface */
174 if (s
->local
.sa
.sa_family
== AF_INET
) {
175 r
= setsockopt(s
->fd
, IPPROTO_IP
, IP_UNICAST_IF
, &ifindex
, sizeof(ifindex
));
177 log_debug_errno(errno
, "Failed to invoke IP_UNICAST_IF: %m");
178 } else if (s
->local
.sa
.sa_family
== AF_INET6
) {
179 r
= setsockopt(s
->fd
, IPPROTO_IPV6
, IPV6_UNICAST_IF
, &ifindex
, sizeof(ifindex
));
181 log_debug_errno(errno
, "Failed to invoke IPV6_UNICAST_IF: %m");
185 s
->identified
= true;
190 static int on_stream_timeout(sd_event_source
*es
, usec_t usec
, void *userdata
) {
191 DnsStream
*s
= userdata
;
195 return dns_stream_complete(s
, ETIMEDOUT
);
198 static int on_stream_io(sd_event_source
*es
, int fd
, uint32_t revents
, void *userdata
) {
199 DnsStream
*s
= userdata
;
204 r
= dns_stream_identify(s
);
206 return dns_stream_complete(s
, -r
);
208 if ((revents
& EPOLLOUT
) &&
210 s
->n_written
< sizeof(s
->write_size
) + s
->write_packet
->size
) {
215 iov
[0].iov_base
= &s
->write_size
;
216 iov
[0].iov_len
= sizeof(s
->write_size
);
217 iov
[1].iov_base
= DNS_PACKET_DATA(s
->write_packet
);
218 iov
[1].iov_len
= s
->write_packet
->size
;
220 IOVEC_INCREMENT(iov
, 2, s
->n_written
);
222 ss
= writev(fd
, iov
, 2);
224 if (errno
!= EINTR
&& errno
!= EAGAIN
)
225 return dns_stream_complete(s
, errno
);
229 /* Are we done? If so, disable the event source for EPOLLOUT */
230 if (s
->n_written
>= sizeof(s
->write_size
) + s
->write_packet
->size
) {
231 r
= dns_stream_update_io(s
);
233 return dns_stream_complete(s
, -r
);
237 if ((revents
& (EPOLLIN
|EPOLLHUP
|EPOLLRDHUP
)) &&
239 s
->n_read
< sizeof(s
->read_size
) + s
->read_packet
->size
)) {
241 if (s
->n_read
< sizeof(s
->read_size
)) {
244 ss
= read(fd
, (uint8_t*) &s
->read_size
+ s
->n_read
, sizeof(s
->read_size
) - s
->n_read
);
246 if (errno
!= EINTR
&& errno
!= EAGAIN
)
247 return dns_stream_complete(s
, errno
);
249 return dns_stream_complete(s
, ECONNRESET
);
254 if (s
->n_read
>= sizeof(s
->read_size
)) {
256 if (be16toh(s
->read_size
) < DNS_PACKET_HEADER_SIZE
)
257 return dns_stream_complete(s
, EBADMSG
);
259 if (s
->n_read
< sizeof(s
->read_size
) + be16toh(s
->read_size
)) {
262 if (!s
->read_packet
) {
263 r
= dns_packet_new(&s
->read_packet
, s
->protocol
, be16toh(s
->read_size
));
265 return dns_stream_complete(s
, -r
);
267 s
->read_packet
->size
= be16toh(s
->read_size
);
268 s
->read_packet
->ipproto
= IPPROTO_TCP
;
269 s
->read_packet
->family
= s
->peer
.sa
.sa_family
;
270 s
->read_packet
->ttl
= s
->ttl
;
271 s
->read_packet
->ifindex
= s
->ifindex
;
273 if (s
->read_packet
->family
== AF_INET
) {
274 s
->read_packet
->sender
.in
= s
->peer
.in
.sin_addr
;
275 s
->read_packet
->sender_port
= be16toh(s
->peer
.in
.sin_port
);
276 s
->read_packet
->destination
.in
= s
->local
.in
.sin_addr
;
277 s
->read_packet
->destination_port
= be16toh(s
->local
.in
.sin_port
);
279 assert(s
->read_packet
->family
== AF_INET6
);
280 s
->read_packet
->sender
.in6
= s
->peer
.in6
.sin6_addr
;
281 s
->read_packet
->sender_port
= be16toh(s
->peer
.in6
.sin6_port
);
282 s
->read_packet
->destination
.in6
= s
->local
.in6
.sin6_addr
;
283 s
->read_packet
->destination_port
= be16toh(s
->local
.in6
.sin6_port
);
285 if (s
->read_packet
->ifindex
== 0)
286 s
->read_packet
->ifindex
= s
->peer
.in6
.sin6_scope_id
;
287 if (s
->read_packet
->ifindex
== 0)
288 s
->read_packet
->ifindex
= s
->local
.in6
.sin6_scope_id
;
293 (uint8_t*) DNS_PACKET_DATA(s
->read_packet
) + s
->n_read
- sizeof(s
->read_size
),
294 sizeof(s
->read_size
) + be16toh(s
->read_size
) - s
->n_read
);
296 if (errno
!= EINTR
&& errno
!= EAGAIN
)
297 return dns_stream_complete(s
, errno
);
299 return dns_stream_complete(s
, ECONNRESET
);
304 /* Are we done? If so, disable the event source for EPOLLIN */
305 if (s
->n_read
>= sizeof(s
->read_size
) + be16toh(s
->read_size
)) {
306 r
= dns_stream_update_io(s
);
308 return dns_stream_complete(s
, -r
);
310 /* If there's a packet handler
311 * installed, call that. Note that
312 * this is optional... */
314 return s
->on_packet(s
);
319 if ((s
->write_packet
&& s
->n_written
>= sizeof(s
->write_size
) + s
->write_packet
->size
) &&
320 (s
->read_packet
&& s
->n_read
>= sizeof(s
->read_size
) + s
->read_packet
->size
))
321 return dns_stream_complete(s
, 0);
326 DnsStream
*dns_stream_free(DnsStream
*s
) {
333 LIST_REMOVE(streams
, s
->manager
->dns_streams
, s
);
334 s
->manager
->n_dns_streams
--;
337 dns_packet_unref(s
->write_packet
);
338 dns_packet_unref(s
->read_packet
);
345 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream
*, dns_stream_free
);
347 int dns_stream_new(Manager
*m
, DnsStream
**ret
, DnsProtocol protocol
, int fd
) {
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
= sd_event_add_io(m
->event
, &s
->io_event_source
, fd
, EPOLLIN
, on_stream_io
, s
);
368 (void) sd_event_source_set_description(s
->io_event_source
, "dns-stream-io");
370 r
= sd_event_add_time(
372 &s
->timeout_event_source
,
373 clock_boottime_or_monotonic(),
374 now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC
, 0,
375 on_stream_timeout
, s
);
379 (void) sd_event_source_set_description(s
->timeout_event_source
, "dns-stream-timeout");
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
);