LCOV - code coverage report
Current view: top level - resolve - resolved-dns-stream.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 330 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 15 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 310 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <netinet/tcp.h>
       4                 :            : 
       5                 :            : #include "alloc-util.h"
       6                 :            : #include "fd-util.h"
       7                 :            : #include "io-util.h"
       8                 :            : #include "missing.h"
       9                 :            : #include "resolved-dns-stream.h"
      10                 :            : 
      11                 :            : #define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
      12                 :            : #define DNS_STREAMS_MAX 128
      13                 :            : 
      14                 :            : #define DNS_QUERIES_PER_STREAM 32
      15                 :            : 
      16                 :          0 : static void dns_stream_stop(DnsStream *s) {
      17         [ #  # ]:          0 :         assert(s);
      18                 :            : 
      19                 :          0 :         s->io_event_source = sd_event_source_unref(s->io_event_source);
      20                 :          0 :         s->timeout_event_source = sd_event_source_unref(s->timeout_event_source);
      21                 :          0 :         s->fd = safe_close(s->fd);
      22                 :            : 
      23                 :            :         /* Disconnect us from the server object if we are now not usable anymore */
      24                 :          0 :         dns_stream_detach(s);
      25                 :          0 : }
      26                 :            : 
      27                 :          0 : static int dns_stream_update_io(DnsStream *s) {
      28                 :          0 :         int f = 0;
      29                 :            : 
      30         [ #  # ]:          0 :         assert(s);
      31                 :            : 
      32   [ #  #  #  # ]:          0 :         if (s->write_packet && s->n_written < sizeof(s->write_size) + s->write_packet->size)
      33                 :          0 :                 f |= EPOLLOUT;
      34         [ #  # ]:          0 :         else if (!ordered_set_isempty(s->write_queue)) {
      35                 :          0 :                 dns_packet_unref(s->write_packet);
      36                 :          0 :                 s->write_packet = ordered_set_steal_first(s->write_queue);
      37                 :          0 :                 s->write_size = htobe16(s->write_packet->size);
      38                 :          0 :                 s->n_written = 0;
      39                 :          0 :                 f |= EPOLLOUT;
      40                 :            :         }
      41                 :            : 
      42                 :            :         /* Let's read a packet if we haven't queued any yet. Except if we already hit a limit of parallel
      43                 :            :          * queries for this connection. */
      44   [ #  #  #  #  :          0 :         if ((!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size) &&
                   #  # ]
      45                 :          0 :                 set_size(s->queries) < DNS_QUERIES_PER_STREAM)
      46                 :          0 :                 f |= EPOLLIN;
      47                 :            : 
      48                 :            : #if ENABLE_DNS_OVER_TLS
      49                 :            :         /* For handshake and clean closing purposes, TLS can override requested events */
      50         [ #  # ]:          0 :         if (s->dnstls_events != 0)
      51                 :          0 :                 f = s->dnstls_events;
      52                 :            : #endif
      53                 :            : 
      54                 :          0 :         return sd_event_source_set_io_events(s->io_event_source, f);
      55                 :            : }
      56                 :            : 
      57                 :          0 : static int dns_stream_complete(DnsStream *s, int error) {
      58                 :          0 :         _cleanup_(dns_stream_unrefp) _unused_ DnsStream *ref = dns_stream_ref(s); /* Protect stream while we process it */
      59                 :            : 
      60         [ #  # ]:          0 :         assert(s);
      61         [ #  # ]:          0 :         assert(error >= 0);
      62                 :            : 
      63                 :            :         /* Error is > 0 when the connection failed for some reason in the network stack. It's == 0 if we sent
      64                 :            :          * and received exactly one packet each (in the LLMNR client case). */
      65                 :            : 
      66                 :            : #if ENABLE_DNS_OVER_TLS
      67         [ #  # ]:          0 :         if (s->encrypted) {
      68                 :            :                 int r;
      69                 :            : 
      70                 :          0 :                 r = dnstls_stream_shutdown(s, error);
      71         [ #  # ]:          0 :                 if (r != -EAGAIN)
      72                 :          0 :                         dns_stream_stop(s);
      73                 :            :         } else
      74                 :            : #endif
      75                 :          0 :                 dns_stream_stop(s);
      76                 :            : 
      77                 :          0 :         dns_stream_detach(s);
      78                 :            : 
      79         [ #  # ]:          0 :         if (s->complete)
      80                 :          0 :                 s->complete(s, error);
      81                 :            :         else /* the default action if no completion function is set is to close the stream */
      82                 :          0 :                 dns_stream_unref(s);
      83                 :            : 
      84                 :          0 :         return 0;
      85                 :            : }
      86                 :            : 
      87                 :          0 : static int dns_stream_identify(DnsStream *s) {
      88                 :            :         union {
      89                 :            :                 struct cmsghdr header; /* For alignment */
      90                 :            :                 uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
      91                 :            :                                + EXTRA_CMSG_SPACE /* kernel appears to require extra space */];
      92                 :            :         } control;
      93                 :          0 :         struct msghdr mh = {};
      94                 :            :         struct cmsghdr *cmsg;
      95                 :            :         socklen_t sl;
      96                 :            :         int r;
      97                 :            : 
      98         [ #  # ]:          0 :         assert(s);
      99                 :            : 
     100         [ #  # ]:          0 :         if (s->identified)
     101                 :          0 :                 return 0;
     102                 :            : 
     103                 :            :         /* Query the local side */
     104                 :          0 :         s->local_salen = sizeof(s->local);
     105                 :          0 :         r = getsockname(s->fd, &s->local.sa, &s->local_salen);
     106         [ #  # ]:          0 :         if (r < 0)
     107                 :          0 :                 return -errno;
     108   [ #  #  #  # ]:          0 :         if (s->local.sa.sa_family == AF_INET6 && s->ifindex <= 0)
     109                 :          0 :                 s->ifindex = s->local.in6.sin6_scope_id;
     110                 :            : 
     111                 :            :         /* Query the remote side */
     112                 :          0 :         s->peer_salen = sizeof(s->peer);
     113                 :          0 :         r = getpeername(s->fd, &s->peer.sa, &s->peer_salen);
     114         [ #  # ]:          0 :         if (r < 0)
     115                 :          0 :                 return -errno;
     116   [ #  #  #  # ]:          0 :         if (s->peer.sa.sa_family == AF_INET6 && s->ifindex <= 0)
     117                 :          0 :                 s->ifindex = s->peer.in6.sin6_scope_id;
     118                 :            : 
     119                 :            :         /* Check consistency */
     120         [ #  # ]:          0 :         assert(s->peer.sa.sa_family == s->local.sa.sa_family);
     121   [ #  #  #  # ]:          0 :         assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6));
     122                 :            : 
     123                 :            :         /* Query connection meta information */
     124                 :          0 :         sl = sizeof(control);
     125         [ #  # ]:          0 :         if (s->peer.sa.sa_family == AF_INET) {
     126                 :          0 :                 r = getsockopt(s->fd, IPPROTO_IP, IP_PKTOPTIONS, &control, &sl);
     127         [ #  # ]:          0 :                 if (r < 0)
     128                 :          0 :                         return -errno;
     129         [ #  # ]:          0 :         } else if (s->peer.sa.sa_family == AF_INET6) {
     130                 :            : 
     131                 :          0 :                 r = getsockopt(s->fd, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, &control, &sl);
     132         [ #  # ]:          0 :                 if (r < 0)
     133                 :          0 :                         return -errno;
     134                 :            :         } else
     135                 :          0 :                 return -EAFNOSUPPORT;
     136                 :            : 
     137                 :          0 :         mh.msg_control = &control;
     138                 :          0 :         mh.msg_controllen = sl;
     139                 :            : 
     140   [ #  #  #  # ]:          0 :         CMSG_FOREACH(cmsg, &mh) {
     141                 :            : 
     142         [ #  # ]:          0 :                 if (cmsg->cmsg_level == IPPROTO_IPV6) {
     143         [ #  # ]:          0 :                         assert(s->peer.sa.sa_family == AF_INET6);
     144                 :            : 
     145      [ #  #  # ]:          0 :                         switch (cmsg->cmsg_type) {
     146                 :            : 
     147                 :          0 :                         case IPV6_PKTINFO: {
     148                 :          0 :                                 struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
     149                 :            : 
     150         [ #  # ]:          0 :                                 if (s->ifindex <= 0)
     151                 :          0 :                                         s->ifindex = i->ipi6_ifindex;
     152                 :          0 :                                 break;
     153                 :            :                         }
     154                 :            : 
     155                 :          0 :                         case IPV6_HOPLIMIT:
     156                 :          0 :                                 s->ttl = *(int *) CMSG_DATA(cmsg);
     157                 :          0 :                                 break;
     158                 :            :                         }
     159                 :            : 
     160         [ #  # ]:          0 :                 } else if (cmsg->cmsg_level == IPPROTO_IP) {
     161         [ #  # ]:          0 :                         assert(s->peer.sa.sa_family == AF_INET);
     162                 :            : 
     163      [ #  #  # ]:          0 :                         switch (cmsg->cmsg_type) {
     164                 :            : 
     165                 :          0 :                         case IP_PKTINFO: {
     166                 :          0 :                                 struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
     167                 :            : 
     168         [ #  # ]:          0 :                                 if (s->ifindex <= 0)
     169                 :          0 :                                         s->ifindex = i->ipi_ifindex;
     170                 :          0 :                                 break;
     171                 :            :                         }
     172                 :            : 
     173                 :          0 :                         case IP_TTL:
     174                 :          0 :                                 s->ttl = *(int *) CMSG_DATA(cmsg);
     175                 :          0 :                                 break;
     176                 :            :                         }
     177                 :          0 :                 }
     178                 :            :         }
     179                 :            : 
     180                 :            :         /* The Linux kernel sets the interface index to the loopback
     181                 :            :          * device if the connection came from the local host since it
     182                 :            :          * avoids the routing table in such a case. Let's unset the
     183                 :            :          * interface index in such a case. */
     184         [ #  # ]:          0 :         if (s->ifindex == LOOPBACK_IFINDEX)
     185                 :          0 :                 s->ifindex = 0;
     186                 :            : 
     187                 :            :         /* If we don't know the interface index still, we look for the
     188                 :            :          * first local interface with a matching address. Yuck! */
     189         [ #  # ]:          0 :         if (s->ifindex <= 0)
     190         [ #  # ]:          0 :                 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);
     191                 :            : 
     192   [ #  #  #  # ]:          0 :         if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) {
     193                 :          0 :                 uint32_t ifindex = htobe32(s->ifindex);
     194                 :            : 
     195                 :            :                 /* Make sure all packets for this connection are sent on the same interface */
     196         [ #  # ]:          0 :                 if (s->local.sa.sa_family == AF_INET) {
     197                 :          0 :                         r = setsockopt(s->fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
     198         [ #  # ]:          0 :                         if (r < 0)
     199         [ #  # ]:          0 :                                 log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF: %m");
     200         [ #  # ]:          0 :                 } else if (s->local.sa.sa_family == AF_INET6) {
     201                 :          0 :                         r = setsockopt(s->fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
     202         [ #  # ]:          0 :                         if (r < 0)
     203         [ #  # ]:          0 :                                 log_debug_errno(errno, "Failed to invoke IPV6_UNICAST_IF: %m");
     204                 :            :                 }
     205                 :            :         }
     206                 :            : 
     207                 :          0 :         s->identified = true;
     208                 :            : 
     209                 :          0 :         return 0;
     210                 :            : }
     211                 :            : 
     212                 :          0 : ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, int flags) {
     213                 :            :         ssize_t m;
     214                 :            : 
     215         [ #  # ]:          0 :         assert(s);
     216         [ #  # ]:          0 :         assert(iov);
     217                 :            : 
     218                 :            : #if ENABLE_DNS_OVER_TLS
     219   [ #  #  #  # ]:          0 :         if (s->encrypted && !(flags & DNS_STREAM_WRITE_TLS_DATA)) {
     220                 :            :                 ssize_t ss;
     221                 :            :                 size_t i;
     222                 :            : 
     223                 :          0 :                 m = 0;
     224         [ #  # ]:          0 :                 for (i = 0; i < iovcnt; i++) {
     225                 :          0 :                         ss = dnstls_stream_write(s, iov[i].iov_base, iov[i].iov_len);
     226         [ #  # ]:          0 :                         if (ss < 0)
     227                 :          0 :                                 return ss;
     228                 :            : 
     229                 :          0 :                         m += ss;
     230         [ #  # ]:          0 :                         if (ss != (ssize_t) iov[i].iov_len)
     231                 :          0 :                                 continue;
     232                 :            :                 }
     233                 :            :         } else
     234                 :            : #endif
     235         [ #  # ]:          0 :         if (s->tfo_salen > 0) {
     236                 :          0 :                 struct msghdr hdr = {
     237                 :            :                         .msg_iov = (struct iovec*) iov,
     238                 :            :                         .msg_iovlen = iovcnt,
     239                 :          0 :                         .msg_name = &s->tfo_address.sa,
     240                 :          0 :                         .msg_namelen = s->tfo_salen
     241                 :            :                 };
     242                 :            : 
     243                 :          0 :                 m = sendmsg(s->fd, &hdr, MSG_FASTOPEN);
     244         [ #  # ]:          0 :                 if (m < 0) {
     245         [ #  # ]:          0 :                         if (errno == EOPNOTSUPP) {
     246                 :          0 :                                 s->tfo_salen = 0;
     247         [ #  # ]:          0 :                                 if (connect(s->fd, &s->tfo_address.sa, s->tfo_salen) < 0)
     248                 :          0 :                                         return -errno;
     249                 :            : 
     250                 :          0 :                                 return -EAGAIN;
     251                 :            :                         }
     252         [ #  # ]:          0 :                         if (errno == EINPROGRESS)
     253                 :          0 :                                 return -EAGAIN;
     254                 :            : 
     255                 :          0 :                         return -errno;
     256                 :            :                 } else
     257                 :          0 :                         s->tfo_salen = 0; /* connection is made */
     258                 :            :         } else {
     259                 :          0 :                 m = writev(s->fd, iov, iovcnt);
     260         [ #  # ]:          0 :                 if (m < 0)
     261                 :          0 :                         return -errno;
     262                 :            :         }
     263                 :            : 
     264                 :          0 :         return m;
     265                 :            : }
     266                 :            : 
     267                 :          0 : static ssize_t dns_stream_read(DnsStream *s, void *buf, size_t count) {
     268                 :            :         ssize_t ss;
     269                 :            : 
     270                 :            : #if ENABLE_DNS_OVER_TLS
     271         [ #  # ]:          0 :         if (s->encrypted)
     272                 :          0 :                 ss = dnstls_stream_read(s, buf, count);
     273                 :            :         else
     274                 :            : #endif
     275                 :            :         {
     276                 :          0 :                 ss = read(s->fd, buf, count);
     277         [ #  # ]:          0 :                 if (ss < 0)
     278                 :          0 :                         return -errno;
     279                 :            :         }
     280                 :            : 
     281                 :          0 :         return ss;
     282                 :            : }
     283                 :            : 
     284                 :          0 : static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
     285                 :          0 :         DnsStream *s = userdata;
     286                 :            : 
     287         [ #  # ]:          0 :         assert(s);
     288                 :            : 
     289                 :          0 :         return dns_stream_complete(s, ETIMEDOUT);
     290                 :            : }
     291                 :            : 
     292                 :          0 : static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
     293                 :          0 :         _cleanup_(dns_stream_unrefp) DnsStream *s = dns_stream_ref(userdata); /* Protect stream while we process it */
     294                 :          0 :         bool progressed = false;
     295                 :            :         int r;
     296                 :            : 
     297         [ #  # ]:          0 :         assert(s);
     298                 :            : 
     299                 :            : #if ENABLE_DNS_OVER_TLS
     300         [ #  # ]:          0 :         if (s->encrypted) {
     301                 :          0 :                 r = dnstls_stream_on_io(s, revents);
     302         [ #  # ]:          0 :                 if (r == DNSTLS_STREAM_CLOSED)
     303                 :          0 :                         return 0;
     304         [ #  # ]:          0 :                 if (r == -EAGAIN)
     305                 :          0 :                         return dns_stream_update_io(s);
     306         [ #  # ]:          0 :                 if (r < 0)
     307                 :          0 :                         return dns_stream_complete(s, -r);
     308                 :            : 
     309                 :          0 :                 r = dns_stream_update_io(s);
     310         [ #  # ]:          0 :                 if (r < 0)
     311                 :          0 :                         return r;
     312                 :            :         }
     313                 :            : #endif
     314                 :            : 
     315                 :            :         /* only identify after connecting */
     316         [ #  # ]:          0 :         if (s->tfo_salen == 0) {
     317                 :          0 :                 r = dns_stream_identify(s);
     318         [ #  # ]:          0 :                 if (r < 0)
     319                 :          0 :                         return dns_stream_complete(s, -r);
     320                 :            :         }
     321                 :            : 
     322         [ #  # ]:          0 :         if ((revents & EPOLLOUT) &&
     323         [ #  # ]:          0 :             s->write_packet &&
     324         [ #  # ]:          0 :             s->n_written < sizeof(s->write_size) + s->write_packet->size) {
     325                 :            : 
     326                 :            :                 struct iovec iov[2];
     327                 :            :                 ssize_t ss;
     328                 :            : 
     329                 :          0 :                 iov[0] = IOVEC_MAKE(&s->write_size, sizeof(s->write_size));
     330                 :          0 :                 iov[1] = IOVEC_MAKE(DNS_PACKET_DATA(s->write_packet), s->write_packet->size);
     331                 :            : 
     332                 :          0 :                 IOVEC_INCREMENT(iov, 2, s->n_written);
     333                 :            : 
     334                 :          0 :                 ss = dns_stream_writev(s, iov, 2, 0);
     335         [ #  # ]:          0 :                 if (ss < 0) {
     336   [ #  #  #  # ]:          0 :                         if (!IN_SET(-ss, EINTR, EAGAIN))
     337                 :          0 :                                 return dns_stream_complete(s, -ss);
     338                 :            :                 } else {
     339                 :          0 :                         progressed = true;
     340                 :          0 :                         s->n_written += ss;
     341                 :            :                 }
     342                 :            : 
     343                 :            :                 /* Are we done? If so, disable the event source for EPOLLOUT */
     344         [ #  # ]:          0 :                 if (s->n_written >= sizeof(s->write_size) + s->write_packet->size) {
     345                 :          0 :                         r = dns_stream_update_io(s);
     346         [ #  # ]:          0 :                         if (r < 0)
     347                 :          0 :                                 return dns_stream_complete(s, -r);
     348                 :            :                 }
     349                 :            :         }
     350                 :            : 
     351         [ #  # ]:          0 :         if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) &&
     352         [ #  # ]:          0 :             (!s->read_packet ||
     353         [ #  # ]:          0 :              s->n_read < sizeof(s->read_size) + s->read_packet->size)) {
     354                 :            : 
     355         [ #  # ]:          0 :                 if (s->n_read < sizeof(s->read_size)) {
     356                 :            :                         ssize_t ss;
     357                 :            : 
     358                 :          0 :                         ss = dns_stream_read(s, (uint8_t*) &s->read_size + s->n_read, sizeof(s->read_size) - s->n_read);
     359         [ #  # ]:          0 :                         if (ss < 0) {
     360   [ #  #  #  # ]:          0 :                                 if (!IN_SET(-ss, EINTR, EAGAIN))
     361                 :          0 :                                         return dns_stream_complete(s, -ss);
     362         [ #  # ]:          0 :                         } else if (ss == 0)
     363                 :          0 :                                 return dns_stream_complete(s, ECONNRESET);
     364                 :            :                         else {
     365                 :          0 :                                 progressed = true;
     366                 :          0 :                                 s->n_read += ss;
     367                 :            :                         }
     368                 :            :                 }
     369                 :            : 
     370         [ #  # ]:          0 :                 if (s->n_read >= sizeof(s->read_size)) {
     371                 :            : 
     372         [ #  # ]:          0 :                         if (be16toh(s->read_size) < DNS_PACKET_HEADER_SIZE)
     373                 :          0 :                                 return dns_stream_complete(s, EBADMSG);
     374                 :            : 
     375         [ #  # ]:          0 :                         if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) {
     376                 :            :                                 ssize_t ss;
     377                 :            : 
     378         [ #  # ]:          0 :                                 if (!s->read_packet) {
     379                 :          0 :                                         r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size), DNS_PACKET_SIZE_MAX);
     380         [ #  # ]:          0 :                                         if (r < 0)
     381                 :          0 :                                                 return dns_stream_complete(s, -r);
     382                 :            : 
     383                 :          0 :                                         s->read_packet->size = be16toh(s->read_size);
     384                 :          0 :                                         s->read_packet->ipproto = IPPROTO_TCP;
     385                 :          0 :                                         s->read_packet->family = s->peer.sa.sa_family;
     386                 :          0 :                                         s->read_packet->ttl = s->ttl;
     387                 :          0 :                                         s->read_packet->ifindex = s->ifindex;
     388                 :            : 
     389         [ #  # ]:          0 :                                         if (s->read_packet->family == AF_INET) {
     390                 :          0 :                                                 s->read_packet->sender.in = s->peer.in.sin_addr;
     391                 :          0 :                                                 s->read_packet->sender_port = be16toh(s->peer.in.sin_port);
     392                 :          0 :                                                 s->read_packet->destination.in = s->local.in.sin_addr;
     393                 :          0 :                                                 s->read_packet->destination_port = be16toh(s->local.in.sin_port);
     394                 :            :                                         } else {
     395         [ #  # ]:          0 :                                                 assert(s->read_packet->family == AF_INET6);
     396                 :          0 :                                                 s->read_packet->sender.in6 = s->peer.in6.sin6_addr;
     397                 :          0 :                                                 s->read_packet->sender_port = be16toh(s->peer.in6.sin6_port);
     398                 :          0 :                                                 s->read_packet->destination.in6 = s->local.in6.sin6_addr;
     399                 :          0 :                                                 s->read_packet->destination_port = be16toh(s->local.in6.sin6_port);
     400                 :            : 
     401         [ #  # ]:          0 :                                                 if (s->read_packet->ifindex == 0)
     402                 :          0 :                                                         s->read_packet->ifindex = s->peer.in6.sin6_scope_id;
     403         [ #  # ]:          0 :                                                 if (s->read_packet->ifindex == 0)
     404                 :          0 :                                                         s->read_packet->ifindex = s->local.in6.sin6_scope_id;
     405                 :            :                                         }
     406                 :            :                                 }
     407                 :            : 
     408                 :          0 :                                 ss = dns_stream_read(s,
     409                 :          0 :                                           (uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size),
     410                 :          0 :                                           sizeof(s->read_size) + be16toh(s->read_size) - s->n_read);
     411         [ #  # ]:          0 :                                 if (ss < 0) {
     412   [ #  #  #  # ]:          0 :                                         if (!IN_SET(-ss, EINTR, EAGAIN))
     413                 :          0 :                                                 return dns_stream_complete(s, -ss);
     414         [ #  # ]:          0 :                                 } else if (ss == 0)
     415                 :          0 :                                         return dns_stream_complete(s, ECONNRESET);
     416                 :            :                                 else
     417                 :          0 :                                         s->n_read += ss;
     418                 :            :                         }
     419                 :            : 
     420                 :            :                         /* Are we done? If so, disable the event source for EPOLLIN */
     421         [ #  # ]:          0 :                         if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) {
     422                 :            :                                 /* If there's a packet handler
     423                 :            :                                  * installed, call that. Note that
     424                 :            :                                  * this is optional... */
     425         [ #  # ]:          0 :                                 if (s->on_packet) {
     426                 :          0 :                                         r = s->on_packet(s);
     427         [ #  # ]:          0 :                                         if (r < 0)
     428                 :          0 :                                                 return r;
     429                 :            :                                 }
     430                 :            : 
     431                 :          0 :                                 r = dns_stream_update_io(s);
     432         [ #  # ]:          0 :                                 if (r < 0)
     433                 :          0 :                                         return dns_stream_complete(s, -r);
     434                 :            :                         }
     435                 :            :                 }
     436                 :            :         }
     437                 :            : 
     438                 :            :         /* Call "complete" callback if finished reading and writing one packet, and there's nothing else left
     439                 :            :          * to write. */
     440         [ #  # ]:          0 :         if (s->type == DNS_STREAM_LLMNR_SEND &&
     441   [ #  #  #  #  :          0 :             (s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) &&
                   #  # ]
     442                 :          0 :             ordered_set_isempty(s->write_queue) &&
     443   [ #  #  #  # ]:          0 :             (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size))
     444                 :          0 :                 return dns_stream_complete(s, 0);
     445                 :            : 
     446                 :            :         /* If we did something, let's restart the timeout event source */
     447   [ #  #  #  # ]:          0 :         if (progressed && s->timeout_event_source) {
     448                 :          0 :                 r = sd_event_source_set_time(s->timeout_event_source, now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC);
     449         [ #  # ]:          0 :                 if (r < 0)
     450         [ #  # ]:          0 :                         log_warning_errno(errno, "Couldn't restart TCP connection timeout, ignoring: %m");
     451                 :            :         }
     452                 :            : 
     453                 :          0 :         return 0;
     454                 :            : }
     455                 :            : 
     456                 :          0 : static DnsStream *dns_stream_free(DnsStream *s) {
     457                 :            :         DnsPacket *p;
     458                 :            :         Iterator i;
     459                 :            : 
     460         [ #  # ]:          0 :         assert(s);
     461                 :            : 
     462                 :          0 :         dns_stream_stop(s);
     463                 :            : 
     464         [ #  # ]:          0 :         if (s->manager) {
     465   [ #  #  #  #  :          0 :                 LIST_REMOVE(streams, s->manager->dns_streams, s);
             #  #  #  # ]
     466                 :          0 :                 s->manager->n_dns_streams[s->type]--;
     467                 :            :         }
     468                 :            : 
     469                 :            : #if ENABLE_DNS_OVER_TLS
     470         [ #  # ]:          0 :         if (s->encrypted)
     471                 :          0 :                 dnstls_stream_free(s);
     472                 :            : #endif
     473                 :            : 
     474         [ #  # ]:          0 :         ORDERED_SET_FOREACH(p, s->write_queue, i)
     475                 :          0 :                 dns_packet_unref(ordered_set_remove(s->write_queue, p));
     476                 :            : 
     477                 :          0 :         dns_packet_unref(s->write_packet);
     478                 :          0 :         dns_packet_unref(s->read_packet);
     479                 :          0 :         dns_server_unref(s->server);
     480                 :            : 
     481                 :          0 :         ordered_set_free(s->write_queue);
     482                 :            : 
     483                 :          0 :         return mfree(s);
     484                 :            : }
     485                 :            : 
     486   [ #  #  #  #  :          0 : DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsStream, dns_stream, dns_stream_free);
                   #  # ]
     487                 :            : 
     488                 :          0 : int dns_stream_new(
     489                 :            :                 Manager *m,
     490                 :            :                 DnsStream **ret,
     491                 :            :                 DnsStreamType type,
     492                 :            :                 DnsProtocol protocol,
     493                 :            :                 int fd,
     494                 :            :                 const union sockaddr_union *tfo_address) {
     495                 :            : 
     496                 :          0 :         _cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
     497                 :            :         int r;
     498                 :            : 
     499         [ #  # ]:          0 :         assert(m);
     500         [ #  # ]:          0 :         assert(ret);
     501         [ #  # ]:          0 :         assert(type >= 0);
     502         [ #  # ]:          0 :         assert(type < _DNS_STREAM_TYPE_MAX);
     503         [ #  # ]:          0 :         assert(protocol >= 0);
     504         [ #  # ]:          0 :         assert(protocol < _DNS_PROTOCOL_MAX);
     505         [ #  # ]:          0 :         assert(fd >= 0);
     506                 :            : 
     507         [ #  # ]:          0 :         if (m->n_dns_streams[type] > DNS_STREAMS_MAX)
     508                 :          0 :                 return -EBUSY;
     509                 :            : 
     510                 :          0 :         s = new(DnsStream, 1);
     511         [ #  # ]:          0 :         if (!s)
     512                 :          0 :                 return -ENOMEM;
     513                 :            : 
     514                 :          0 :         *s = (DnsStream) {
     515                 :            :                 .n_ref = 1,
     516                 :            :                 .fd = -1,
     517                 :            :                 .protocol = protocol,
     518                 :            :         };
     519                 :            : 
     520                 :          0 :         r = ordered_set_ensure_allocated(&s->write_queue, &dns_packet_hash_ops);
     521         [ #  # ]:          0 :         if (r < 0)
     522                 :          0 :                 return r;
     523                 :            : 
     524                 :          0 :         r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s);
     525         [ #  # ]:          0 :         if (r < 0)
     526                 :          0 :                 return r;
     527                 :            : 
     528                 :          0 :         (void) sd_event_source_set_description(s->io_event_source, "dns-stream-io");
     529                 :            : 
     530                 :          0 :         r = sd_event_add_time(
     531                 :            :                         m->event,
     532                 :          0 :                         &s->timeout_event_source,
     533                 :            :                         clock_boottime_or_monotonic(),
     534                 :          0 :                         now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC, 0,
     535                 :            :                         on_stream_timeout, s);
     536         [ #  # ]:          0 :         if (r < 0)
     537                 :          0 :                 return r;
     538                 :            : 
     539                 :          0 :         (void) sd_event_source_set_description(s->timeout_event_source, "dns-stream-timeout");
     540                 :            : 
     541   [ #  #  #  # ]:          0 :         LIST_PREPEND(streams, m->dns_streams, s);
     542                 :          0 :         m->n_dns_streams[type]++;
     543                 :          0 :         s->manager = m;
     544                 :            : 
     545                 :          0 :         s->fd = fd;
     546                 :            : 
     547         [ #  # ]:          0 :         if (tfo_address) {
     548                 :          0 :                 s->tfo_address = *tfo_address;
     549         [ #  # ]:          0 :                 s->tfo_salen = tfo_address->sa.sa_family == AF_INET6 ? sizeof(tfo_address->in6) : sizeof(tfo_address->in);
     550                 :            :         }
     551                 :            : 
     552                 :          0 :         *ret = TAKE_PTR(s);
     553                 :            : 
     554                 :          0 :         return 0;
     555                 :            : }
     556                 :            : 
     557                 :          0 : int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
     558                 :            :         int r;
     559                 :            : 
     560         [ #  # ]:          0 :         assert(s);
     561         [ #  # ]:          0 :         assert(p);
     562                 :            : 
     563                 :          0 :         r = ordered_set_put(s->write_queue, p);
     564         [ #  # ]:          0 :         if (r < 0)
     565                 :          0 :                 return r;
     566                 :            : 
     567                 :          0 :         dns_packet_ref(p);
     568                 :            : 
     569                 :          0 :         return dns_stream_update_io(s);
     570                 :            : }
     571                 :            : 
     572                 :          0 : DnsPacket *dns_stream_take_read_packet(DnsStream *s) {
     573         [ #  # ]:          0 :         assert(s);
     574                 :            : 
     575         [ #  # ]:          0 :         if (!s->read_packet)
     576                 :          0 :                 return NULL;
     577                 :            : 
     578         [ #  # ]:          0 :         if (s->n_read < sizeof(s->read_size))
     579                 :          0 :                 return NULL;
     580                 :            : 
     581         [ #  # ]:          0 :         if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size))
     582                 :          0 :                 return NULL;
     583                 :            : 
     584                 :          0 :         s->n_read = 0;
     585                 :          0 :         return TAKE_PTR(s->read_packet);
     586                 :            : }
     587                 :            : 
     588                 :          0 : void dns_stream_detach(DnsStream *s) {
     589         [ #  # ]:          0 :         assert(s);
     590                 :            : 
     591         [ #  # ]:          0 :         if (!s->server)
     592                 :          0 :                 return;
     593                 :            : 
     594         [ #  # ]:          0 :         if (s->server->stream != s)
     595                 :          0 :                 return;
     596                 :            : 
     597                 :          0 :         dns_server_unref_stream(s->server);
     598                 :            : }

Generated by: LCOV version 1.14