diff options
author | Tom Gundersen <teg@jklm.no> | 2015-12-08 17:24:09 +0100 |
---|---|---|
committer | Tom Gundersen <teg@jklm.no> | 2015-12-08 17:24:09 +0100 |
commit | 73f72c61086f77b75431b1c6a068cea3fe6b9222 (patch) | |
tree | 85d0751d1764287abf73e3ec9376c8331b394004 | |
parent | 2af917de2d2c52808974ee62d245d114313ebae6 (diff) | |
parent | 7778dffff3d8bd7438fe19a248c16203668324c9 (diff) |
Merge pull request #2122 from zonque/resolved-mdns-1
resolved: more mDNS specific bits
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | src/resolve/resolved-def.h | 6 | ||||
-rw-r--r-- | src/resolve/resolved-dns-cache.c | 40 | ||||
-rw-r--r-- | src/resolve/resolved-dns-cache.h | 3 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.c | 55 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.h | 6 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.h | 8 | ||||
-rw-r--r-- | src/resolve/resolved-dns-scope.c | 56 | ||||
-rw-r--r-- | src/resolve/resolved-dns-scope.h | 1 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.c | 337 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.h | 8 | ||||
-rw-r--r-- | src/resolve/resolved-link.c | 24 | ||||
-rw-r--r-- | src/resolve/resolved-link.h | 3 | ||||
-rw-r--r-- | src/resolve/resolved-manager.c | 23 | ||||
-rw-r--r-- | src/resolve/resolved-manager.h | 8 | ||||
-rw-r--r-- | src/resolve/resolved-mdns.c | 288 | ||||
-rw-r--r-- | src/resolve/resolved-mdns.h | 32 |
17 files changed, 797 insertions, 103 deletions
diff --git a/Makefile.am b/Makefile.am index 1dda8c5137..41d0daaba2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5164,6 +5164,8 @@ systemd_resolved_SOURCES = \ src/resolve/resolved-link.c \ src/resolve/resolved-llmnr.h \ src/resolve/resolved-llmnr.c \ + src/resolve/resolved-mdns.h \ + src/resolve/resolved-mdns.c \ src/resolve/resolved-def.h \ src/resolve/resolved-dns-rr.h \ src/resolve/resolved-dns-rr.c \ diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h index db5ee57b51..6014d345f3 100644 --- a/src/resolve/resolved-def.h +++ b/src/resolve/resolved-def.h @@ -24,6 +24,8 @@ #define SD_RESOLVED_DNS (UINT64_C(1) << 0) #define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1) #define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2) +#define SD_RESOLVED_MDNS_IPV4 (UINT64_C(1) << 3) +#define SD_RESOLVED_MDNS_IPV6 (UINT64_C(1) << 4) #define SD_RESOLVED_NO_CNAME (UINT64_C(1) << 5) #define SD_RESOLVED_NO_TXT (UINT64_C(1) << 6) #define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) << 7) @@ -31,4 +33,6 @@ #define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9) #define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6) -#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_LLMNR|SD_RESOLVED_DNS) +#define SD_RESOLVED_MDNS (SD_RESOLVED_MDNS_IPV4|SD_RESOLVED_MDNS_IPV6) + +#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index bcb9994a8c..6124ff659c 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -459,7 +459,12 @@ int dns_cache_put( /* Second, add in positive entries for all contained RRs */ for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) { - r = dns_cache_put_positive(c, answer->items[i].rr, authenticated, timestamp, owner_family, owner_address); + DnsResourceRecord *rr = answer->items[i].rr; + + if (rr->key->cache_flush) + dns_cache_remove(c, rr->key); + + r = dns_cache_put_positive(c, rr, authenticated, timestamp, owner_family, owner_address); if (r < 0) goto fail; } @@ -726,6 +731,39 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_ return 1; } +int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { + unsigned ancount = 0; + Iterator iterator; + DnsCacheItem *i; + int r; + + assert(cache); + + HASHMAP_FOREACH(i, cache->by_key, iterator) { + DnsCacheItem *j; + + LIST_FOREACH(by_key, j, i) { + _cleanup_free_ char *t = NULL; + + if (!j->rr) + continue; + + if (!dns_key_is_shared(j->rr->key)) + continue; + + r = dns_packet_append_rr(p, j->rr, NULL, NULL); + if (r < 0) + return r; + + ancount ++; + } + } + + DNS_PACKET_HEADER(p)->ancount = htobe16(ancount); + + return 0; +} + void dns_cache_dump(DnsCache *cache, FILE *f) { Iterator iterator; DnsCacheItem *i; diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index 5f91164785..0f28bbe543 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -32,6 +32,7 @@ typedef struct DnsCache { } DnsCache; #include "resolved-dns-answer.h" +#include "resolved-dns-packet.h" #include "resolved-dns-question.h" #include "resolved-dns-rr.h" @@ -45,3 +46,5 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_ void dns_cache_dump(DnsCache *cache, FILE *f); bool dns_cache_is_empty(DnsCache *cache); + +int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p); diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index ea776f7916..9bd08eeec2 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -88,6 +88,16 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool 0 /* ad */, 0 /* cd */, 0 /* rcode */)); + else if (protocol == DNS_PROTOCOL_MDNS) + h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, + 0 /* opcode */, + 0 /* aa */, + 0 /* tc */, + 0 /* rd (ask for recursion) */, + 0 /* ra */, + 0 /* ad */, + 0 /* cd */, + 0 /* rcode */)); else h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, 0 /* opcode */, @@ -182,6 +192,13 @@ int dns_packet_validate_reply(DnsPacket *p) { break; + case DNS_PROTOCOL_MDNS: + /* RFC 6762, Section 18 */ + if (DNS_PACKET_RCODE(p) != 0) + return -EBADMSG; + + break; + default: break; } @@ -223,6 +240,18 @@ int dns_packet_validate_query(DnsPacket *p) { break; + case DNS_PROTOCOL_MDNS: + /* RFC 6762, Section 18 */ + if (DNS_PACKET_AA(p) != 0 || + DNS_PACKET_RD(p) != 0 || + DNS_PACKET_RA(p) != 0 || + DNS_PACKET_AD(p) != 0 || + DNS_PACKET_CD(p) != 0 || + DNS_PACKET_RCODE(p) != 0) + return -EBADMSG; + + break; + default: break; } @@ -1392,6 +1421,7 @@ fail: int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) { _cleanup_free_ char *name = NULL; + bool cache_flush = false; uint16_t class, type; DnsResourceKey *key; size_t saved_rindex; @@ -1414,12 +1444,23 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) { if (r < 0) goto fail; + if (p->protocol == DNS_PROTOCOL_MDNS) { + /* See RFC6762, Section 10.2 */ + + if (class & MDNS_RR_CACHE_FLUSH) { + class &= ~MDNS_RR_CACHE_FLUSH; + cache_flush = true; + } + } + key = dns_resource_key_new_consume(class, type, name); if (!key) { r = -ENOMEM; goto fail; } + key->cache_flush = cache_flush; + name = NULL; *ret = key; @@ -1778,8 +1819,16 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { break; - case DNS_TYPE_NSEC: - r = dns_packet_read_name(p, &rr->nsec.next_domain_name, false, NULL); + case DNS_TYPE_NSEC: { + + /* + * RFC6762, section 18.14 explicly states mDNS should use name compression. + * This contradicts RFC3845, section 2.1.1 + */ + + bool allow_compressed = p->protocol == DNS_PROTOCOL_MDNS; + + r = dns_packet_read_name(p, &rr->nsec.next_domain_name, allow_compressed, NULL); if (r < 0) goto fail; @@ -1792,7 +1841,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { * without the NSEC bit set. */ break; - + } case DNS_TYPE_NSEC3: { uint8_t size; diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index aa2823cfb9..48b3572cb4 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -225,6 +225,9 @@ DnsProtocol dns_protocol_from_string(const char *s) _pure_; #define LLMNR_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 252U) }) #define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } }) +#define MDNS_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 251U) }) +#define MDNS_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfb } }) + static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, bool authenticated) { uint64_t f; @@ -239,6 +242,9 @@ static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, case DNS_PROTOCOL_LLMNR: return f|(family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4); + case DNS_PROTOCOL_MDNS: + return family == AF_INET6 ? SD_RESOLVED_MDNS_IPV6 : SD_RESOLVED_MDNS_IPV4; + default: break; } diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index b82fa77562..5c2306ba96 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -45,6 +45,9 @@ enum { #define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8) #define DNSKEY_FLAG_SEP (UINT16_C(1) << 0) +/* mDNS RR flags */ +#define MDNS_RR_CACHE_FLUSH (UINT16_C(1) << 15) + /* DNSSEC algorithm identifiers, see * http://tools.ietf.org/html/rfc4034#appendix-A.1 and * https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */ @@ -76,6 +79,7 @@ struct DnsResourceKey { unsigned n_ref; uint16_t class, type; char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */ + bool cache_flush:1; }; /* Creates a temporary resource key. This is only useful to quickly @@ -246,6 +250,10 @@ int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRec int dns_resource_key_to_string(const DnsResourceKey *key, char **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref); +static inline bool dns_key_is_shared(const DnsResourceKey *key) { + return IN_SET(key->type, DNS_TYPE_PTR); +} + DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key); DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name); DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index a90692cdf4..eae903526b 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -30,6 +30,7 @@ #include "random-util.h" #include "resolved-dns-scope.h" #include "resolved-llmnr.h" +#include "resolved-mdns.h" #include "socket-util.h" #include "strv.h" @@ -59,6 +60,7 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int LIST_PREPEND(scopes, m->dns_scopes, s); dns_scope_llmnr_membership(s, true); + dns_scope_mdns_membership(s, true); log_debug("New scope on link %s, protocol %s, family %s", l ? l->name : "*", dns_protocol_to_string(protocol), family == AF_UNSPEC ? "*" : af_to_name(family)); @@ -95,6 +97,7 @@ DnsScope* dns_scope_free(DnsScope *s) { log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family)); dns_scope_llmnr_membership(s, false); + dns_scope_mdns_membership(s, false); dns_scope_abort_transactions(s); while (s->query_candidates) @@ -162,7 +165,6 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { union in_addr_union addr; int ifindex = 0, r; int family; - uint16_t port; uint32_t mtu; size_t saved_size = 0; @@ -228,7 +230,6 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { return -EBUSY; family = s->family; - port = LLMNR_PORT; if (family == AF_INET) { addr.in = LLMNR_MULTICAST_IPV4_ADDRESS; @@ -241,7 +242,30 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { if (fd < 0) return fd; - r = manager_send(s->manager, fd, ifindex, family, &addr, port, p); + r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, p); + if (r < 0) + return r; + + break; + + case DNS_PROTOCOL_MDNS: + if (!ratelimit_test(&s->ratelimit)) + return -EBUSY; + + family = s->family; + + if (family == AF_INET) { + addr.in = MDNS_MULTICAST_IPV4_ADDRESS; + fd = manager_mdns_ipv4_fd(s->manager); + } else if (family == AF_INET6) { + addr.in6 = MDNS_MULTICAST_IPV6_ADDRESS; + fd = manager_mdns_ipv6_fd(s->manager); + } else + return -EAFNOSUPPORT; + if (fd < 0) + return fd; + + r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, p); if (r < 0) return r; @@ -477,19 +501,15 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) { return true; } -int dns_scope_llmnr_membership(DnsScope *s, bool b) { +static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in, struct in6_addr in6) { int fd; assert(s); - - if (s->protocol != DNS_PROTOCOL_LLMNR) - return 0; - assert(s->link); if (s->family == AF_INET) { struct ip_mreqn mreqn = { - .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS, + .imr_multiaddr = in, .imr_ifindex = s->link->ifindex, }; @@ -508,7 +528,7 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) { } else if (s->family == AF_INET6) { struct ipv6_mreq mreq = { - .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS, + .ipv6mr_multiaddr = in6, .ipv6mr_interface = s->link->ifindex, }; @@ -527,6 +547,22 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) { return 0; } +int dns_scope_llmnr_membership(DnsScope *s, bool b) { + + if (s->protocol != DNS_PROTOCOL_LLMNR) + return 0; + + return dns_scope_multicast_membership(s, b, LLMNR_MULTICAST_IPV4_ADDRESS, LLMNR_MULTICAST_IPV6_ADDRESS); +} + +int dns_scope_mdns_membership(DnsScope *s, bool b) { + + if (s->protocol != DNS_PROTOCOL_MDNS) + return 0; + + return dns_scope_multicast_membership(s, b, MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_IPV6_ADDRESS); +} + static int dns_scope_make_reply_packet( DnsScope *s, uint16_t id, diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index 15d9a1fd6f..2fc2e07deb 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -93,6 +93,7 @@ DnsServer *dns_scope_get_dns_server(DnsScope *s); void dns_scope_next_dns_server(DnsScope *s); int dns_scope_llmnr_membership(DnsScope *s, bool b); +int dns_scope_mdns_membership(DnsScope *s, bool b); void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 1103a34c6f..90f07e6c4b 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -24,6 +24,7 @@ #include "dns-domain.h" #include "fd-util.h" #include "random-util.h" +#include "resolved-dns-cache.h" #include "resolved-dns-transaction.h" #include "resolved-llmnr.h" #include "string-table.h" @@ -243,7 +244,7 @@ static int on_stream_complete(DnsStream *s, int error) { } if (dns_packet_validate_reply(p) <= 0) { - log_debug("Invalid LLMNR TCP packet."); + log_debug("Invalid TCP reply packet."); dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); return 0; } @@ -384,6 +385,18 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { break; + case DNS_PROTOCOL_MDNS: + assert(t->scope->link); + + /* For mDNS we will not accept any packets from other interfaces */ + if (p->ifindex != t->scope->link->ifindex) + return; + + if (p->family != t->scope->family) + return; + + break; + case DNS_PROTOCOL_DNS: break; @@ -446,6 +459,13 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { } if (DNS_PACKET_TC(p)) { + + /* Truncated packets for mDNS are not allowed. Give up immediately. */ + if (t->scope->protocol == DNS_PROTOCOL_MDNS) { + dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + return; + } + /* Response was truncated, let's try again with good old TCP */ r = dns_transaction_open_tcp(t); if (r == -ESRCH) { @@ -454,7 +474,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { return; } if (r < 0) { - /* On LLMNR, if we cannot connect to the host, + /* On LLMNR and mDNS, if we cannot connect to the host, * we immediately give up */ if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); @@ -481,29 +501,31 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { return; } - /* Only consider responses with equivalent query section to the request */ - if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) { - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - return; - } + if (t->scope->protocol == DNS_PROTOCOL_DNS) { + /* Only consider responses with equivalent query section to the request */ + if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) { + dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + return; + } - /* Install the answer as answer to the transaction */ - dns_answer_unref(t->answer); - t->answer = dns_answer_ref(p->answer); - t->answer_rcode = DNS_PACKET_RCODE(p); - t->answer_authenticated = t->scope->dnssec_mode == DNSSEC_TRUST && DNS_PACKET_AD(p); - - /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */ - if (DNS_PACKET_SHALL_CACHE(p)) - dns_cache_put(&t->scope->cache, - t->key, - DNS_PACKET_RCODE(p), - p->answer, - DNS_PACKET_ANCOUNT(p), - t->answer_authenticated, - 0, - p->family, - &p->sender); + /* Install the answer as answer to the transaction */ + dns_answer_unref(t->answer); + t->answer = dns_answer_ref(p->answer); + t->answer_rcode = DNS_PACKET_RCODE(p); + t->answer_authenticated = t->scope->dnssec_mode == DNSSEC_TRUST && DNS_PACKET_AD(p); + + /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */ + if (DNS_PACKET_SHALL_CACHE(p)) + dns_cache_put(&t->scope->cache, + t->key, + DNS_PACKET_RCODE(p), + p->answer, + DNS_PACKET_ANCOUNT(p), + t->answer_authenticated, + 0, + p->family, + &p->sender); + } if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS) dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); @@ -571,21 +593,26 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat assert(s); assert(t); - /* Timeout reached? Increase the timeout for the server used */ - switch (t->scope->protocol) { - case DNS_PROTOCOL_DNS: - assert(t->server); + if (!t->initial_jitter_scheduled || t->initial_jitter_elapsed) { + /* Timeout reached? Increase the timeout for the server used */ + switch (t->scope->protocol) { + case DNS_PROTOCOL_DNS: + assert(t->server); - dns_server_packet_lost(t->server, t->current_features, usec - t->start_usec); + dns_server_packet_lost(t->server, t->current_features, usec - t->start_usec); - break; - case DNS_PROTOCOL_LLMNR: - case DNS_PROTOCOL_MDNS: - dns_scope_packet_lost(t->scope, usec - t->start_usec); + break; + case DNS_PROTOCOL_LLMNR: + case DNS_PROTOCOL_MDNS: + dns_scope_packet_lost(t->scope, usec - t->start_usec); - break; - default: - assert_not_reached("Invalid DNS protocol."); + break; + default: + assert_not_reached("Invalid DNS protocol."); + } + + if (t->initial_jitter_scheduled) + t->initial_jitter_elapsed = true; } /* ...and try again with a new server */ @@ -598,38 +625,6 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat return 0; } -static int dns_transaction_make_packet(DnsTransaction *t) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - int r; - - assert(t); - - if (t->sent) - return 0; - - r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode == DNSSEC_YES); - if (r < 0) - return r; - - r = dns_scope_good_key(t->scope, t->key); - if (r < 0) - return r; - if (r == 0) - return -EDOM; - - r = dns_packet_append_key(p, t->key, NULL); - if (r < 0) - return r; - - DNS_PACKET_HEADER(p)->qdcount = htobe16(1); - DNS_PACKET_HEADER(p)->id = t->id; - - t->sent = p; - p = NULL; - - return 0; -} - static usec_t transaction_get_resend_timeout(DnsTransaction *t) { assert(t); assert(t->scope); @@ -639,17 +634,18 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) { assert(t->server); return t->server->resend_timeout; - case DNS_PROTOCOL_LLMNR: case DNS_PROTOCOL_MDNS: + assert(t->n_attempts > 0); + return (1 << (t->n_attempts - 1)) * USEC_PER_SEC; + case DNS_PROTOCOL_LLMNR: return t->scope->resend_timeout; default: assert_not_reached("Invalid DNS protocol."); } } -int dns_transaction_go(DnsTransaction *t) { +static int dns_transaction_prepare_next_attempt(DnsTransaction *t, usec_t ts) { bool had_stream; - usec_t ts; int r; assert(t); @@ -658,11 +654,6 @@ int dns_transaction_go(DnsTransaction *t) { dns_transaction_stop(t); - log_debug("Excercising transaction on scope %s on %s/%s", - dns_protocol_to_string(t->scope->protocol), - t->scope->link ? t->scope->link->name : "*", - t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family)); - if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) { dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED); return 0; @@ -675,8 +666,6 @@ int dns_transaction_go(DnsTransaction *t) { return 0; } - assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); - t->n_attempts++; t->start_usec = ts; t->received = dns_packet_unref(t->received); @@ -739,31 +728,203 @@ int dns_transaction_go(DnsTransaction *t) { } } - if (t->scope->protocol == DNS_PROTOCOL_LLMNR && !t->initial_jitter) { - usec_t jitter; + return 1; +} + +static int dns_transaction_make_packet_mdns(DnsTransaction *t) { + + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + bool add_known_answers = false; + DnsTransaction *other; + unsigned qdcount; + usec_t ts; + int r; + + assert(t); + assert(t->scope->protocol == DNS_PROTOCOL_MDNS); + + /* Discard any previously prepared packet, so we can start over and coaleasce again */ + t->sent = dns_packet_unref(t->sent); + + r = dns_packet_new_query(&p, t->scope->protocol, 0, false); + if (r < 0) + return r; + + r = dns_packet_append_key(p, t->key, NULL); + if (r < 0) + return r; + + qdcount = 1; + + if (dns_key_is_shared(t->key)) + add_known_answers = true; + + /* + * For mDNS, we want to coalesce as many open queries in pending transactions into one single + * query packet on the wire as possible. To achieve that, we iterate through all pending transactions + * in our current scope, and see whether their timing contraints allow them to be sent. + */ + + assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); + + LIST_FOREACH(transactions_by_scope, other, t->scope->transactions) { + + /* Skip ourselves */ + if (other == t) + continue; + + if (other->state != DNS_TRANSACTION_PENDING) + continue; + + if (other->next_attempt_after > ts) + continue; + + if (qdcount >= UINT16_MAX) + break; + + r = dns_packet_append_key(p, other->key, NULL); + + /* + * If we can't stuff more questions into the packet, just give up. + * One of the 'other' transactions will fire later and take care of the rest. + */ + if (r == -EMSGSIZE) + break; + + if (r < 0) + return r; + + r = dns_transaction_prepare_next_attempt(other, ts); + if (r <= 0) + continue; + + ts += transaction_get_resend_timeout(other); + + r = sd_event_add_time( + other->scope->manager->event, + &other->timeout_event_source, + clock_boottime_or_monotonic(), + ts, 0, + on_transaction_timeout, other); + if (r < 0) + return r; + + other->state = DNS_TRANSACTION_PENDING; + other->next_attempt_after = ts; + + qdcount ++; + + if (dns_key_is_shared(other->key)) + add_known_answers = true; + } + + DNS_PACKET_HEADER(p)->qdcount = htobe16(qdcount); + DNS_PACKET_HEADER(p)->id = t->id; + + /* Append known answer section if we're asking for any shared record */ + if (add_known_answers) { + r = dns_cache_export_shared_to_packet(&t->scope->cache, p); + if (r < 0) + return r; + } + + t->sent = p; + p = NULL; + + return 0; +} + +static int dns_transaction_make_packet(DnsTransaction *t) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + int r; + + assert(t); + + if (t->scope->protocol == DNS_PROTOCOL_MDNS) + return dns_transaction_make_packet_mdns(t); + + if (t->sent) + return 0; + + r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode == DNSSEC_YES); + if (r < 0) + return r; + + r = dns_scope_good_key(t->scope, t->key); + if (r < 0) + return r; + if (r == 0) + return -EDOM; + + r = dns_packet_append_key(p, t->key, NULL); + if (r < 0) + return r; + + DNS_PACKET_HEADER(p)->qdcount = htobe16(1); + DNS_PACKET_HEADER(p)->id = t->id; + + t->sent = p; + p = NULL; + + return 0; +} + +int dns_transaction_go(DnsTransaction *t) { + usec_t ts; + int r; + + assert(t); + + assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); + r = dns_transaction_prepare_next_attempt(t, ts); + if (r <= 0) + return r; + + log_debug("Excercising transaction on scope %s on %s/%s", + dns_protocol_to_string(t->scope->protocol), + t->scope->link ? t->scope->link->name : "*", + t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family)); + + if (!t->initial_jitter_scheduled && + (t->scope->protocol == DNS_PROTOCOL_LLMNR || + t->scope->protocol == DNS_PROTOCOL_MDNS)) { + usec_t jitter, accuracy; /* RFC 4795 Section 2.7 suggests all queries should be * delayed by a random time from 0 to JITTER_INTERVAL. */ - t->initial_jitter = true; + t->initial_jitter_scheduled = true; random_bytes(&jitter, sizeof(jitter)); - jitter %= LLMNR_JITTER_INTERVAL_USEC; + + switch (t->scope->protocol) { + case DNS_PROTOCOL_LLMNR: + jitter %= LLMNR_JITTER_INTERVAL_USEC; + accuracy = LLMNR_JITTER_INTERVAL_USEC; + break; + case DNS_PROTOCOL_MDNS: + jitter %= MDNS_JITTER_RANGE_USEC; + jitter += MDNS_JITTER_MIN_USEC; + accuracy = MDNS_JITTER_RANGE_USEC; + break; + default: + assert_not_reached("bad protocol"); + } r = sd_event_add_time( t->scope->manager->event, &t->timeout_event_source, clock_boottime_or_monotonic(), - ts + jitter, - LLMNR_JITTER_INTERVAL_USEC, + ts + jitter, accuracy, on_transaction_timeout, t); if (r < 0) return r; t->n_attempts = 0; + t->next_attempt_after = ts; t->state = DNS_TRANSACTION_PENDING; - log_debug("Delaying LLMNR transaction for " USEC_FMT "us.", jitter); + log_debug("Delaying %s transaction for " USEC_FMT "us.", dns_protocol_to_string(t->scope->protocol), jitter); return 0; } @@ -810,16 +971,20 @@ int dns_transaction_go(DnsTransaction *t) { return dns_transaction_go(t); } + ts += transaction_get_resend_timeout(t); + r = sd_event_add_time( t->scope->manager->event, &t->timeout_event_source, clock_boottime_or_monotonic(), - ts + transaction_get_resend_timeout(t), 0, + ts, 0, on_transaction_timeout, t); if (r < 0) return r; t->state = DNS_TRANSACTION_PENDING; + t->next_attempt_after = ts; + return 1; } diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index a3058ce6e8..af08b20e44 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -62,7 +62,8 @@ struct DnsTransaction { DnsTransactionState state; uint16_t id; - bool initial_jitter; + bool initial_jitter_scheduled; + bool initial_jitter_elapsed; DnsPacket *sent, *received; @@ -72,6 +73,7 @@ struct DnsTransaction { bool answer_authenticated; usec_t start_usec; + usec_t next_attempt_after; sd_event_source *timeout_event_source; unsigned n_attempts; @@ -119,6 +121,10 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_; /* LLMNR Jitter interval, see RFC 4795 Section 7 */ #define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC) +/* mDNS Jitter interval, see RFC 6762 Section 5.2 */ +#define MDNS_JITTER_MIN_USEC (20 * USEC_PER_MSEC) +#define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC) + /* Maximum attempts to send DNS requests, across all DNS servers */ #define DNS_TRANSACTION_ATTEMPTS_MAX 16 diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index ddd9427dab..84100bd988 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -77,6 +77,8 @@ Link *link_free(Link *l) { dns_scope_free(l->unicast_scope); dns_scope_free(l->llmnr_ipv4_scope); dns_scope_free(l->llmnr_ipv6_scope); + dns_scope_free(l->mdns_ipv4_scope); + dns_scope_free(l->mdns_ipv6_scope); free(l); return NULL; @@ -118,6 +120,28 @@ static void link_allocate_scopes(Link *l) { } } else l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope); + + if (link_relevant(l, AF_INET) && + l->mdns_support != SUPPORT_NO && + l->manager->mdns_support != SUPPORT_NO) { + if (!l->mdns_ipv4_scope) { + r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET); + if (r < 0) + log_warning_errno(r, "Failed to allocate mDNS IPv4 scope: %m"); + } + } else + l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope); + + if (link_relevant(l, AF_INET6) && + l->mdns_support != SUPPORT_NO && + l->manager->mdns_support != SUPPORT_NO) { + if (!l->mdns_ipv6_scope) { + r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6); + if (r < 0) + log_warning_errno(r, "Failed to allocate mDNS IPv6 scope: %m"); + } + } else + l->mdns_ipv6_scope = dns_scope_free(l->mdns_ipv6_scope); } void link_add_rrs(Link *l, bool force_remove) { diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index eb00015bd5..a3b406bbc2 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -67,10 +67,13 @@ struct Link { unsigned n_search_domains; Support llmnr_support; + Support mdns_support; DnsScope *unicast_scope; DnsScope *llmnr_ipv4_scope; DnsScope *llmnr_ipv6_scope; + DnsScope *mdns_ipv4_scope; + DnsScope *mdns_ipv6_scope; char name[IF_NAMESIZE]; uint32_t mtu; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 5a3696ccb0..a2677f442a 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -40,6 +40,7 @@ #include "resolved-llmnr.h" #include "resolved-manager.h" #include "resolved-resolv-conf.h" +#include "resolved-mdns.h" #include "socket-util.h" #include "string-table.h" #include "string-util.h" @@ -472,6 +473,7 @@ int manager_new(Manager **ret) { m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1; m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1; + m->mdns_ipv4_fd = m->mdns_ipv6_fd = -1; m->hostname_fd = -1; m->llmnr_support = SUPPORT_YES; @@ -528,6 +530,10 @@ int manager_start(Manager *m) { if (r < 0) return r; + r = manager_mdns_start(m); + if (r < 0) + return r; + return 0; } @@ -559,6 +565,7 @@ Manager *manager_free(Manager *m) { sd_event_source_unref(m->rtnl_event_source); manager_llmnr_stop(m); + manager_mdns_stop(m); sd_bus_slot_unref(m->prepare_for_sleep_slot); sd_event_source_unref(m->bus_retry_event_source); @@ -1024,11 +1031,25 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p) { if (!l) return NULL; - if (p->protocol == DNS_PROTOCOL_LLMNR) { + switch (p->protocol) { + case DNS_PROTOCOL_LLMNR: if (p->family == AF_INET) return l->llmnr_ipv4_scope; else if (p->family == AF_INET6) return l->llmnr_ipv6_scope; + + break; + + case DNS_PROTOCOL_MDNS: + if (p->family == AF_INET) + return l->mdns_ipv4_scope; + else if (p->family == AF_INET6) + return l->mdns_ipv6_scope; + + break; + + default: + break; } return NULL; diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 1056f23ab7..b52273403a 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -54,6 +54,7 @@ struct Manager { sd_event *event; Support llmnr_support; + Support mdns_support; /* Network */ Hashmap *links; @@ -102,6 +103,13 @@ struct Manager { sd_event_source *llmnr_ipv4_tcp_event_source; sd_event_source *llmnr_ipv6_tcp_event_source; + /* mDNS */ + int mdns_ipv4_fd; + int mdns_ipv6_fd; + + sd_event_source *mdns_ipv4_event_source; + sd_event_source *mdns_ipv6_event_source; + /* dbus */ sd_bus *bus; sd_event_source *bus_retry_event_source; diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c new file mode 100644 index 0000000000..096a4b1fe5 --- /dev/null +++ b/src/resolve/resolved-mdns.c @@ -0,0 +1,288 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Daniel Mack + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. + ***/ + +#include <resolv.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "fd-util.h" +#include "resolved-manager.h" +#include "resolved-mdns.h" + +void manager_mdns_stop(Manager *m) { + assert(m); + + m->mdns_ipv4_event_source = sd_event_source_unref(m->mdns_ipv4_event_source); + m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd); + + m->mdns_ipv6_event_source = sd_event_source_unref(m->mdns_ipv6_event_source); + m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd); +} + +int manager_mdns_start(Manager *m) { + int r; + + assert(m); + + if (m->mdns_support == SUPPORT_NO) + return 0; + + r = manager_mdns_ipv4_fd(m); + if (r == -EADDRINUSE) + goto eaddrinuse; + if (r < 0) + return r; + + if (socket_ipv6_is_supported()) { + r = manager_mdns_ipv6_fd(m); + if (r == -EADDRINUSE) + goto eaddrinuse; + if (r < 0) + return r; + } + + return 0; + +eaddrinuse: + log_warning("There appears to be another mDNS responder running. Turning off mDNS support."); + m->mdns_support = SUPPORT_NO; + manager_mdns_stop(m); + + return 0; +} + +static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + Manager *m = userdata; + DnsScope *scope; + int r; + + r = manager_recv(m, fd, DNS_PROTOCOL_MDNS, &p); + if (r <= 0) + return r; + + scope = manager_find_scope(m, p); + if (!scope) { + log_warning("Got mDNS UDP packet on unknown scope. Ignoring."); + return 0; + } + + if (dns_packet_validate_reply(p) > 0) { + unsigned i; + + log_debug("Got mDNS reply packet"); + + /* + * mDNS is different from regular DNS and LLMNR with regard to handling responses. + * While on other protocols, we can ignore every answer that doesn't match a question + * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all + * incoming information, regardless of the DNS packet ID. + * + * Hence, extract the packet here, and try to find a transaction for answer the we got + * and complete it. Also store the new information in scope's cache. + */ + r = dns_packet_extract(p); + if (r < 0) { + log_debug("mDNS packet extraction failed."); + return 0; + } + + dns_scope_check_conflicts(scope, p); + + for (i = 0; i < p->answer->n_rrs; i++) { + DnsResourceRecord *rr; + DnsTransaction *t; + + rr = p->answer->items[i].rr; + + t = dns_scope_find_transaction(scope, rr->key, false); + if (t) + dns_transaction_process_reply(t, p); + } + + dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, + p->answer->n_rrs, false, 0, p->family, &p->sender); + + } else if (dns_packet_validate_query(p) > 0) { + log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p)); + + dns_scope_process_query(scope, NULL, p); + } else + log_debug("Invalid mDNS UDP packet."); + + return 0; +} + +int manager_mdns_ipv4_fd(Manager *m) { + union sockaddr_union sa = { + .in.sin_family = AF_INET, + .in.sin_port = htobe16(MDNS_PORT), + }; + static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255; + int r; + + assert(m); + + if (m->mdns_ipv4_fd >= 0) + return m->mdns_ipv4_fd; + + m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (m->mdns_ipv4_fd < 0) + return -errno; + + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + /* Disable Don't-Fragment bit in the IP header */ + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m); + if (r < 0) + goto fail; + + return m->mdns_ipv4_fd; + +fail: + m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd); + return r; +} + +int manager_mdns_ipv6_fd(Manager *m) { + union sockaddr_union sa = { + .in6.sin6_family = AF_INET6, + .in6.sin6_port = htobe16(MDNS_PORT), + }; + static const int one = 1, ttl = 255; + int r; + + assert(m); + + if (m->mdns_ipv6_fd >= 0) + return m->mdns_ipv6_fd; + + m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (m->mdns_ipv6_fd < 0) + return -errno; + + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */ + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m); + if (r < 0) { + r = -errno; + goto fail; + } + + return m->mdns_ipv6_fd; + +fail: + m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd); + return r; +} diff --git a/src/resolve/resolved-mdns.h b/src/resolve/resolved-mdns.h new file mode 100644 index 0000000000..8a84010615 --- /dev/null +++ b/src/resolve/resolved-mdns.h @@ -0,0 +1,32 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Daniel Mack + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "resolved-manager.h" + +#define MDNS_PORT 5353 + +int manager_mdns_ipv4_fd(Manager *m); +int manager_mdns_ipv6_fd(Manager *m); + +void manager_mdns_stop(Manager *m); +int manager_mdns_start(Manager *m); |