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(MAX(sizeof(struct in_pktinfo), sizeof(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;
116 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
118 if (cmsg->cmsg_level == IPPROTO_IPV6) {
119 assert(s->peer.sa.sa_family == AF_INET6);
121 switch (cmsg->cmsg_type) {
124 struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
127 s->ifindex = i->ipi6_ifindex;
132 s->ttl = *(int *) CMSG_DATA(cmsg);
136 } else if (cmsg->cmsg_level == IPPROTO_IP) {
137 assert(s->peer.sa.sa_family == AF_INET);
139 switch (cmsg->cmsg_type) {
142 struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
145 s->ifindex = i->ipi_ifindex;
150 s->ttl = *(int *) CMSG_DATA(cmsg);
156 /* The Linux kernel sets the interface index to the loopback
157 * device if the connection came from the local host since it
158 * avoids the routing table in such a case. Let's unset the
159 * interface index in such a case. */
160 if (s->ifindex > 0 && manager_ifindex_is_loopback(s->manager, s->ifindex) != 0)
163 /* If we don't know the interface index still, we look for the
164 * first local interface with a matching address. Yuck! */
166 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);
168 if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) {
169 uint32_t ifindex = htobe32(s->ifindex);
171 /* Make sure all packets for this connection are sent on the same interface */
172 if (s->local.sa.sa_family == AF_INET) {
173 r = setsockopt(s->fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
175 log_debug("Failed to invoke IP_UNICAST_IF: %m");
176 } else if (s->local.sa.sa_family == AF_INET6) {
177 r = setsockopt(s->fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
179 log_debug("Failed to invoke IPV6_UNICAST_IF: %m");
183 s->identified = true;
188 static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
189 DnsStream *s = userdata;
193 return dns_stream_complete(s, ETIMEDOUT);
196 static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
197 DnsStream *s = userdata;
202 r = dns_stream_identify(s);
204 return dns_stream_complete(s, -r);
206 if ((revents & EPOLLOUT) &&
208 s->n_written < sizeof(s->write_size) + s->write_packet->size) {
213 iov[0].iov_base = &s->write_size;
214 iov[0].iov_len = sizeof(s->write_size);
215 iov[1].iov_base = DNS_PACKET_DATA(s->write_packet);
216 iov[1].iov_len = s->write_packet->size;
218 IOVEC_INCREMENT(iov, 2, s->n_written);
220 ss = writev(fd, iov, 2);
222 if (errno != EINTR && errno != EAGAIN)
223 return dns_stream_complete(s, errno);
227 /* Are we done? If so, disable the event source for EPOLLOUT */
228 if (s->n_written >= sizeof(s->write_size) + s->write_packet->size) {
229 r = dns_stream_update_io(s);
231 return dns_stream_complete(s, -r);
235 if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) &&
237 s->n_read < sizeof(s->read_size) + s->read_packet->size)) {
239 if (s->n_read < sizeof(s->read_size)) {
242 ss = read(fd, (uint8_t*) &s->read_size + s->n_read, sizeof(s->read_size) - s->n_read);
244 if (errno != EINTR && errno != EAGAIN)
245 return dns_stream_complete(s, errno);
247 return dns_stream_complete(s, ECONNRESET);
252 if (s->n_read >= sizeof(s->read_size)) {
254 if (be16toh(s->read_size) < DNS_PACKET_HEADER_SIZE)
255 return dns_stream_complete(s, EBADMSG);
257 if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) {
260 if (!s->read_packet) {
261 r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size));
263 return dns_stream_complete(s, -r);
265 s->read_packet->size = be16toh(s->read_size);
266 s->read_packet->ipproto = IPPROTO_TCP;
267 s->read_packet->family = s->peer.sa.sa_family;
268 s->read_packet->ttl = s->ttl;
269 s->read_packet->ifindex = s->ifindex;
271 if (s->read_packet->family == AF_INET) {
272 s->read_packet->sender.in = s->peer.in.sin_addr;
273 s->read_packet->sender_port = be16toh(s->peer.in.sin_port);
274 s->read_packet->destination.in = s->local.in.sin_addr;
275 s->read_packet->destination_port = be16toh(s->local.in.sin_port);
277 assert(s->read_packet->family == AF_INET6);
278 s->read_packet->sender.in6 = s->peer.in6.sin6_addr;
279 s->read_packet->sender_port = be16toh(s->peer.in6.sin6_port);
280 s->read_packet->destination.in6 = s->local.in6.sin6_addr;
281 s->read_packet->destination_port = be16toh(s->local.in6.sin6_port);
283 if (s->read_packet->ifindex == 0)
284 s->read_packet->ifindex = s->peer.in6.sin6_scope_id;
285 if (s->read_packet->ifindex == 0)
286 s->read_packet->ifindex = s->local.in6.sin6_scope_id;
291 (uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size),
292 sizeof(s->read_size) + be16toh(s->read_size) - s->n_read);
294 if (errno != EINTR && errno != EAGAIN)
295 return dns_stream_complete(s, errno);
297 return dns_stream_complete(s, ECONNRESET);
302 /* Are we done? If so, disable the event source for EPOLLIN */
303 if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) {
304 r = dns_stream_update_io(s);
306 return dns_stream_complete(s, -r);
308 /* If there's a packet handler
309 * installed, call that. Note that
310 * this is optional... */
312 return s->on_packet(s);
317 if ((s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) &&
318 (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size))
319 return dns_stream_complete(s, 0);
324 DnsStream *dns_stream_free(DnsStream *s) {
331 LIST_REMOVE(streams, s->manager->dns_streams, s);
332 s->manager->n_dns_streams--;
335 dns_packet_unref(s->write_packet);
336 dns_packet_unref(s->read_packet);
343 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_free);
345 int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
346 static const int one = 1;
347 _cleanup_(dns_stream_freep) DnsStream *s = NULL;
353 if (m->n_dns_streams > DNS_STREAMS_MAX)
356 s = new0(DnsStream, 1);
361 s->protocol = protocol;
363 r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
367 r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s);
371 r = sd_event_add_time(
373 &s->timeout_event_source,
374 clock_boottime_or_monotonic(),
375 now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC, 0,
376 on_stream_timeout, s);
380 LIST_PREPEND(streams, m->dns_streams, s);
391 int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
397 s->write_packet = dns_packet_ref(p);
398 s->write_size = htobe16(p->size);
401 return dns_stream_update_io(s);