resolved-dns-stream.c revision 56f64d95763a799ba4475daf44d8e9f72a1bd474
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen/***
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen This file is part of systemd.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen Copyright 2014 Lennart Poettering
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen systemd is free software; you can redistribute it and/or modify it
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen under the terms of the GNU Lesser General Public License as published by
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen (at your option) any later version.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen systemd is distributed in the hope that it will be useful, but
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen Lesser General Public License for more details.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen You should have received a copy of the GNU Lesser General Public License
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen***/
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <netinet/tcp.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include "missing.h"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include "resolved-dns-stream.h"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#define DNS_STREAMS_MAX 128
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic void dns_stream_stop(DnsStream *s) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen assert(s);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->io_event_source = sd_event_source_unref(s->io_event_source);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->timeout_event_source = sd_event_source_unref(s->timeout_event_source);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->fd = safe_close(s->fd);
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen}
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic int dns_stream_update_io(DnsStream *s) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen int f = 0;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen assert(s);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen if (s->write_packet && s->n_written < sizeof(s->write_size) + s->write_packet->size)
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen f |= EPOLLOUT;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen if (!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size)
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen f |= EPOLLIN;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen return sd_event_source_set_io_events(s->io_event_source, f);
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen}
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic int dns_stream_complete(DnsStream *s, int error) {
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen assert(s);
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen dns_stream_stop(s);
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen if (s->complete)
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen s->complete(s, error);
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen else
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen dns_stream_free(s);
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen return 0;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen}
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic int dns_stream_identify(DnsStream *s) {
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen union {
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen struct cmsghdr header; /* For alignment */
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen + EXTRA_CMSG_SPACE /* kernel appears to require extra space */];
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen } control;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen struct msghdr mh = {};
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen struct cmsghdr *cmsg;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen socklen_t sl;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen int r;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen assert(s);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (s->identified)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return 0;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen /* Query the local side */
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->local_salen = sizeof(s->local);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen r = getsockname(s->fd, &s->local.sa, &s->local_salen);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (r < 0)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return -errno;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (s->local.sa.sa_family == AF_INET6 && s->ifindex <= 0)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->ifindex = s->local.in6.sin6_scope_id;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen /* Query the remote side */
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->peer_salen = sizeof(s->peer);
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen r = getpeername(s->fd, &s->peer.sa, &s->peer_salen);
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen if (r < 0)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return -errno;
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen if (s->peer.sa.sa_family == AF_INET6 && s->ifindex <= 0)
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen s->ifindex = s->peer.in6.sin6_scope_id;
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen /* Check consistency */
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen assert(s->peer.sa.sa_family == s->local.sa.sa_family);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6));
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen /* Query connection meta information */
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen sl = sizeof(control);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (s->peer.sa.sa_family == AF_INET) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen r = getsockopt(s->fd, IPPROTO_IP, IP_PKTOPTIONS, &control, &sl);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (r < 0)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return -errno;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen } else if (s->peer.sa.sa_family == AF_INET6) {
8cd11a0f0f4ca05199e1166f6a07472b296f7455Tom Gundersen
8cd11a0f0f4ca05199e1166f6a07472b296f7455Tom Gundersen r = getsockopt(s->fd, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, &control, &sl);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (r < 0)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return -errno;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen } else
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return -EAFNOSUPPORT;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen mh.msg_control = &control;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen mh.msg_controllen = sl;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (cmsg->cmsg_level == IPPROTO_IPV6) {
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen assert(s->peer.sa.sa_family == AF_INET6);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen switch (cmsg->cmsg_type) {
b3070dc0258831c7e2b13624f75fa3dbd80d9833Tom Gundersen
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen case IPV6_PKTINFO: {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (s->ifindex <= 0)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->ifindex = i->ipi6_ifindex;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen break;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen }
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen case IPV6_HOPLIMIT:
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen s->ttl = *(int *) CMSG_DATA(cmsg);
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen break;
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen }
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen } else if (cmsg->cmsg_level == IPPROTO_IP) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen assert(s->peer.sa.sa_family == AF_INET);
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen
ef1ba6065c6ccea94d4ee867f36df7bbc53a5224Tom Gundersen switch (cmsg->cmsg_type) {
ef1ba6065c6ccea94d4ee867f36df7bbc53a5224Tom Gundersen
ef1ba6065c6ccea94d4ee867f36df7bbc53a5224Tom Gundersen case IP_PKTINFO: {
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen if (s->ifindex <= 0)
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen s->ifindex = i->ipi_ifindex;
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen break;
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen }
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen case IP_TTL:
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->ttl = *(int *) CMSG_DATA(cmsg);
0617ffabe86a6d366252477eafbe59a888b149d4Tom Gundersen break;
c166a070553511e402de5ad216d3fb62b49bdacbTom Gundersen }
8cd11a0f0f4ca05199e1166f6a07472b296f7455Tom Gundersen }
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen }
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen /* The Linux kernel sets the interface index to the loopback
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen * device if the connection came from the local host since it
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen * avoids the routing table in such a case. Let's unset the
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen * interface index in such a case. */
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen if (s->ifindex == LOOPBACK_IFINDEX)
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen s->ifindex = 0;
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen /* If we don't know the interface index still, we look for the
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen * first local interface with a matching address. Yuck! */
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen if (s->ifindex <= 0)
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen 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);
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen uint32_t ifindex = htobe32(s->ifindex);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen /* Make sure all packets for this connection are sent on the same interface */
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (s->local.sa.sa_family == AF_INET) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen r = setsockopt(s->fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (r < 0)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF: %m");
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen } else if (s->local.sa.sa_family == AF_INET6) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen r = setsockopt(s->fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen if (r < 0)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen log_debug_errno(errno, "Failed to invoke IPV6_UNICAST_IF: %m");
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen }
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen }
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->identified = true;
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek return 0;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen}
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen DnsStream *s = userdata;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen assert(s);
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen return dns_stream_complete(s, ETIMEDOUT);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen}
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen DnsStream *s = userdata;
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen int r;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen assert(s);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen r = dns_stream_identify(s);
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen if (r < 0)
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen return dns_stream_complete(s, -r);
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen if ((revents & EPOLLOUT) &&
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen s->write_packet &&
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen s->n_written < sizeof(s->write_size) + s->write_packet->size) {
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen struct iovec iov[2];
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen ssize_t ss;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen iov[0].iov_base = &s->write_size;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen iov[0].iov_len = sizeof(s->write_size);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen iov[1].iov_base = DNS_PACKET_DATA(s->write_packet);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen iov[1].iov_len = s->write_packet->size;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen IOVEC_INCREMENT(iov, 2, s->n_written);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ss = writev(fd, iov, 2);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (ss < 0) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (errno != EINTR && errno != EAGAIN)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return dns_stream_complete(s, errno);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen } else
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->n_written += ss;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen /* Are we done? If so, disable the event source for EPOLLOUT */
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen if (s->n_written >= sizeof(s->write_size) + s->write_packet->size) {
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen r = dns_stream_update_io(s);
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen if (r < 0)
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen return dns_stream_complete(s, -r);
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen }
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen }
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) &&
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen (!s->read_packet ||
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen s->n_read < sizeof(s->read_size) + s->read_packet->size)) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen if (s->n_read < sizeof(s->read_size)) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ssize_t ss;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ss = read(fd, (uint8_t*) &s->read_size + s->n_read, sizeof(s->read_size) - s->n_read);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (ss < 0) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (errno != EINTR && errno != EAGAIN)
71a6151083d842b2f5bf04e50239f0bf85d34d2eTom Gundersen return dns_stream_complete(s, errno);
71a6151083d842b2f5bf04e50239f0bf85d34d2eTom Gundersen } else if (ss == 0)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return dns_stream_complete(s, ECONNRESET);
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen else
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen s->n_read += ss;
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen }
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (s->n_read >= sizeof(s->read_size)) {
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen if (be16toh(s->read_size) < DNS_PACKET_HEADER_SIZE)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return dns_stream_complete(s, EBADMSG);
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen
407fe036a24834203aca6c1eec7d74d9ad3e9ee0Tom Gundersen if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ssize_t ss;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (!s->read_packet) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size));
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (r < 0)
71a6151083d842b2f5bf04e50239f0bf85d34d2eTom Gundersen return dns_stream_complete(s, -r);
71a6151083d842b2f5bf04e50239f0bf85d34d2eTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->read_packet->size = be16toh(s->read_size);
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen s->read_packet->ipproto = IPPROTO_TCP;
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen s->read_packet->family = s->peer.sa.sa_family;
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen s->read_packet->ttl = s->ttl;
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen s->read_packet->ifindex = s->ifindex;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (s->read_packet->family == AF_INET) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->read_packet->sender.in = s->peer.in.sin_addr;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->read_packet->sender_port = be16toh(s->peer.in.sin_port);
aa3437a573ed2a2b2a03c9649fe11d27b40a772bTom Gundersen s->read_packet->destination.in = s->local.in.sin_addr;
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen s->read_packet->destination_port = be16toh(s->local.in.sin_port);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen } else {
22936833e177d9e477e059ec2b4f4fa9213b4e19Tom Gundersen assert(s->read_packet->family == AF_INET6);
dd3efc0993b6e95ff04775e9125e2fc9d30fe261Tom Gundersen s->read_packet->sender.in6 = s->peer.in6.sin6_addr;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->read_packet->sender_port = be16toh(s->peer.in6.sin6_port);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->read_packet->destination.in6 = s->local.in6.sin6_addr;
s->read_packet->destination_port = be16toh(s->local.in6.sin6_port);
if (s->read_packet->ifindex == 0)
s->read_packet->ifindex = s->peer.in6.sin6_scope_id;
if (s->read_packet->ifindex == 0)
s->read_packet->ifindex = s->local.in6.sin6_scope_id;
}
}
ss = read(fd,
(uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size),
sizeof(s->read_size) + be16toh(s->read_size) - s->n_read);
if (ss < 0) {
if (errno != EINTR && errno != EAGAIN)
return dns_stream_complete(s, errno);
} else if (ss == 0)
return dns_stream_complete(s, ECONNRESET);
else
s->n_read += ss;
}
/* Are we done? If so, disable the event source for EPOLLIN */
if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) {
r = dns_stream_update_io(s);
if (r < 0)
return dns_stream_complete(s, -r);
/* If there's a packet handler
* installed, call that. Note that
* this is optional... */
if (s->on_packet)
return s->on_packet(s);
}
}
}
if ((s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) &&
(s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size))
return dns_stream_complete(s, 0);
return 0;
}
DnsStream *dns_stream_free(DnsStream *s) {
if (!s)
return NULL;
dns_stream_stop(s);
if (s->manager) {
LIST_REMOVE(streams, s->manager->dns_streams, s);
s->manager->n_dns_streams--;
}
dns_packet_unref(s->write_packet);
dns_packet_unref(s->read_packet);
free(s);
return 0;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_free);
int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
static const int one = 1;
_cleanup_(dns_stream_freep) DnsStream *s = NULL;
int r;
assert(m);
assert(fd >= 0);
if (m->n_dns_streams > DNS_STREAMS_MAX)
return -EBUSY;
s = new0(DnsStream, 1);
if (!s)
return -ENOMEM;
s->fd = -1;
s->protocol = protocol;
r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
if (r < 0)
return -errno;
r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s);
if (r < 0)
return r;
r = sd_event_add_time(
m->event,
&s->timeout_event_source,
clock_boottime_or_monotonic(),
now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC, 0,
on_stream_timeout, s);
if (r < 0)
return r;
LIST_PREPEND(streams, m->dns_streams, s);
s->manager = m;
s->fd = fd;
m->n_dns_streams++;
*ret = s;
s = NULL;
return 0;
}
int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
assert(s);
if (s->write_packet)
return -EBUSY;
s->write_packet = dns_packet_ref(p);
s->write_size = htobe16(p->size);
s->n_written = 0;
return dns_stream_update_io(s);
}