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

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <resolv.h>
       4                 :            : #include <netinet/in.h>
       5                 :            : #include <arpa/inet.h>
       6                 :            : 
       7                 :            : #include "alloc-util.h"
       8                 :            : #include "fd-util.h"
       9                 :            : #include "resolved-manager.h"
      10                 :            : #include "resolved-mdns.h"
      11                 :            : #include "sort-util.h"
      12                 :            : 
      13                 :            : #define CLEAR_CACHE_FLUSH(x) (~MDNS_RR_CACHE_FLUSH & (x))
      14                 :            : 
      15                 :          0 : void manager_mdns_stop(Manager *m) {
      16         [ #  # ]:          0 :         assert(m);
      17                 :            : 
      18                 :          0 :         m->mdns_ipv4_event_source = sd_event_source_unref(m->mdns_ipv4_event_source);
      19                 :          0 :         m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
      20                 :            : 
      21                 :          0 :         m->mdns_ipv6_event_source = sd_event_source_unref(m->mdns_ipv6_event_source);
      22                 :          0 :         m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
      23                 :          0 : }
      24                 :            : 
      25                 :          0 : int manager_mdns_start(Manager *m) {
      26                 :            :         int r;
      27                 :            : 
      28         [ #  # ]:          0 :         assert(m);
      29                 :            : 
      30         [ #  # ]:          0 :         if (m->mdns_support == RESOLVE_SUPPORT_NO)
      31                 :          0 :                 return 0;
      32                 :            : 
      33                 :          0 :         r = manager_mdns_ipv4_fd(m);
      34         [ #  # ]:          0 :         if (r == -EADDRINUSE)
      35                 :          0 :                 goto eaddrinuse;
      36         [ #  # ]:          0 :         if (r < 0)
      37                 :          0 :                 return r;
      38                 :            : 
      39         [ #  # ]:          0 :         if (socket_ipv6_is_supported()) {
      40                 :          0 :                 r = manager_mdns_ipv6_fd(m);
      41         [ #  # ]:          0 :                 if (r == -EADDRINUSE)
      42                 :          0 :                         goto eaddrinuse;
      43         [ #  # ]:          0 :                 if (r < 0)
      44                 :          0 :                         return r;
      45                 :            :         }
      46                 :            : 
      47                 :          0 :         return 0;
      48                 :            : 
      49                 :          0 : eaddrinuse:
      50         [ #  # ]:          0 :         log_warning("Another mDNS responder prohibits binding the socket to the same port. Turning off mDNS support.");
      51                 :          0 :         m->mdns_support = RESOLVE_SUPPORT_NO;
      52                 :          0 :         manager_mdns_stop(m);
      53                 :            : 
      54                 :          0 :         return 0;
      55                 :            : }
      56                 :            : 
      57                 :          0 : static int mdns_rr_compare(DnsResourceRecord * const *a, DnsResourceRecord * const *b) {
      58                 :          0 :         DnsResourceRecord *x = *(DnsResourceRecord **) a, *y = *(DnsResourceRecord **) b;
      59                 :            :         size_t m;
      60                 :            :         int r;
      61                 :            : 
      62         [ #  # ]:          0 :         assert(x);
      63         [ #  # ]:          0 :         assert(y);
      64                 :            : 
      65         [ #  # ]:          0 :         r = CMP(CLEAR_CACHE_FLUSH(x->key->class), CLEAR_CACHE_FLUSH(y->key->class));
      66         [ #  # ]:          0 :         if (r != 0)
      67                 :          0 :                 return r;
      68                 :            : 
      69         [ #  # ]:          0 :         r = CMP(x->key->type, y->key->type);
      70         [ #  # ]:          0 :         if (r != 0)
      71                 :          0 :                 return r;
      72                 :            : 
      73                 :          0 :         r = dns_resource_record_to_wire_format(x, false);
      74         [ #  # ]:          0 :         if (r < 0) {
      75         [ #  # ]:          0 :                 log_warning_errno(r, "Can't wire-format RR: %m");
      76                 :          0 :                 return 0;
      77                 :            :         }
      78                 :            : 
      79                 :          0 :         r = dns_resource_record_to_wire_format(y, false);
      80         [ #  # ]:          0 :         if (r < 0) {
      81         [ #  # ]:          0 :                 log_warning_errno(r, "Can't wire-format RR: %m");
      82                 :          0 :                 return 0;
      83                 :            :         }
      84                 :            : 
      85                 :          0 :         m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(x), DNS_RESOURCE_RECORD_RDATA_SIZE(y));
      86                 :            : 
      87                 :          0 :         r = memcmp(DNS_RESOURCE_RECORD_RDATA(x), DNS_RESOURCE_RECORD_RDATA(y), m);
      88         [ #  # ]:          0 :         if (r != 0)
      89                 :          0 :                 return r;
      90                 :            : 
      91         [ #  # ]:          0 :         return CMP(DNS_RESOURCE_RECORD_RDATA_SIZE(x), DNS_RESOURCE_RECORD_RDATA_SIZE(y));
      92                 :            : }
      93                 :            : 
      94                 :          0 : static int proposed_rrs_cmp(DnsResourceRecord **x, unsigned x_size, DnsResourceRecord **y, unsigned y_size) {
      95                 :            :         unsigned m;
      96                 :            :         int r;
      97                 :            : 
      98                 :          0 :         m = MIN(x_size, y_size);
      99         [ #  # ]:          0 :         for (unsigned i = 0; i < m; i++) {
     100                 :          0 :                 r = mdns_rr_compare(&x[i], &y[i]);
     101         [ #  # ]:          0 :                 if (r != 0)
     102                 :          0 :                         return r;
     103                 :            :         }
     104                 :            : 
     105         [ #  # ]:          0 :         return CMP(x_size, y_size);
     106                 :            : }
     107                 :            : 
     108                 :          0 : static int mdns_packet_extract_matching_rrs(DnsPacket *p, DnsResourceKey *key, DnsResourceRecord ***ret_rrs) {
     109                 :          0 :         _cleanup_free_ DnsResourceRecord **list = NULL;
     110                 :          0 :         unsigned n = 0, size = 0;
     111                 :            :         int r;
     112                 :            : 
     113         [ #  # ]:          0 :         assert(p);
     114         [ #  # ]:          0 :         assert(key);
     115         [ #  # ]:          0 :         assert(ret_rrs);
     116   [ #  #  #  # ]:          0 :         assert_return(DNS_PACKET_NSCOUNT(p) > 0, -EINVAL);
     117                 :            : 
     118         [ #  # ]:          0 :         for (size_t i = DNS_PACKET_ANCOUNT(p); i < (DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)); i++) {
     119                 :          0 :                 r = dns_resource_key_match_rr(key, p->answer->items[i].rr, NULL);
     120         [ #  # ]:          0 :                 if (r < 0)
     121                 :          0 :                         return r;
     122         [ #  # ]:          0 :                 if (r > 0)
     123                 :          0 :                         size++;
     124                 :            :         }
     125                 :            : 
     126         [ #  # ]:          0 :         if (size == 0)
     127                 :          0 :                 return 0;
     128                 :            : 
     129                 :          0 :         list = new(DnsResourceRecord *, size);
     130         [ #  # ]:          0 :         if (!list)
     131                 :          0 :                 return -ENOMEM;
     132                 :            : 
     133         [ #  # ]:          0 :         for (size_t i = DNS_PACKET_ANCOUNT(p); i < (DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)); i++) {
     134                 :          0 :                 r = dns_resource_key_match_rr(key, p->answer->items[i].rr, NULL);
     135         [ #  # ]:          0 :                 if (r < 0)
     136                 :          0 :                         return r;
     137         [ #  # ]:          0 :                 if (r > 0)
     138                 :          0 :                         list[n++] = p->answer->items[i].rr;
     139                 :            :         }
     140         [ #  # ]:          0 :         assert(n == size);
     141                 :          0 :         typesafe_qsort(list, size, mdns_rr_compare);
     142                 :            : 
     143                 :          0 :         *ret_rrs = TAKE_PTR(list);
     144                 :            : 
     145                 :          0 :         return size;
     146                 :            : }
     147                 :            : 
     148                 :          0 : static int mdns_do_tiebreak(DnsResourceKey *key, DnsAnswer *answer, DnsPacket *p) {
     149                 :          0 :         _cleanup_free_ DnsResourceRecord **our = NULL, **remote = NULL;
     150                 :            :         DnsResourceRecord *rr;
     151                 :          0 :         size_t i = 0, size;
     152                 :            :         int r;
     153                 :            : 
     154                 :          0 :         size = dns_answer_size(answer);
     155                 :          0 :         our = new(DnsResourceRecord *, size);
     156         [ #  # ]:          0 :         if (!our)
     157                 :          0 :                 return -ENOMEM;
     158                 :            : 
     159   [ #  #  #  #  :          0 :         DNS_ANSWER_FOREACH(rr, answer)
             #  #  #  # ]
     160         [ #  # ]:          0 :                 our[i++] = rr;
     161                 :            : 
     162                 :          0 :         typesafe_qsort(our, size, mdns_rr_compare);
     163                 :            : 
     164                 :          0 :         r = mdns_packet_extract_matching_rrs(p, key, &remote);
     165         [ #  # ]:          0 :         if (r < 0)
     166                 :          0 :                 return r;
     167                 :            : 
     168         [ #  # ]:          0 :         assert(r > 0);
     169                 :            : 
     170         [ #  # ]:          0 :         if (proposed_rrs_cmp(remote, r, our, size) > 0)
     171                 :          0 :                 return 1;
     172                 :            : 
     173                 :          0 :         return 0;
     174                 :            : }
     175                 :            : 
     176                 :          0 : static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
     177                 :          0 :         _cleanup_(dns_answer_unrefp) DnsAnswer *full_answer = NULL;
     178                 :          0 :         _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
     179                 :          0 :         DnsResourceKey *key = NULL;
     180                 :            :         DnsResourceRecord *rr;
     181                 :          0 :         bool tentative = false;
     182                 :            :         int r;
     183                 :            : 
     184         [ #  # ]:          0 :         assert(s);
     185         [ #  # ]:          0 :         assert(p);
     186                 :            : 
     187                 :          0 :         r = dns_packet_extract(p);
     188         [ #  # ]:          0 :         if (r < 0)
     189         [ #  # ]:          0 :                 return log_debug_errno(r, "Failed to extract resource records from incoming packet: %m");
     190                 :            : 
     191   [ #  #  #  # ]:          0 :         assert_return((dns_question_size(p->question) > 0), -EINVAL);
     192                 :            : 
     193   [ #  #  #  #  :          0 :         DNS_QUESTION_FOREACH(key, p->question) {
          #  #  #  #  #  
                      # ]
     194   [ #  #  #  #  :          0 :                 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
                   #  # ]
     195                 :            : 
     196                 :          0 :                 r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
     197         [ #  # ]:          0 :                 if (r < 0)
     198         [ #  # ]:          0 :                         return log_debug_errno(r, "Failed to lookup key: %m");
     199                 :            : 
     200   [ #  #  #  # ]:          0 :                 if (tentative && DNS_PACKET_NSCOUNT(p) > 0) {
     201                 :            :                         /*
     202                 :            :                          * A race condition detected with the probe packet from
     203                 :            :                          * a remote host.
     204                 :            :                          * Do simultaneous probe tiebreaking as described in
     205                 :            :                          * RFC 6762, Section 8.2. In case we lost don't reply
     206                 :            :                          * the question and withdraw conflicting RRs.
     207                 :            :                          */
     208                 :          0 :                         r = mdns_do_tiebreak(key, answer, p);
     209         [ #  # ]:          0 :                         if (r < 0)
     210         [ #  # ]:          0 :                                 return log_debug_errno(r, "Failed to do tiebreaking");
     211                 :            : 
     212         [ #  # ]:          0 :                         if (r > 0) { /* we lost */
     213   [ #  #  #  #  :          0 :                                 DNS_ANSWER_FOREACH(rr, answer) {
          #  #  #  #  #  
                      # ]
     214                 :            :                                         DnsZoneItem *i;
     215                 :            : 
     216                 :          0 :                                         i = dns_zone_get(&s->zone, rr);
     217         [ #  # ]:          0 :                                         if (i)
     218                 :          0 :                                                 dns_zone_item_conflict(i);
     219                 :            :                                 }
     220                 :            : 
     221                 :          0 :                                 continue;
     222                 :            :                         }
     223                 :            :                 }
     224                 :            : 
     225                 :          0 :                 r = dns_answer_extend(&full_answer, answer);
     226         [ #  # ]:          0 :                 if (r < 0)
     227         [ #  # ]:          0 :                         return log_debug_errno(r, "Failed to extend answer: %m");
     228                 :            :         }
     229                 :            : 
     230         [ #  # ]:          0 :         if (dns_answer_isempty(full_answer))
     231                 :          0 :                 return 0;
     232                 :            : 
     233                 :          0 :         r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, NULL, full_answer, NULL, false, &reply);
     234         [ #  # ]:          0 :         if (r < 0)
     235         [ #  # ]:          0 :                 return log_debug_errno(r, "Failed to build reply packet: %m");
     236                 :            : 
     237         [ #  # ]:          0 :         if (!ratelimit_below(&s->ratelimit))
     238                 :          0 :                 return 0;
     239                 :            : 
     240                 :          0 :         r = dns_scope_emit_udp(s, -1, reply);
     241         [ #  # ]:          0 :         if (r < 0)
     242         [ #  # ]:          0 :                 return log_debug_errno(r, "Failed to send reply packet: %m");
     243                 :            : 
     244                 :          0 :         return 0;
     245                 :            : }
     246                 :            : 
     247                 :          0 : static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
     248                 :          0 :         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
     249                 :          0 :         Manager *m = userdata;
     250                 :            :         DnsScope *scope;
     251                 :            :         int r;
     252                 :            : 
     253                 :          0 :         r = manager_recv(m, fd, DNS_PROTOCOL_MDNS, &p);
     254         [ #  # ]:          0 :         if (r <= 0)
     255                 :          0 :                 return r;
     256                 :            : 
     257         [ #  # ]:          0 :         if (manager_our_packet(m, p))
     258                 :          0 :                 return 0;
     259                 :            : 
     260                 :          0 :         scope = manager_find_scope(m, p);
     261         [ #  # ]:          0 :         if (!scope) {
     262         [ #  # ]:          0 :                 log_debug("Got mDNS UDP packet on unknown scope. Ignoring.");
     263                 :          0 :                 return 0;
     264                 :            :         }
     265                 :            : 
     266         [ #  # ]:          0 :         if (dns_packet_validate_reply(p) > 0) {
     267                 :            :                 DnsResourceRecord *rr;
     268                 :            : 
     269         [ #  # ]:          0 :                 log_debug("Got mDNS reply packet");
     270                 :            : 
     271                 :            :                 /*
     272                 :            :                  * mDNS is different from regular DNS and LLMNR with regard to handling responses.
     273                 :            :                  * While on other protocols, we can ignore every answer that doesn't match a question
     274                 :            :                  * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all
     275                 :            :                  * incoming information, regardless of the DNS packet ID.
     276                 :            :                  *
     277                 :            :                  * Hence, extract the packet here, and try to find a transaction for answer the we got
     278                 :            :                  * and complete it. Also store the new information in scope's cache.
     279                 :            :                  */
     280                 :          0 :                 r = dns_packet_extract(p);
     281         [ #  # ]:          0 :                 if (r < 0) {
     282         [ #  # ]:          0 :                         log_debug("mDNS packet extraction failed.");
     283                 :          0 :                         return 0;
     284                 :            :                 }
     285                 :            : 
     286                 :          0 :                 dns_scope_check_conflicts(scope, p);
     287                 :            : 
     288   [ #  #  #  #  :          0 :                 DNS_ANSWER_FOREACH(rr, p->answer) {
          #  #  #  #  #  
                      # ]
     289                 :          0 :                         const char *name = dns_resource_key_name(rr->key);
     290                 :            :                         DnsTransaction *t;
     291                 :            : 
     292                 :            :                         /* If the received reply packet contains ANY record that is not .local or .in-addr.arpa,
     293                 :            :                          * we assume someone's playing tricks on us and discard the packet completely. */
     294   [ #  #  #  # ]:          0 :                         if (!(dns_name_endswith(name, "in-addr.arpa") > 0 ||
     295                 :          0 :                               dns_name_endswith(name, "local") > 0))
     296                 :          0 :                                 return 0;
     297                 :            : 
     298         [ #  # ]:          0 :                         if (rr->ttl == 0) {
     299         [ #  # ]:          0 :                                 log_debug("Got a goodbye packet");
     300                 :            :                                 /* See the section 10.1 of RFC6762 */
     301                 :          0 :                                 rr->ttl = 1;
     302                 :            :                         }
     303                 :            : 
     304                 :          0 :                         t = dns_scope_find_transaction(scope, rr->key, false);
     305         [ #  # ]:          0 :                         if (t)
     306                 :          0 :                                 dns_transaction_process_reply(t, p);
     307                 :            : 
     308                 :            :                         /* Also look for the various types of ANY transactions */
     309                 :          0 :                         t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(rr->key)), false);
     310         [ #  # ]:          0 :                         if (t)
     311                 :          0 :                                 dns_transaction_process_reply(t, p);
     312                 :            : 
     313                 :          0 :                         t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY, rr->key->type, dns_resource_key_name(rr->key)), false);
     314         [ #  # ]:          0 :                         if (t)
     315                 :          0 :                                 dns_transaction_process_reply(t, p);
     316                 :            : 
     317                 :          0 :                         t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY, DNS_TYPE_ANY, dns_resource_key_name(rr->key)), false);
     318         [ #  # ]:          0 :                         if (t)
     319                 :          0 :                                 dns_transaction_process_reply(t, p);
     320                 :            :                 }
     321                 :            : 
     322                 :          0 :                 dns_cache_put(&scope->cache, scope->manager->enable_cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender);
     323                 :            : 
     324         [ #  # ]:          0 :         } else if (dns_packet_validate_query(p) > 0)  {
     325         [ #  # ]:          0 :                 log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
     326                 :            : 
     327                 :          0 :                 r = mdns_scope_process_query(scope, p);
     328         [ #  # ]:          0 :                 if (r < 0) {
     329         [ #  # ]:          0 :                         log_debug_errno(r, "mDNS query processing failed: %m");
     330                 :          0 :                         return 0;
     331                 :            :                 }
     332                 :            :         } else
     333         [ #  # ]:          0 :                 log_debug("Invalid mDNS UDP packet.");
     334                 :            : 
     335                 :          0 :         return 0;
     336                 :            : }
     337                 :            : 
     338                 :          0 : int manager_mdns_ipv4_fd(Manager *m) {
     339                 :          0 :         union sockaddr_union sa = {
     340                 :            :                 .in.sin_family = AF_INET,
     341                 :          0 :                 .in.sin_port = htobe16(MDNS_PORT),
     342                 :            :         };
     343                 :          0 :         _cleanup_close_ int s = -1;
     344                 :            :         int r;
     345                 :            : 
     346         [ #  # ]:          0 :         assert(m);
     347                 :            : 
     348         [ #  # ]:          0 :         if (m->mdns_ipv4_fd >= 0)
     349                 :          0 :                 return m->mdns_ipv4_fd;
     350                 :            : 
     351                 :          0 :         s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
     352         [ #  # ]:          0 :         if (s < 0)
     353         [ #  # ]:          0 :                 return log_error_errno(errno, "mDNS-IPv4: Failed to create socket: %m");
     354                 :            : 
     355                 :          0 :         r = setsockopt_int(s, IPPROTO_IP, IP_TTL, 255);
     356         [ #  # ]:          0 :         if (r < 0)
     357         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_TTL: %m");
     358                 :            : 
     359                 :          0 :         r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_TTL, 255);
     360         [ #  # ]:          0 :         if (r < 0)
     361         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_MULTICAST_TTL: %m");
     362                 :            : 
     363                 :          0 :         r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_LOOP, true);
     364         [ #  # ]:          0 :         if (r < 0)
     365         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_MULTICAST_LOOP: %m");
     366                 :            : 
     367                 :          0 :         r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
     368         [ #  # ]:          0 :         if (r < 0)
     369         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_PKTINFO: %m");
     370                 :            : 
     371                 :          0 :         r = setsockopt_int(s, IPPROTO_IP, IP_RECVTTL, true);
     372         [ #  # ]:          0 :         if (r < 0)
     373         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_RECVTTL: %m");
     374                 :            : 
     375                 :            :         /* Disable Don't-Fragment bit in the IP header */
     376                 :          0 :         r = setsockopt_int(s, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
     377         [ #  # ]:          0 :         if (r < 0)
     378         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv4: Failed to set IP_MTU_DISCOVER: %m");
     379                 :            : 
     380                 :            :         /* See the section 15.1 of RFC6762 */
     381                 :            :         /* first try to bind without SO_REUSEADDR to detect another mDNS responder */
     382                 :          0 :         r = bind(s, &sa.sa, sizeof(sa.in));
     383         [ #  # ]:          0 :         if (r < 0) {
     384         [ #  # ]:          0 :                 if (errno != EADDRINUSE)
     385         [ #  # ]:          0 :                         return log_error_errno(errno, "mDNS-IPv4: Failed to bind socket: %m");
     386                 :            : 
     387         [ #  # ]:          0 :                 log_warning("mDNS-IPv4: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers.");
     388                 :            : 
     389                 :            :                 /* try again with SO_REUSEADDR */
     390                 :          0 :                 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
     391         [ #  # ]:          0 :                 if (r < 0)
     392         [ #  # ]:          0 :                         return log_error_errno(r, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
     393                 :            : 
     394                 :          0 :                 r = bind(s, &sa.sa, sizeof(sa.in));
     395         [ #  # ]:          0 :                 if (r < 0)
     396         [ #  # ]:          0 :                         return log_error_errno(errno, "mDNS-IPv4: Failed to bind socket: %m");
     397                 :            :         } else {
     398                 :            :                 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
     399                 :          0 :                 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
     400         [ #  # ]:          0 :                 if (r < 0)
     401         [ #  # ]:          0 :                         return log_error_errno(r, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
     402                 :            :         }
     403                 :            : 
     404                 :          0 :         r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, s, EPOLLIN, on_mdns_packet, m);
     405         [ #  # ]:          0 :         if (r < 0)
     406         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv4: Failed to create event source: %m");
     407                 :            : 
     408                 :          0 :         return m->mdns_ipv4_fd = TAKE_FD(s);
     409                 :            : }
     410                 :            : 
     411                 :          0 : int manager_mdns_ipv6_fd(Manager *m) {
     412                 :          0 :         union sockaddr_union sa = {
     413                 :            :                 .in6.sin6_family = AF_INET6,
     414                 :          0 :                 .in6.sin6_port = htobe16(MDNS_PORT),
     415                 :            :         };
     416                 :          0 :         _cleanup_close_ int s = -1;
     417                 :            :         int r;
     418                 :            : 
     419         [ #  # ]:          0 :         assert(m);
     420                 :            : 
     421         [ #  # ]:          0 :         if (m->mdns_ipv6_fd >= 0)
     422                 :          0 :                 return m->mdns_ipv6_fd;
     423                 :            : 
     424                 :          0 :         s = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
     425         [ #  # ]:          0 :         if (s < 0)
     426         [ #  # ]:          0 :                 return log_error_errno(errno, "mDNS-IPv6: Failed to create socket: %m");
     427                 :            : 
     428                 :          0 :         r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255);
     429         [ #  # ]:          0 :         if (r < 0)
     430         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_UNICAST_HOPS: %m");
     431                 :            : 
     432                 :            :         /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
     433                 :          0 :         r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
     434         [ #  # ]:          0 :         if (r < 0)
     435         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_MULTICAST_HOPS: %m");
     436                 :            : 
     437                 :          0 :         r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, true);
     438         [ #  # ]:          0 :         if (r < 0)
     439         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_MULTICAST_LOOP: %m");
     440                 :            : 
     441                 :          0 :         r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
     442         [ #  # ]:          0 :         if (r < 0)
     443         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_V6ONLY: %m");
     444                 :            : 
     445                 :          0 :         r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
     446         [ #  # ]:          0 :         if (r < 0)
     447         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_RECVPKTINFO: %m");
     448                 :            : 
     449                 :          0 :         r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
     450         [ #  # ]:          0 :         if (r < 0)
     451         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_RECVHOPLIMIT: %m");
     452                 :            : 
     453                 :            :         /* See the section 15.1 of RFC6762 */
     454                 :            :         /* first try to bind without SO_REUSEADDR to detect another mDNS responder */
     455                 :          0 :         r = bind(s, &sa.sa, sizeof(sa.in6));
     456         [ #  # ]:          0 :         if (r < 0) {
     457         [ #  # ]:          0 :                 if (errno != EADDRINUSE)
     458         [ #  # ]:          0 :                         return log_error_errno(errno, "mDNS-IPv6: Failed to bind socket: %m");
     459                 :            : 
     460         [ #  # ]:          0 :                 log_warning("mDNS-IPv6: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers.");
     461                 :            : 
     462                 :            :                 /* try again with SO_REUSEADDR */
     463                 :          0 :                 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
     464         [ #  # ]:          0 :                 if (r < 0)
     465         [ #  # ]:          0 :                         return log_error_errno(r, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
     466                 :            : 
     467                 :          0 :                 r = bind(s, &sa.sa, sizeof(sa.in6));
     468         [ #  # ]:          0 :                 if (r < 0)
     469         [ #  # ]:          0 :                         return log_error_errno(errno, "mDNS-IPv6: Failed to bind socket: %m");
     470                 :            :         } else {
     471                 :            :                 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
     472                 :          0 :                 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
     473         [ #  # ]:          0 :                 if (r < 0)
     474         [ #  # ]:          0 :                         return log_error_errno(r, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
     475                 :            :         }
     476                 :            : 
     477                 :          0 :         r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, s, EPOLLIN, on_mdns_packet, m);
     478         [ #  # ]:          0 :         if (r < 0)
     479         [ #  # ]:          0 :                 return log_error_errno(r, "mDNS-IPv6: Failed to create event source: %m");
     480                 :            : 
     481                 :          0 :         return m->mdns_ipv6_fd = TAKE_FD(s);
     482                 :            : }

Generated by: LCOV version 1.14