]>
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>
24 #include "alloc-util.h"
28 #include "resolved-dns-stream.h"
30 #define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
31 #define DNS_STREAMS_MAX 128
33 static void dns_stream_stop(DnsStream
*s
) {
36 s
->io_event_source
= sd_event_source_unref(s
->io_event_source
);
37 s
->timeout_event_source
= sd_event_source_unref(s
->timeout_event_source
);
38 s
->fd
= safe_close(s
->fd
);
41 static int dns_stream_update_io(DnsStream
*s
) {
46 if (s
->write_packet
&& s
->n_written
< sizeof(s
->write_size
) + s
->write_packet
->size
)
48 if (!s
->read_packet
|| s
->n_read
< sizeof(s
->read_size
) + s
->read_packet
->size
)
51 return sd_event_source_set_io_events(s
->io_event_source
, f
);
54 static int dns_stream_complete(DnsStream
*s
, int error
) {
60 s
->complete(s
, error
);
67 static int dns_stream_identify(DnsStream
*s
) {
69 struct cmsghdr header
; /* For alignment */
70 uint8_t buffer
[CMSG_SPACE(MAXSIZE(struct in_pktinfo
, struct in6_pktinfo
))
71 + EXTRA_CMSG_SPACE
/* kernel appears to require extra space */];
73 struct msghdr mh
= {};
83 /* Query the local side */
84 s
->local_salen
= sizeof(s
->local
);
85 r
= getsockname(s
->fd
, &s
->local
.sa
, &s
->local_salen
);
88 if (s
->local
.sa
.sa_family
== AF_INET6
&& s
->ifindex
<= 0)
89 s
->ifindex
= s
->local
.in6
.sin6_scope_id
;
91 /* Query the remote side */
92 s
->peer_salen
= sizeof(s
->peer
);
93 r
= getpeername(s
->fd
, &s
->peer
.sa
, &s
->peer_salen
);
96 if (s
->peer
.sa
.sa_family
== AF_INET6
&& s
->ifindex
<= 0)
97 s
->ifindex
= s
->peer
.in6
.sin6_scope_id
;
99 /* Check consistency */
100 assert(s
->peer
.sa
.sa_family
== s
->local
.sa
.sa_family
);
101 assert(IN_SET(s
->peer
.sa
.sa_family
, AF_INET
, AF_INET6
));
103 /* Query connection meta information */
104 sl
= sizeof(control
);
105 if (s
->peer
.sa
.sa_family
== AF_INET
) {
106 r
= getsockopt(s
->fd
, IPPROTO_IP
, IP_PKTOPTIONS
, &control
, &sl
);
109 } else if (s
->peer
.sa
.sa_family
== AF_INET6
) {
111 r
= getsockopt(s
->fd
, IPPROTO_IPV6
, IPV6_2292PKTOPTIONS
, &control
, &sl
);
115 return -EAFNOSUPPORT
;
117 mh
.msg_control
= &control
;
118 mh
.msg_controllen
= sl
;
120 CMSG_FOREACH(cmsg
, &mh
) {
122 if (cmsg
->cmsg_level
== IPPROTO_IPV6
) {
123 assert(s
->peer
.sa
.sa_family
== AF_INET6
);
125 switch (cmsg
->cmsg_type
) {
128 struct in6_pktinfo
*i
= (struct in6_pktinfo
*) CMSG_DATA(cmsg
);
131 s
->ifindex
= i
->ipi6_ifindex
;
136 s
->ttl
= *(int *) CMSG_DATA(cmsg
);
140 } else if (cmsg
->cmsg_level
== IPPROTO_IP
) {
141 assert(s
->peer
.sa
.sa_family
== AF_INET
);
143 switch (cmsg
->cmsg_type
) {
146 struct in_pktinfo
*i
= (struct in_pktinfo
*) CMSG_DATA(cmsg
);
149 s
->ifindex
= i
->ipi_ifindex
;
154 s
->ttl
= *(int *) CMSG_DATA(cmsg
);
160 /* The Linux kernel sets the interface index to the loopback
161 * device if the connection came from the local host since it
162 * avoids the routing table in such a case. Let's unset the
163 * interface index in such a case. */
164 if (s
->ifindex
== LOOPBACK_IFINDEX
)
167 /* If we don't know the interface index still, we look for the
168 * first local interface with a matching address. Yuck! */
170 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
);
172 if (s
->protocol
== DNS_PROTOCOL_LLMNR
&& s
->ifindex
> 0) {
173 uint32_t ifindex
= htobe32(s
->ifindex
);
175 /* Make sure all packets for this connection are sent on the same interface */
176 if (s
->local
.sa
.sa_family
== AF_INET
) {
177 r
= setsockopt(s
->fd
, IPPROTO_IP
, IP_UNICAST_IF
, &ifindex
, sizeof(ifindex
));
179 log_debug_errno(errno
, "Failed to invoke IP_UNICAST_IF: %m");
180 } else if (s
->local
.sa
.sa_family
== AF_INET6
) {
181 r
= setsockopt(s
->fd
, IPPROTO_IPV6
, IPV6_UNICAST_IF
, &ifindex
, sizeof(ifindex
));
183 log_debug_errno(errno
, "Failed to invoke IPV6_UNICAST_IF: %m");
187 s
->identified
= true;
192 static int on_stream_timeout(sd_event_source
*es
, usec_t usec
, void *userdata
) {
193 DnsStream
*s
= userdata
;
197 return dns_stream_complete(s
, ETIMEDOUT
);
200 static int on_stream_io(sd_event_source
*es
, int fd
, uint32_t revents
, void *userdata
) {
201 DnsStream
*s
= userdata
;
206 r
= dns_stream_identify(s
);
208 return dns_stream_complete(s
, -r
);
210 if ((revents
& EPOLLOUT
) &&
212 s
->n_written
< sizeof(s
->write_size
) + s
->write_packet
->size
) {
217 iov
[0].iov_base
= &s
->write_size
;
218 iov
[0].iov_len
= sizeof(s
->write_size
);
219 iov
[1].iov_base
= DNS_PACKET_DATA(s
->write_packet
);
220 iov
[1].iov_len
= s
->write_packet
->size
;
222 IOVEC_INCREMENT(iov
, 2, s
->n_written
);
224 ss
= writev(fd
, iov
, 2);
226 if (errno
!= EINTR
&& errno
!= EAGAIN
)
227 return dns_stream_complete(s
, errno
);
231 /* Are we done? If so, disable the event source for EPOLLOUT */
232 if (s
->n_written
>= sizeof(s
->write_size
) + s
->write_packet
->size
) {
233 r
= dns_stream_update_io(s
);
235 return dns_stream_complete(s
, -r
);
239 if ((revents
& (EPOLLIN
|EPOLLHUP
|EPOLLRDHUP
)) &&
241 s
->n_read
< sizeof(s
->read_size
) + s
->read_packet
->size
)) {
243 if (s
->n_read
< sizeof(s
->read_size
)) {
246 ss
= read(fd
, (uint8_t*) &s
->read_size
+ s
->n_read
, sizeof(s
->read_size
) - s
->n_read
);
248 if (errno
!= EINTR
&& errno
!= EAGAIN
)
249 return dns_stream_complete(s
, errno
);
251 return dns_stream_complete(s
, ECONNRESET
);
256 if (s
->n_read
>= sizeof(s
->read_size
)) {
258 if (be16toh(s
->read_size
) < DNS_PACKET_HEADER_SIZE
)
259 return dns_stream_complete(s
, EBADMSG
);
261 if (s
->n_read
< sizeof(s
->read_size
) + be16toh(s
->read_size
)) {
264 if (!s
->read_packet
) {
265 r
= dns_packet_new(&s
->read_packet
, s
->protocol
, be16toh(s
->read_size
));
267 return dns_stream_complete(s
, -r
);
269 s
->read_packet
->size
= be16toh(s
->read_size
);
270 s
->read_packet
->ipproto
= IPPROTO_TCP
;
271 s
->read_packet
->family
= s
->peer
.sa
.sa_family
;
272 s
->read_packet
->ttl
= s
->ttl
;
273 s
->read_packet
->ifindex
= s
->ifindex
;
275 if (s
->read_packet
->family
== AF_INET
) {
276 s
->read_packet
->sender
.in
= s
->peer
.in
.sin_addr
;
277 s
->read_packet
->sender_port
= be16toh(s
->peer
.in
.sin_port
);
278 s
->read_packet
->destination
.in
= s
->local
.in
.sin_addr
;
279 s
->read_packet
->destination_port
= be16toh(s
->local
.in
.sin_port
);
281 assert(s
->read_packet
->family
== AF_INET6
);
282 s
->read_packet
->sender
.in6
= s
->peer
.in6
.sin6_addr
;
283 s
->read_packet
->sender_port
= be16toh(s
->peer
.in6
.sin6_port
);
284 s
->read_packet
->destination
.in6
= s
->local
.in6
.sin6_addr
;
285 s
->read_packet
->destination_port
= be16toh(s
->local
.in6
.sin6_port
);
287 if (s
->read_packet
->ifindex
== 0)
288 s
->read_packet
->ifindex
= s
->peer
.in6
.sin6_scope_id
;
289 if (s
->read_packet
->ifindex
== 0)
290 s
->read_packet
->ifindex
= s
->local
.in6
.sin6_scope_id
;
295 (uint8_t*) DNS_PACKET_DATA(s
->read_packet
) + s
->n_read
- sizeof(s
->read_size
),
296 sizeof(s
->read_size
) + be16toh(s
->read_size
) - s
->n_read
);
298 if (errno
!= EINTR
&& errno
!= EAGAIN
)
299 return dns_stream_complete(s
, errno
);
301 return dns_stream_complete(s
, ECONNRESET
);
306 /* Are we done? If so, disable the event source for EPOLLIN */
307 if (s
->n_read
>= sizeof(s
->read_size
) + be16toh(s
->read_size
)) {
308 r
= dns_stream_update_io(s
);
310 return dns_stream_complete(s
, -r
);
312 /* If there's a packet handler
313 * installed, call that. Note that
314 * this is optional... */
316 return s
->on_packet(s
);
321 if ((s
->write_packet
&& s
->n_written
>= sizeof(s
->write_size
) + s
->write_packet
->size
) &&
322 (s
->read_packet
&& s
->n_read
>= sizeof(s
->read_size
) + s
->read_packet
->size
))
323 return dns_stream_complete(s
, 0);
328 DnsStream
*dns_stream_free(DnsStream
*s
) {
335 LIST_REMOVE(streams
, s
->manager
->dns_streams
, s
);
336 s
->manager
->n_dns_streams
--;
339 dns_packet_unref(s
->write_packet
);
340 dns_packet_unref(s
->read_packet
);
347 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream
*, dns_stream_free
);
349 int dns_stream_new(Manager
*m
, DnsStream
**ret
, DnsProtocol protocol
, int fd
) {
350 static const int one
= 1;
351 _cleanup_(dns_stream_freep
) DnsStream
*s
= NULL
;
357 if (m
->n_dns_streams
> DNS_STREAMS_MAX
)
360 s
= new0(DnsStream
, 1);
365 s
->protocol
= protocol
;
367 r
= setsockopt(fd
, IPPROTO_TCP
, TCP_NODELAY
, &one
, sizeof(one
));
371 r
= sd_event_add_io(m
->event
, &s
->io_event_source
, fd
, EPOLLIN
, on_stream_io
, s
);
375 r
= sd_event_add_time(
377 &s
->timeout_event_source
,
378 clock_boottime_or_monotonic(),
379 now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC
, 0,
380 on_stream_timeout
, s
);
384 LIST_PREPEND(streams
, m
->dns_streams
, s
);
395 int dns_stream_write_packet(DnsStream
*s
, DnsPacket
*p
) {
401 s
->write_packet
= dns_packet_ref(p
);
402 s
->write_size
= htobe16(p
->size
);
405 return dns_stream_update_io(s
);