resolved: respond to local resolver requests on 127.0.0.53:53

In order to improve compatibility with local clients that speak DNS directly
(and do not use NSS or our bus API) listen locally on 127.0.0.53:53 and process
any queries made that way.

Note that resolved does not implement a full DNS server on this port, but
simply enough to allow normal, local clients to resolve RRs through resolved.
Specifically it does not implement queries without the RD bit set (these are
requests where recursive lookups are explicitly disabled), and neither queries
with DNSSEC DO set in combination with DNSSEC CD (i.e. DNSSEC lookups with
validation turned off). It also refuses zone transfers and obsolete RR types.
All lookups done this way will be rejected with a clean error code, so that the
client side can repeat the query with a reduced feature set.

The code will set the DNSSEC AD flag however, depending on whether the data
resolved has been validated (or comes from a local, trusted source).

Lookups made via this mechanisms are propagated to LLMNR and mDNS as necessary,
but this is only partially useful as DNS packets cannot carry IP scope data
(i.e. the ifindex), and hence link-local addresses returned cannot be used
properly (and given that LLMNR/mDNS are mostly about link-local communication
this is quite a limitation). Also, given that DNS tends to use IDNA for
non-ASCII names, while LLMNR/mDNS uses UTF-8 lookups cannot be mapped 1:1.

In general this should improve compatibility with clients bypassing NSS but
it is highly recommended for clients to instead use NSS or our native bus API.

This patch also beefs up the DnsStream logic, as it reuses the code for local
TCP listening. DnsStream now provides proper reference counting for its
objects.

In order to avoid feedback loops resolved will no silently ignore 127.0.0.53
specified as DNS server when reading configuration.

resolved listens on 127.0.0.53:53 instead of 127.0.0.1:53 in order to leave
the latter free for local, external DNS servers or forwarders.

This also changes the "etc.conf" tmpfiles snippet to create a symlink from
/etc/resolv.conf to /usr/lib/systemd/resolv.conf by default, thus making this
stub the default mode of operation if /etc is not populated.
This commit is contained in:
Lennart Poettering
2016-06-21 00:58:47 +02:00
parent 2a3900d7e4
commit b30bf55d5c
24 changed files with 896 additions and 79 deletions

View File

@@ -125,6 +125,7 @@ dist_systemunit_DATA_busnames =
dist_sysusers_DATA =
check_PROGRAMS =
check_DATA =
dist_rootlibexec_DATA =
tests=
manual_tests =
TEST_EXTENSIONS = .py
@@ -5147,7 +5148,7 @@ systemd_export_LDADD = \
$(ZLIB_LIBS) \
-lbz2
dist_rootlibexec_DATA = \
dist_rootlibexec_DATA += \
src/import/import-pubring.gpg
nodist_systemunit_DATA += \
@@ -5259,6 +5260,8 @@ systemd_resolved_SOURCES = \
src/resolve/resolved-dns-stream.c \
src/resolve/resolved-dns-trust-anchor.h \
src/resolve/resolved-dns-trust-anchor.c \
src/resolve/resolved-dns-stub.h \
src/resolve/resolved-dns-stub.c \
src/resolve/resolved-etc-hosts.h \
src/resolve/resolved-etc-hosts.c \
src/shared/gcrypt-util.c \
@@ -5411,6 +5414,9 @@ EXTRA_DIST += \
units/systemd-resolved.service.m4.in \
src/resolve/resolved.conf.in
dist_rootlibexec_DATA += \
src/resolve/resolv.conf
# ------------------------------------------------------------------------------
if ENABLE_NETWORKD
rootlibexec_PROGRAMS += \

11
src/resolve/resolv.conf Normal file
View File

@@ -0,0 +1,11 @@
# This is a static resolv.conf file for connecting local clients to
# systemd-resolved via its DNS stub listener on 127.0.0.53.
#
# Third party programs must not access this file directly, but only through the
# symlink at /etc/resolv.conf. To manage resolv.conf(5) in a different way,
# replace this symlink by a static file or a different symlink.
#
# See systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.
nameserver 127.0.0.53

View File

@@ -37,6 +37,10 @@ int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char
if (r < 0)
return r;
/* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */
if (!dns_server_address_valid(family, &address))
return 0;
/* Filter out duplicates */
s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, ifindex);
if (s) {

View File

@@ -264,6 +264,7 @@ int dns_packet_validate_query(DnsPacket *p) {
switch (p->protocol) {
case DNS_PROTOCOL_LLMNR:
case DNS_PROTOCOL_DNS:
/* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */
if (DNS_PACKET_QDCOUNT(p) != 1)
return -EBADMSG;
@@ -719,9 +720,8 @@ int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, in
goto fail;
/* RDLENGTH */
if (edns0_do) {
/* If DO is on, also append RFC6975 Algorithm data */
if (edns0_do & !DNS_PACKET_QR(p)) {
/* If DO is on and this is not a reply, also append RFC6975 Algorithm data */
static const uint8_t rfc6975[] = {
@@ -752,7 +752,6 @@ int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, in
r = dns_packet_append_blob(p, rfc6975, sizeof(rfc6975), NULL);
} else
r = dns_packet_append_uint16(p, 0, NULL);
if (r < 0)
goto fail;
@@ -2062,8 +2061,10 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
assert(rr->key->type == DNS_TYPE_OPT);
/* Check that the version is 0 */
if (((rr->ttl >> 16) & UINT32_C(0xFF)) != 0)
return false;
if (((rr->ttl >> 16) & UINT32_C(0xFF)) != 0) {
*rfc6975 = false;
return true; /* if it's not version 0, it's OK, but we will ignore the OPT field contents */
}
p = rr->opt.data;
l = rr->opt.data_size;
@@ -2186,16 +2187,27 @@ int dns_packet_extract(DnsPacket *p) {
continue;
}
if (has_rfc6975) {
/* If the OPT RR contains RFC6975 algorithm data, then this is indication that
* the server just copied the OPT it got from us (which contained that data)
* back into the reply. If so, then it doesn't properly support EDNS, as
* RFC6975 makes it very clear that the algorithm data should only be contained
* in questions, never in replies. Crappy Belkin routers copy the OPT data for
* example, hence let's detect this so that we downgrade early. */
log_debug("OPT RR contained RFC6975 data, ignoring.");
bad_opt = true;
continue;
if (DNS_PACKET_QR(p)) {
/* Additional checks for responses */
if (!DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(rr)) {
/* If this is a reply and we don't know the EDNS version then something
* is weird... */
log_debug("EDNS version newer that our request, bad server.");
return -EBADMSG;
}
if (has_rfc6975) {
/* If the OPT RR contains RFC6975 algorithm data, then this is indication that
* the server just copied the OPT it got from us (which contained that data)
* back into the reply. If so, then it doesn't properly support EDNS, as
* RFC6975 makes it very clear that the algorithm data should only be contained
* in questions, never in replies. Crappy Belkin routers copy the OPT data for
* example, hence let's detect this so that we downgrade early. */
log_debug("OPT RR contained RFC6975 data, ignoring.");
bad_opt = true;
continue;
}
}
p->opt = dns_resource_record_ref(rr);

View File

@@ -118,6 +118,8 @@ static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
#define DNS_PACKET_AD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 5) & 1)
#define DNS_PACKET_CD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 4) & 1)
#define DNS_PACKET_FLAG_TC (UINT16_C(1) << 9)
static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) {
uint16_t rcode;
@@ -126,7 +128,34 @@ static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) {
else
rcode = 0;
return rcode | (be16toh(DNS_PACKET_HEADER(p)->flags) & 15);
return rcode | (be16toh(DNS_PACKET_HEADER(p)->flags) & 0xF);
}
static inline uint16_t DNS_PACKET_PAYLOAD_SIZE_MAX(DnsPacket *p) {
/* Returns the advertised maximum datagram size for replies, or the DNS default if there's nothing defined. */
if (p->opt)
return MAX(DNS_PACKET_UNICAST_SIZE_MAX, p->opt->key->class);
return DNS_PACKET_UNICAST_SIZE_MAX;
}
static inline bool DNS_PACKET_DO(DnsPacket *p) {
if (!p->opt)
return false;
return !!(p->opt->ttl & (1U << 15));
}
static inline bool DNS_PACKET_VERSION_SUPPORTED(DnsPacket *p) {
/* Returns true if this packet is in a version we support. Which means either non-EDNS or EDNS(0), but not EDNS
* of any newer versions */
if (!p->opt)
return true;
return DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(p->opt);
}
/* LLMNR defines some bits differently */

View File

@@ -404,6 +404,16 @@ DnsQuery *dns_query_free(DnsQuery *q) {
sd_bus_message_unref(q->request);
sd_bus_track_unref(q->bus_track);
dns_packet_unref(q->request_dns_packet);
if (q->request_dns_stream) {
/* Detach the stream from our query, in case something else keeps a reference to it. */
q->request_dns_stream->complete = NULL;
q->request_dns_stream->on_packet = NULL;
q->request_dns_stream->query = NULL;
dns_stream_unref(q->request_dns_stream);
}
free(q->request_address_string);
if (q->manager) {

View File

@@ -99,6 +99,10 @@ struct DnsQuery {
unsigned block_all_complete;
char *request_address_string;
/* DNS stub information */
DnsPacket *request_dns_packet;
DnsStream *request_dns_stream;
/* Completion callback */
void (*complete)(DnsQuery* q);
unsigned block_ready;

View File

@@ -282,6 +282,13 @@ static inline size_t DNS_RESOURCE_RECORD_RDATA_SIZE(DnsResourceRecord *rr) {
return rr->wire_format_size - rr->wire_format_rdata_offset;
}
static inline uint8_t DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(DnsResourceRecord *rr) {
assert(rr);
assert(rr->key->type == DNS_TYPE_OPT);
return ((rr->ttl >> 16) & 0xFF) == 0;
}
DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name);
DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname);
int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name);

View File

@@ -232,7 +232,7 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) {
if (fd < 0)
return fd;
r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, p);
r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, NULL, p);
if (r < 0)
return r;
@@ -257,7 +257,7 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) {
if (fd < 0)
return fd;
r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, p);
r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, NULL, p);
if (r < 0)
return r;
@@ -668,11 +668,11 @@ static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
}
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
DnsResourceKey *key = NULL;
bool tentative = false;
int r, fd;
int r;
assert(s);
assert(p);
@@ -694,7 +694,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
r = dns_packet_extract(p);
if (r < 0) {
log_debug_errno(r, "Failed to extract resources from incoming packet: %m");
log_debug_errno(r, "Failed to extract resource records from incoming packet: %m");
return;
}
@@ -724,9 +724,21 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
return;
}
if (stream)
if (stream) {
r = dns_stream_write_packet(stream, reply);
else {
if (r < 0) {
log_debug_errno(r, "Failed to enqueue reply packet: %m");
return;
}
/* Let's take an extra reference on this stream, so that it stays around after returning. The reference
* will be dangling until the stream is disconnected, and the default completion handler of the stream
* will then unref the stream and destroy it */
if (DNS_STREAM_QUEUED(stream))
dns_stream_ref(stream);
} else {
int fd;
if (!ratelimit_test(&s->ratelimit))
return;
@@ -748,12 +760,11 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
* verified uniqueness for all records. Also see RFC
* 4795, Section 2.7 */
r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply);
}
if (r < 0) {
log_debug_errno(r, "Failed to send reply packet: %m");
return;
r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, NULL, reply);
if (r < 0) {
log_debug_errno(r, "Failed to send reply packet: %m");
return;
}
}
}

View File

@@ -21,6 +21,7 @@
#include "alloc-util.h"
#include "resolved-dns-server.h"
#include "resolved-dns-stub.h"
#include "resolved-resolv-conf.h"
#include "siphash24.h"
#include "string-table.h"
@@ -750,6 +751,19 @@ void manager_next_dns_server(Manager *m) {
manager_set_dns_server(m, m->dns_servers);
}
bool dns_server_address_valid(int family, const union in_addr_union *sa) {
/* Refuses the 0 IP addresses as well as 127.0.0.53 (which is our own DNS stub) */
if (in_addr_is_null(family, sa))
return false;
if (family == AF_INET && sa->in.s_addr == htobe32(INADDR_DNS_STUB))
return false;
return true;
}
static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = {
[DNS_SERVER_SYSTEM] = "system",
[DNS_SERVER_FALLBACK] = "fallback",

View File

@@ -141,6 +141,8 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
DnsServer *manager_get_dns_server(Manager *m);
void manager_next_dns_server(Manager *m);
bool dns_server_address_valid(int family, const union in_addr_union *sa);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
extern const struct hash_ops dns_server_hash_ops;

View File

@@ -56,8 +56,8 @@ static int dns_stream_complete(DnsStream *s, int error) {
if (s->complete)
s->complete(s, error);
else
dns_stream_free(s);
else /* the default action if no completion function is set is to close the stream */
dns_stream_unref(s);
return 0;
}
@@ -323,10 +323,16 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
return 0;
}
DnsStream *dns_stream_free(DnsStream *s) {
DnsStream *dns_stream_unref(DnsStream *s) {
if (!s)
return NULL;
assert(s->n_ref > 0);
s->n_ref--;
if (s->n_ref > 0)
return NULL;
dns_stream_stop(s);
if (s->manager) {
@@ -339,13 +345,23 @@ DnsStream *dns_stream_free(DnsStream *s) {
free(s);
return 0;
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_unref);
DnsStream *dns_stream_ref(DnsStream *s) {
if (!s)
return NULL;
assert(s->n_ref > 0);
s->n_ref++;
return s;
}
int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
_cleanup_(dns_stream_freep) DnsStream *s = NULL;
_cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
int r;
assert(m);
@@ -358,6 +374,7 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
if (!s)
return -ENOMEM;
s->n_ref = 1;
s->fd = -1;
s->protocol = protocol;

View File

@@ -26,8 +26,16 @@ typedef struct DnsStream DnsStream;
#include "resolved-dns-packet.h"
#include "resolved-dns-transaction.h"
/* Streams are used by three subsystems:
*
* 1. The normal transaction logic when doing a DNS or LLMNR lookup via TCP
* 2. The LLMNR logic when accepting a TCP-based lookup
* 3. The DNS stub logic when accepting a TCP-based lookup
*/
struct DnsStream {
Manager *manager;
int n_ref;
DnsProtocol protocol;
@@ -50,12 +58,23 @@ struct DnsStream {
int (*on_packet)(DnsStream *s);
int (*complete)(DnsStream *s, int error);
DnsTransaction *transaction;
DnsTransaction *transaction; /* when used by the transaction logic */
DnsQuery *query; /* when used by the DNS stub logic */
LIST_FIELDS(DnsStream, streams);
};
int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd);
DnsStream *dns_stream_free(DnsStream *s);
DnsStream *dns_stream_unref(DnsStream *s);
DnsStream *dns_stream_ref(DnsStream *s);
int dns_stream_write_packet(DnsStream *s, DnsPacket *p);
static inline bool DNS_STREAM_QUEUED(DnsStream *s) {
assert(s);
if (s->fd < 0) /* already stopped? */
return false;
return !!s->write_packet;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
#pragma once
/***
This file is part of systemd.
Copyright 2016 Lennart Poettering
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"
/* 127.0.0.53 in native endian */
#define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U)
int manager_dns_stub_udp_fd(Manager *m);
int manager_dns_stub_tcp_fd(Manager *m);
void manager_dns_stub_stop(Manager *m);
int manager_dns_stub_start(Manager *m);

View File

@@ -60,7 +60,14 @@ static void dns_transaction_flush_dnssec_transactions(DnsTransaction *t) {
static void dns_transaction_close_connection(DnsTransaction *t) {
assert(t);
t->stream = dns_stream_free(t->stream);
if (t->stream) {
/* Let's detach the stream from our transaction, in case something else keeps a reference to it. */
t->stream->complete = NULL;
t->stream->on_packet = NULL;
t->stream->transaction = NULL;
t->stream = dns_stream_unref(t->stream);
}
t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
t->dns_udp_fd = safe_close(t->dns_udp_fd);
}
@@ -444,7 +451,7 @@ static int on_stream_complete(DnsStream *s, int error) {
t = s->transaction;
p = dns_packet_ref(s->read_packet);
t->stream = dns_stream_free(t->stream);
dns_transaction_close_connection(t);
if (ERRNO_IS_DISCONNECT(error)) {
usec_t usec;
@@ -556,7 +563,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
r = dns_stream_write_packet(t->stream, t->sent);
if (r < 0) {
t->stream = dns_stream_free(t->stream);
t->stream = dns_stream_unref(t->stream);
return r;
}

View File

@@ -230,6 +230,9 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
if (sz != FAMILY_ADDRESS_SIZE(family))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
if (!dns_server_address_valid(family, d))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;

View File

@@ -91,18 +91,19 @@ static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *u
DnsScope *scope;
int r;
assert(s);
assert(fd >= 0);
assert(m);
r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p);
if (r <= 0)
return r;
scope = manager_find_scope(m, p);
if (!scope) {
if (!scope)
log_warning("Got LLMNR UDP packet on unknown scope. Ignoring.");
return 0;
}
if (dns_packet_validate_reply(p) > 0) {
log_debug("Got LLMNR reply packet for id %u", DNS_PACKET_ID(p));
else if (dns_packet_validate_reply(p) > 0) {
log_debug("Got LLMNR UDP reply packet for id %u", DNS_PACKET_ID(p));
dns_scope_check_conflicts(scope, p);
@@ -111,7 +112,7 @@ static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *u
dns_transaction_process_reply(t, p);
} else if (dns_packet_validate_query(p) > 0) {
log_debug("Got LLMNR query packet for id %u", DNS_PACKET_ID(p));
log_debug("Got LLMNR UDP query packet for id %u", DNS_PACKET_ID(p));
dns_scope_process_query(scope, NULL, p);
} else
@@ -283,25 +284,19 @@ static int on_llmnr_stream_packet(DnsStream *s) {
DnsScope *scope;
assert(s);
assert(s->read_packet);
scope = manager_find_scope(s->manager, s->read_packet);
if (!scope) {
if (!scope)
log_warning("Got LLMNR TCP packet on unknown scope. Ignoring.");
return 0;
}
if (dns_packet_validate_query(s->read_packet) > 0) {
log_debug("Got query packet for id %u", DNS_PACKET_ID(s->read_packet));
else if (dns_packet_validate_query(s->read_packet) > 0) {
log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(s->read_packet));
dns_scope_process_query(scope, s, s->read_packet);
/* If no reply packet was set, we free the stream */
if (s->write_packet)
return 0;
} else
log_debug("Invalid LLMNR TCP packet.");
log_debug("Invalid LLMNR TCP packet, ignoring.");
dns_stream_free(s);
dns_stream_unref(s);
return 0;
}

View File

@@ -36,6 +36,7 @@
#include "random-util.h"
#include "resolved-bus.h"
#include "resolved-conf.h"
#include "resolved-dns-stub.h"
#include "resolved-etc-hosts.h"
#include "resolved-llmnr.h"
#include "resolved-manager.h"
@@ -493,6 +494,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->dns_stub_udp_fd = m->dns_stub_tcp_fd = -1;
m->hostname_fd = -1;
m->llmnr_support = RESOLVE_SUPPORT_YES;
@@ -555,6 +557,10 @@ int manager_start(Manager *m) {
assert(m);
r = manager_dns_stub_start(m);
if (r < 0)
return r;
r = manager_llmnr_start(m);
if (r < 0)
return r;
@@ -584,6 +590,11 @@ Manager *manager_free(Manager *m) {
dns_scope_free(m->unicast_scope);
/* At this point only orphaned streams should remain. All others should have been freed already by their
* owners */
while (m->dns_streams)
dns_stream_unref(m->dns_streams);
hashmap_free(m->links);
hashmap_free(m->dns_transactions);
@@ -595,6 +606,7 @@ Manager *manager_free(Manager *m) {
manager_llmnr_stop(m);
manager_mdns_stop(m);
manager_dns_stub_stop(m);
sd_bus_slot_unref(m->prepare_for_sleep_slot);
sd_event_source_unref(m->bus_retry_event_source);
@@ -809,7 +821,14 @@ int manager_write(Manager *m, int fd, DnsPacket *p) {
return 0;
}
static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_addr *addr, uint16_t port, DnsPacket *p) {
static int manager_ipv4_send(
Manager *m,
int fd,
int ifindex,
const struct in_addr *destination,
uint16_t port,
const struct in_addr *source,
DnsPacket *p) {
union sockaddr_union sa = {
.in.sin_family = AF_INET,
};
@@ -822,14 +841,14 @@ static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_ad
assert(m);
assert(fd >= 0);
assert(addr);
assert(destination);
assert(port > 0);
assert(p);
iov.iov_base = DNS_PACKET_DATA(p);
iov.iov_len = p->size;
sa.in.sin_addr = *addr;
sa.in.sin_addr = *destination;
sa.in.sin_port = htobe16(port),
mh.msg_iov = &iov;
@@ -853,12 +872,23 @@ static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_ad
pi = (struct in_pktinfo*) CMSG_DATA(cmsg);
pi->ipi_ifindex = ifindex;
if (source)
pi->ipi_spec_dst = *source;
}
return sendmsg_loop(fd, &mh, 0);
}
static int manager_ipv6_send(Manager *m, int fd, int ifindex, const struct in6_addr *addr, uint16_t port, DnsPacket *p) {
static int manager_ipv6_send(
Manager *m,
int fd,
int ifindex,
const struct in6_addr *destination,
uint16_t port,
const struct in6_addr *source,
DnsPacket *p) {
union sockaddr_union sa = {
.in6.sin6_family = AF_INET6,
};
@@ -871,14 +901,14 @@ static int manager_ipv6_send(Manager *m, int fd, int ifindex, const struct in6_a
assert(m);
assert(fd >= 0);
assert(addr);
assert(destination);
assert(port > 0);
assert(p);
iov.iov_base = DNS_PACKET_DATA(p);
iov.iov_len = p->size;
sa.in6.sin6_addr = *addr;
sa.in6.sin6_addr = *destination;
sa.in6.sin6_port = htobe16(port),
sa.in6.sin6_scope_id = ifindex;
@@ -903,24 +933,36 @@ static int manager_ipv6_send(Manager *m, int fd, int ifindex, const struct in6_a
pi = (struct in6_pktinfo*) CMSG_DATA(cmsg);
pi->ipi6_ifindex = ifindex;
if (source)
pi->ipi6_addr = *source;
}
return sendmsg_loop(fd, &mh, 0);
}
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p) {
int manager_send(
Manager *m,
int fd,
int ifindex,
int family,
const union in_addr_union *destination,
uint16_t port,
const union in_addr_union *source,
DnsPacket *p) {
assert(m);
assert(fd >= 0);
assert(addr);
assert(destination);
assert(port > 0);
assert(p);
log_debug("Sending %s packet with id %" PRIu16 " on interface %i/%s.", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
if (family == AF_INET)
return manager_ipv4_send(m, fd, ifindex, &addr->in, port, p);
return manager_ipv4_send(m, fd, ifindex, &destination->in, port, &source->in, p);
if (family == AF_INET6)
return manager_ipv6_send(m, fd, ifindex, &addr->in6, port, p);
return manager_ipv6_send(m, fd, ifindex, &destination->in6, port, &source->in6, p);
return -EAFNOSUPPORT;
}

View File

@@ -128,6 +128,13 @@ struct Manager {
Set* etc_hosts_by_address;
Hashmap* etc_hosts_by_name;
usec_t etc_hosts_last, etc_hosts_mtime;
/* Local DNS stub on 127.0.0.53:53 */
int dns_stub_udp_fd;
int dns_stub_tcp_fd;
sd_event_source *dns_stub_udp_event_source;
sd_event_source *dns_stub_tcp_event_source;
};
/* Manager */
@@ -140,7 +147,7 @@ int manager_start(Manager *m);
uint32_t manager_find_mtu(Manager *m);
int manager_write(Manager *m, int fd, DnsPacket *p);
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p);
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *destination, uint16_t port, const union in_addr_union *source, DnsPacket *p);
int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret);
int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr);

Some files were not shown because too many files have changed in this diff Show More