summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--man/systemd.netdev.xml10
-rw-r--r--src/libsystemd/sd-netlink/netlink-internal.h2
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c48
-rw-r--r--src/network/networkd-netdev-gperf.gperf1
-rw-r--r--src/network/networkd-netdev-tuntap.c3
-rw-r--r--src/network/networkd-netdev-tuntap.h1
-rw-r--r--src/resolve-host/resolve-host.c4
-rw-r--r--src/resolve/resolved-dns-packet.c4
-rw-r--r--src/resolve/resolved-dns-packet.h3
-rw-r--r--src/resolve/resolved-dns-scope.c24
-rw-r--r--src/resolve/resolved-dns-scope.h4
-rw-r--r--src/resolve/resolved-dns-server.c44
-rw-r--r--src/resolve/resolved-dns-server.h7
-rw-r--r--src/resolve/resolved-dns-transaction.c131
-rw-r--r--src/resolve/resolved-dns-transaction.h12
-rw-r--r--src/resolve/resolved-link.c23
-rw-r--r--src/resolve/resolved-manager.c121
-rw-r--r--src/resolve/resolved-manager.h9
19 files changed, 278 insertions, 175 deletions
diff --git a/TODO b/TODO
index b3406dd6dd..3a2b4cf9f0 100644
--- a/TODO
+++ b/TODO
@@ -351,8 +351,6 @@ Features:
- edns0
- dname
- cname on PTR (?)
- - maybe randomize DNS UDP source ports
- - maybe compare query section of DNS replies
* Allow multiple ExecStart= for all Type= settings, so that we can cover rescue.service nicely
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index 01c31c5ef5..ff7b85453f 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -563,6 +563,16 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><varname>VnetHeader=</varname></term>
+ <listitem><para>Takes a boolean argument. Configures
+ IFF_VNET_HDR flag for a tap device. It allows sending
+ and receiving larger Generic Segmentation Offload (GSO)
+ packets. This may increase throughput significantly.
+ Defaults to
+ <literal>no</literal>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>User=</varname></term>
<listitem><para>User to grant access to the
<filename>/dev/net/tun</filename> device.</para>
diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h
index 6f51ebe73d..4026e2c341 100644
--- a/src/libsystemd/sd-netlink/netlink-internal.h
+++ b/src/libsystemd/sd-netlink/netlink-internal.h
@@ -94,6 +94,8 @@ struct sd_netlink {
struct netlink_attribute {
size_t offset; /* offset from hdr to attribute */
+ bool nested:1;
+ bool net_byteorder:1;
};
struct netlink_container {
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index 13573dcea8..b0ed2f2882 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -38,6 +38,7 @@
#define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers ++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr;
#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
+#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
sd_netlink_message *m;
@@ -475,7 +476,7 @@ int sd_netlink_message_close_container(sd_netlink_message *m) {
return 0;
}
-static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data) {
+static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data, bool *net_byteorder) {
struct netlink_attribute *attribute;
struct rtattr *rta;
@@ -495,6 +496,9 @@ static int netlink_message_read_internal(sd_netlink_message *m, unsigned short t
*data = RTA_DATA(rta);
+ if (net_byteorder)
+ *net_byteorder = attribute->net_byteorder;
+
return RTA_PAYLOAD(rta);
}
@@ -508,7 +512,7 @@ int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, c
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data);
+ r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0)
return r;
else if (strnlen(attr_data, r) >= (size_t) r)
@@ -530,7 +534,7 @@ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data);
+ r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0)
return r;
else if ((size_t) r < sizeof(uint8_t))
@@ -543,8 +547,9 @@ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8
}
int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) {
- int r;
void *attr_data;
+ bool net_byteorder;
+ int r;
assert_return(m, -EINVAL);
@@ -552,21 +557,26 @@ int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data);
+ r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
if (r < 0)
return r;
else if ((size_t) r < sizeof(uint16_t))
return -EIO;
- if (data)
- *data = *(uint16_t *) attr_data;
+ if (data) {
+ if (net_byteorder)
+ *data = be16toh(*(uint16_t *) attr_data);
+ else
+ *data = *(uint16_t *) attr_data;
+ }
return 0;
}
int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) {
- int r;
void *attr_data;
+ bool net_byteorder;
+ int r;
assert_return(m, -EINVAL);
@@ -574,14 +584,18 @@ int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data);
+ r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
if (r < 0)
return r;
else if ((size_t)r < sizeof(uint32_t))
return -EIO;
- if (data)
- *data = *(uint32_t *) attr_data;
+ if (data) {
+ if (net_byteorder)
+ *data = be32toh(*(uint32_t *) attr_data);
+ else
+ *data = *(uint32_t *) attr_data;
+ }
return 0;
}
@@ -596,7 +610,7 @@ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short typ
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data);
+ r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0)
return r;
else if ((size_t)r < sizeof(struct ether_addr))
@@ -618,7 +632,7 @@ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short typ
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data);
+ r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0)
return r;
else if ((size_t)r < sizeof(struct ifa_cacheinfo))
@@ -640,7 +654,7 @@ int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type,
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data);
+ r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0)
return r;
else if ((size_t)r < sizeof(struct in_addr))
@@ -662,7 +676,7 @@ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type,
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data);
+ r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0)
return r;
else if ((size_t)r < sizeof(struct in6_addr))
@@ -699,6 +713,8 @@ static int netlink_container_parse(sd_netlink_message *m,
log_debug("rtnl: message parse - overwriting repeated attribute");
attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr;
+ attributes[type].nested = RTA_FLAGS(rta) & NLA_F_NESTED;
+ attributes[type].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER;
}
container->attributes = attributes;
@@ -781,7 +797,7 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
} else
return -EINVAL;
- r = netlink_message_read_internal(m, type_id, &container);
+ r = netlink_message_read_internal(m, type_id, &container, NULL);
if (r < 0)
return r;
else
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index 66ed2e013c..010c106610 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -59,6 +59,7 @@ Tun.Group, config_parse_string, 0,
Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
+Tap.VnetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
Tap.User, config_parse_string, 0, offsetof(TunTap, user_name)
Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name)
Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode)
diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/networkd-netdev-tuntap.c
index 378312f091..ba84e802fc 100644
--- a/src/network/networkd-netdev-tuntap.c
+++ b/src/network/networkd-netdev-tuntap.c
@@ -51,6 +51,9 @@ static int netdev_fill_tuntap_message(NetDev *netdev, struct ifreq *ifr) {
if (t->multi_queue)
ifr->ifr_flags |= IFF_MULTI_QUEUE;
+ if (t->vnet_hdr)
+ ifr->ifr_flags |= IFF_VNET_HDR;
+
strncpy(ifr->ifr_name, netdev->ifname, IFNAMSIZ-1);
return 0;
diff --git a/src/network/networkd-netdev-tuntap.h b/src/network/networkd-netdev-tuntap.h
index b804875bbb..29f8bb0ea5 100644
--- a/src/network/networkd-netdev-tuntap.h
+++ b/src/network/networkd-netdev-tuntap.h
@@ -33,6 +33,7 @@ struct TunTap {
bool one_queue;
bool multi_queue;
bool packet_info;
+ bool vnet_hdr;
};
extern const NetDevVTable tun_vtable;
diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c
index f9448e3bc5..0edba415b6 100644
--- a/src/resolve-host/resolve-host.c
+++ b/src/resolve-host/resolve-host.c
@@ -89,10 +89,6 @@ static int resolve_host(sd_bus *bus, const char *name) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_auto_start(req, false);
- if (r < 0)
- return bus_log_create_error(r);
-
r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
if (r < 0)
return bus_log_create_error(r);
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index ea7975a094..1ebebf8ea4 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -32,10 +32,10 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
assert(ret);
- if (mtu <= 0)
+ if (mtu <= UDP_PACKET_HEADER_SIZE)
a = DNS_PACKET_SIZE_START;
else
- a = mtu;
+ a = mtu - UDP_PACKET_HEADER_SIZE;
if (a < DNS_PACKET_HEADER_SIZE)
a = DNS_PACKET_HEADER_SIZE;
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index bf998aa84e..6588ed9df5 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -21,6 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <netinet/udp.h>
+#include <netinet/ip.h>
#include "macro.h"
#include "sparse-endian.h"
@@ -53,6 +55,7 @@ struct DnsPacketHeader {
};
#define DNS_PACKET_HEADER_SIZE sizeof(DnsPacketHeader)
+#define UDP_PACKET_HEADER_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr))
/* The various DNS protocols deviate in how large a packet can grow,
but the TCP transport has a 16bit size field, hence that appears to
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 25392d21d7..7b72c090c2 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -125,7 +125,8 @@ void dns_scope_next_dns_server(DnsScope *s) {
manager_next_dns_server(s->manager);
}
-int dns_scope_emit(DnsScope *s, DnsPacket *p) {
+int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **server) {
+ DnsServer *srv = NULL;
union in_addr_union addr;
int ifindex = 0, r;
int family;
@@ -144,8 +145,6 @@ int dns_scope_emit(DnsScope *s, DnsPacket *p) {
mtu = manager_find_mtu(s->manager);
if (s->protocol == DNS_PROTOCOL_DNS) {
- DnsServer *srv;
-
if (DNS_PACKET_QDCOUNT(p) > 1)
return -EOPNOTSUPP;
@@ -160,13 +159,13 @@ int dns_scope_emit(DnsScope *s, DnsPacket *p) {
if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
return -EMSGSIZE;
- if (p->size > mtu)
+ if (p->size + UDP_PACKET_HEADER_SIZE > mtu)
return -EMSGSIZE;
if (family == AF_INET)
- fd = manager_dns_ipv4_fd(s->manager);
+ fd = transaction_dns_ipv4_fd(t);
else if (family == AF_INET6)
- fd = manager_dns_ipv6_fd(s->manager);
+ fd = transaction_dns_ipv6_fd(t);
else
return -EAFNOSUPPORT;
if (fd < 0)
@@ -200,10 +199,14 @@ int dns_scope_emit(DnsScope *s, DnsPacket *p) {
if (r < 0)
return r;
+ if (server)
+ *server = srv;
+
return 1;
}
-int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port) {
+int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
+ DnsServer *srv = NULL;
_cleanup_close_ int fd = -1;
union sockaddr_union sa = {};
socklen_t salen;
@@ -214,8 +217,6 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
assert((family == AF_UNSPEC) == !address);
if (family == AF_UNSPEC) {
- DnsServer *srv;
-
srv = dns_scope_get_dns_server(s);
if (!srv)
return -ESRCH;
@@ -288,6 +289,9 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
if (r < 0 && errno != EINPROGRESS)
return -errno;
+ if (server)
+ *server = srv;
+
ret = fd;
fd = -1;
@@ -696,7 +700,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata
return 0;
}
- r = dns_scope_emit(scope, p);
+ r = dns_scope_emit(scope, NULL, p, NULL);
if (r < 0)
log_debug_errno(r, "Failed to send conflict packet: %m");
}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index cfbde1343f..5c5ccc71c5 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -65,8 +65,8 @@ struct DnsScope {
int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family);
DnsScope* dns_scope_free(DnsScope *s);
-int dns_scope_emit(DnsScope *s, DnsPacket *p);
-int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port);
+int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **server);
+int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server);
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
int dns_scope_good_key(DnsScope *s, DnsResourceKey *key);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 9a62a63258..92e48ae442 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -41,6 +41,7 @@ int dns_server_new(
if (!s)
return -ENOMEM;
+ s->n_ref = 1;
s->type = type;
s->family = family;
s->address = *in_addr;
@@ -74,33 +75,46 @@ int dns_server_new(
return 0;
}
-DnsServer* dns_server_free(DnsServer *s) {
+DnsServer* dns_server_ref(DnsServer *s) {
if (!s)
return NULL;
- if (s->link) {
- if (s->type == DNS_SERVER_LINK)
- LIST_REMOVE(servers, s->link->dns_servers, s);
+ assert(s->n_ref > 0);
- if (s->link->current_dns_server == s)
- link_set_dns_server(s->link, NULL);
- }
+ s->n_ref ++;
- if (s->manager) {
- if (s->type == DNS_SERVER_SYSTEM)
- LIST_REMOVE(servers, s->manager->dns_servers, s);
- else if (s->type == DNS_SERVER_FALLBACK)
- LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
+ return s;
+}
+
+static DnsServer* dns_server_free(DnsServer *s) {
+ if (!s)
+ return NULL;
- if (s->manager->current_dns_server == s)
- manager_set_dns_server(s->manager, NULL);
- }
+ if (s->link && s->link->current_dns_server == s)
+ link_set_dns_server(s->link, NULL);
+
+ if (s->manager && s->manager->current_dns_server == s)
+ manager_set_dns_server(s->manager, NULL);
free(s);
return NULL;
}
+DnsServer* dns_server_unref(DnsServer *s) {
+ if (!s)
+ return NULL;
+
+ assert(s->n_ref > 0);
+
+ if (s->n_ref == 1)
+ dns_server_free(s);
+ else
+ s->n_ref --;
+
+ return NULL;
+}
+
static unsigned long dns_server_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
const DnsServer *s = p;
uint64_t u;
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 70ff35b08f..06059e8829 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -37,6 +37,8 @@ typedef enum DnsServerType {
struct DnsServer {
Manager *manager;
+ unsigned n_ref;
+
DnsServerType type;
Link *link;
@@ -57,6 +59,9 @@ int dns_server_new(
int family,
const union in_addr_union *address);
-DnsServer* dns_server_free(DnsServer *s);
+DnsServer* dns_server_ref(DnsServer *s);
+DnsServer* dns_server_unref(DnsServer *s);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
extern const struct hash_ops dns_server_hash_ops;
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 3f4673df7a..e468f245f7 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -39,6 +39,12 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
dns_packet_unref(t->received);
dns_answer_unref(t->cached);
+ sd_event_source_unref(t->dns_ipv4_event_source);
+ sd_event_source_unref(t->dns_ipv6_event_source);
+ safe_close(t->dns_ipv4_fd);
+ safe_close(t->dns_ipv6_fd);
+
+ dns_server_unref(t->server);
dns_stream_free(t->stream);
if (t->scope) {
@@ -88,6 +94,8 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) {
if (!t)
return -ENOMEM;
+ t->dns_ipv4_fd = t->dns_ipv6_fd = -1;
+
t->question = dns_question_ref(q);
do
@@ -237,6 +245,7 @@ static int on_stream_complete(DnsStream *s, int error) {
}
static int dns_transaction_open_tcp(DnsTransaction *t) {
+ _cleanup_(dns_server_unrefp) DnsServer *server = NULL;
_cleanup_close_ int fd = -1;
int r;
@@ -246,12 +255,12 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
return 0;
if (t->scope->protocol == DNS_PROTOCOL_DNS)
- fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53);
+ fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server);
else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
/* When we already received a query to this (but it was truncated), send to its sender address */
if (t->received)
- fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port);
+ fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL);
else {
union in_addr_union address;
int family = AF_UNSPEC;
@@ -265,7 +274,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
if (r == 0)
return -EINVAL;
- fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT);
+ fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL);
}
} else
return -EAFNOSUPPORT;
@@ -285,6 +294,9 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
return r;
}
+
+ dns_server_unref(t->server);
+ t->server = dns_server_ref(server);
t->received = dns_packet_unref(t->received);
t->stream->complete = on_stream_complete;
t->stream->transaction = t;
@@ -333,10 +345,15 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
if (t->scope->protocol == DNS_PROTOCOL_DNS) {
/* For DNS we are fine with accepting packets on any
- * interface, but the source IP address must be one of
- * a valid DNS server */
+ * interface, but the source IP address must be the
+ * one of the DNS server we queried */
+
+ assert(t->server);
- if (!dns_scope_good_dns_server(t->scope, p->family, &p->sender))
+ if (t->server->family != p->family)
+ return;
+
+ if (!in_addr_equal(p->family, &p->sender, &t->server->address))
return;
if (p->sender_port != 53)
@@ -398,6 +415,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
return;
}
+ /* Only consider responses with equivalent query section to the request */
+ if (!dns_question_is_superset(p->question, t->question) ||
+ !dns_question_is_superset(t->question, p->question))
+ dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+
/* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
@@ -492,6 +514,7 @@ int dns_transaction_go(DnsTransaction *t) {
}
t->n_attempts++;
+ t->server = dns_server_unref(t->server);
t->received = dns_packet_unref(t->received);
t->cached = dns_answer_unref(t->cached);
t->cached_rcode = 0;
@@ -571,17 +594,20 @@ int dns_transaction_go(DnsTransaction *t) {
* always be made via TCP on LLMNR */
r = dns_transaction_open_tcp(t);
} else {
+ DnsServer *server;
+
/* Try via UDP, and if that fails due to large size try via TCP */
- r = dns_scope_emit(t->scope, t->sent);
- if (r == -EMSGSIZE)
+ r = dns_scope_emit(t->scope, t, t->sent, &server);
+ if (r >= 0)
+ t->server = dns_server_ref(server);
+ else if (r == -EMSGSIZE)
r = dns_transaction_open_tcp(t);
}
if (r == -ESRCH) {
/* No servers to send this to? */
dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
return 0;
- }
- if (r < 0) {
+ } else if (r < 0) {
if (t->scope->protocol != DNS_PROTOCOL_DNS) {
dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
return 0;
@@ -606,6 +632,91 @@ int dns_transaction_go(DnsTransaction *t) {
return 1;
}
+static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+ DnsTransaction *t = userdata;
+ int r;
+
+ assert(t);
+ assert(t->scope);
+
+ r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p);
+ if (r <= 0)
+ return r;
+
+ if (dns_packet_validate_reply(p) > 0 &&
+ DNS_PACKET_ID(p) == t->id) {
+ dns_transaction_process_reply(t, p);
+ } else
+ log_debug("Invalid DNS packet.");
+
+ return 0;
+}
+
+int transaction_dns_ipv4_fd(DnsTransaction *t) {
+ const int one = 1;
+ int r;
+
+ assert(t);
+ assert(t->scope);
+ assert(t->scope->manager);
+
+ if (t->dns_ipv4_fd >= 0)
+ return t->dns_ipv4_fd;
+
+ t->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (t->dns_ipv4_fd < 0)
+ return -errno;
+
+ r = setsockopt(t->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv4_event_source, t->dns_ipv4_fd, EPOLLIN, on_dns_packet, t);
+ if (r < 0)
+ goto fail;
+
+ return t->dns_ipv4_fd;
+
+fail:
+ t->dns_ipv4_fd = safe_close(t->dns_ipv4_fd);
+ return r;
+}
+
+int transaction_dns_ipv6_fd(DnsTransaction *t) {
+ const int one = 1;
+ int r;
+
+ assert(t);
+ assert(t->scope);
+ assert(t->scope->manager);
+
+ if (t->dns_ipv6_fd >= 0)
+ return t->dns_ipv6_fd;
+
+ t->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (t->dns_ipv6_fd < 0)
+ return -errno;
+
+ r = setsockopt(t->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv6_event_source, t->dns_ipv6_fd, EPOLLIN, on_dns_packet, t);
+ if (r < 0)
+ goto fail;
+
+ return t->dns_ipv6_fd;
+
+fail:
+ t->dns_ipv6_fd = safe_close(t->dns_ipv6_fd);
+ return r;
+}
+
static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = {
[DNS_TRANSACTION_NULL] = "null",
[DNS_TRANSACTION_PENDING] = "pending",
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index f6d539d315..87f342ca11 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -61,6 +61,15 @@ struct DnsTransaction {
sd_event_source *timeout_event_source;
unsigned n_attempts;
+ int dns_ipv4_fd;
+ int dns_ipv6_fd;
+
+ sd_event_source *dns_ipv4_event_source;
+ sd_event_source *dns_ipv6_event_source;
+
+ /* the active server */
+ DnsServer *server;
+
/* TCP connection logic, if we need it */
DnsStream *stream;
@@ -86,6 +95,9 @@ int dns_transaction_go(DnsTransaction *t);
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p);
void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state);
+int transaction_dns_ipv4_fd(DnsTransaction *t);
+int transaction_dns_ipv6_fd(DnsTransaction *t);
+
const char* dns_transaction_state_to_string(DnsTransactionState p) _const_;
DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index ff8dc3a5bc..d66b3a88fc 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -58,7 +58,6 @@ int link_new(Manager *m, Link **ret, int ifindex) {
}
Link *link_free(Link *l) {
-
if (!l)
return NULL;
@@ -68,8 +67,12 @@ Link *link_free(Link *l) {
if (l->manager)
hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
- while (l->dns_servers)
- dns_server_free(l->dns_servers);
+ while (l->dns_servers) {
+ DnsServer *s = l->dns_servers;
+
+ LIST_REMOVE(servers, l->dns_servers, s);
+ dns_server_unref(s);
+ }
dns_scope_free(l->unicast_scope);
dns_scope_free(l->llmnr_ipv4_scope);
@@ -182,14 +185,20 @@ static int link_update_dns_servers(Link *l) {
}
LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
- if (s->marked)
- dns_server_free(s);
+ if (s->marked) {
+ LIST_REMOVE(servers, l->dns_servers, s);
+ dns_server_unref(s);
+ }
return 0;
clear:
- while (l->dns_servers)
- dns_server_free(l->dns_servers);
+ while (l->dns_servers) {
+ s = l->dns_servers;
+
+ LIST_REMOVE(servers, l->dns_servers, s);
+ dns_server_unref(s);
+ }
return r;
}
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 6785a2e3c7..17de14bae1 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -404,7 +404,6 @@ int manager_new(Manager **ret) {
if (!m)
return -ENOMEM;
- m->dns_ipv4_fd = m->dns_ipv6_fd = -1;
m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1;
m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1;
m->hostname_fd = -1;
@@ -486,11 +485,6 @@ Manager *manager_free(Manager *m) {
sd_event_source_unref(m->network_event_source);
sd_network_monitor_unref(m->network_monitor);
- sd_event_source_unref(m->dns_ipv4_event_source);
- sd_event_source_unref(m->dns_ipv6_event_source);
- safe_close(m->dns_ipv4_fd);
- safe_close(m->dns_ipv6_fd);
-
manager_llmnr_stop(m);
sd_bus_slot_unref(m->prepare_for_sleep_slot);
@@ -603,8 +597,10 @@ int manager_read_resolv_conf(Manager *m) {
}
LIST_FOREACH_SAFE(servers, s, nx, m->dns_servers)
- if (s->marked)
- dns_server_free(s);
+ if (s->marked) {
+ LIST_REMOVE(servers, m->dns_servers, s);
+ dns_server_unref(s);
+ }
/* Whenever /etc/resolv.conf changes, start using the first
* DNS server of it. This is useful to deal with broken
@@ -619,8 +615,12 @@ int manager_read_resolv_conf(Manager *m) {
return 0;
clear:
- while (m->dns_servers)
- dns_server_free(m->dns_servers);
+ while (m->dns_servers) {
+ s = m->dns_servers;
+
+ LIST_REMOVE(servers, m->dns_servers, s);
+ dns_server_unref(s);
+ }
return r;
}
@@ -923,89 +923,6 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
return 1;
}
-static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- DnsTransaction *t = NULL;
- Manager *m = userdata;
- int r;
-
- r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p);
- if (r <= 0)
- return r;
-
- if (dns_packet_validate_reply(p) > 0) {
- t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
- if (!t)
- return 0;
-
- dns_transaction_process_reply(t, p);
-
- } else
- log_debug("Invalid DNS packet.");
-
- return 0;
-}
-
-int manager_dns_ipv4_fd(Manager *m) {
- const int one = 1;
- int r;
-
- assert(m);
-
- if (m->dns_ipv4_fd >= 0)
- return m->dns_ipv4_fd;
-
- m->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->dns_ipv4_fd < 0)
- return -errno;
-
- r = setsockopt(m->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = sd_event_add_io(m->event, &m->dns_ipv4_event_source, m->dns_ipv4_fd, EPOLLIN, on_dns_packet, m);
- if (r < 0)
- goto fail;
-
- return m->dns_ipv4_fd;
-
-fail:
- m->dns_ipv4_fd = safe_close(m->dns_ipv4_fd);
- return r;
-}
-
-int manager_dns_ipv6_fd(Manager *m) {
- const int one = 1;
- int r;
-
- assert(m);
-
- if (m->dns_ipv6_fd >= 0)
- return m->dns_ipv6_fd;
-
- m->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->dns_ipv6_fd < 0)
- return -errno;
-
- r = setsockopt(m->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = sd_event_add_io(m->event, &m->dns_ipv6_event_source, m->dns_ipv6_fd, EPOLLIN, on_dns_packet, m);
- if (r < 0)
- goto fail;
-
- return m->dns_ipv6_fd;
-
-fail:
- m->dns_ipv6_fd = safe_close(m->dns_ipv6_fd);
- return r;
-}
-
static int sendmsg_loop(int fd, struct msghdr *mh, int flags) {
int r;
@@ -1381,15 +1298,25 @@ void manager_verify_all(Manager *m) {
}
void manager_flush_dns_servers(Manager *m, DnsServerType t) {
+ DnsServer *s;
+
assert(m);
if (t == DNS_SERVER_SYSTEM)
- while (m->dns_servers)
- dns_server_free(m->dns_servers);
+ while (m->dns_servers) {
+ s = m->dns_servers;
+
+ LIST_REMOVE(servers, m->dns_servers, s);
+ dns_server_unref(s);
+ }
if (t == DNS_SERVER_FALLBACK)
- while (m->fallback_dns_servers)
- dns_server_free(m->fallback_dns_servers);
+ while (m->fallback_dns_servers) {
+ s = m->fallback_dns_servers;
+
+ LIST_REMOVE(servers, m->fallback_dns_servers, s);
+ dns_server_unref(s);
+ }
}
static const char* const support_table[_SUPPORT_MAX] = {
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 4e70a5b500..005f844df2 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -65,12 +65,6 @@ struct Manager {
unsigned n_dns_streams;
/* Unicast dns */
- int dns_ipv4_fd;
- int dns_ipv6_fd;
-
- sd_event_source *dns_ipv4_event_source;
- sd_event_source *dns_ipv6_event_source;
-
LIST_HEAD(DnsServer, dns_servers);
LIST_HEAD(DnsServer, fallback_dns_servers);
DnsServer *current_dns_server;
@@ -128,9 +122,6 @@ uint32_t manager_find_mtu(Manager *m);
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p);
int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret);
-int manager_dns_ipv4_fd(Manager *m);
-int manager_dns_ipv6_fd(Manager *m);
-
int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr);
LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr);