mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
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:
@@ -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
11
src/resolve/resolv.conf
Normal 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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
572
src/resolve/resolved-dns-stub.c
Normal file
572
src/resolve/resolved-dns-stub.c
Normal file
File diff suppressed because it is too large
Load Diff
31
src/resolve/resolved-dns-stub.h
Normal file
31
src/resolve/resolved-dns-stub.h
Normal 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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user