diff options
-rw-r--r-- | NEWS | 46 | ||||
-rw-r--r-- | man/systemd.netdev.xml | 2 | ||||
-rw-r--r-- | src/basic/util.c | 7 | ||||
-rw-r--r-- | src/libsystemd/sd-bus/bus-objects.c | 82 | ||||
-rw-r--r-- | src/libsystemd/sd-bus/test-bus-objects.c | 18 | ||||
-rw-r--r-- | src/network/networkd-netdev-gperf.gperf | 2 | ||||
-rw-r--r-- | src/resolve/resolved-dns-scope.c | 57 | ||||
-rw-r--r-- | src/resolve/resolved-dns-scope.h | 3 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.c | 188 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.h | 10 | ||||
-rw-r--r-- | src/resolve/resolved-manager.c | 46 | ||||
-rw-r--r-- | src/resolve/resolved-manager.h | 1 |
12 files changed, 257 insertions, 205 deletions
@@ -13,28 +13,30 @@ CHANGES WITH 223: * sd-dhcp now exposes vendor specific extensions via sd_dhcp_lease_get_vendor_specific(). - * systemd-networkd gained a new boolean configuration option for TAP - devices called 'VnetHeader='. If set, the IFF_VNET_HDR flag is set - for the device, thus allowing to send and receive GSO packets. - - * systemd-networkd gained a new string configuration option to override - the hostname sent to a DHCP server, called 'Hostname='. If set and - 'SendHostname=' is true, networkd will use the configured hostname - instead of the system hostname when sending DHCP requests. - - * systemd-networkd gained a new tunnel configuration option called - 'IPv6FlowLabel='. If set, networkd will configure the IPv6 flow-label - of the tunnel device according to RFC2460. - - * systemd-networkd gained a new tunnel configuration option called - 'CopyDSCP='. If enabled, the DSCP field of ip6 tunnels is copied into - the decapsulated packet. - - * systemd-networkd gained a set of boolean bridge configuration - options. 'UseBPDU=', 'HairPin=', 'FastLeave=', 'AllowPortToBeRoot=', - and 'UnicastFlood=' are now parsed by networkd and applied to the - respective bridge link device via the respective IFLA_BRPORT_* - netlink attribute. + * systemd-networkd gained a number of new configuration options. + + - A new boolean configuration option for TAP devices called + 'VNetHeader='. If set, the IFF_VNET_HDR flag is set for the + device, thus allowing to send and receive GSO packets. + + - A new tunnel configuration option called 'CopyDSCP='. + If enabled, the DSCP field of ip6 tunnels is copied into the + decapsulated packet. + + - A set of boolean bridge configuration options were added. + 'UseBPDU=', 'HairPin=', 'FastLeave=', 'AllowPortToBeRoot=', + and 'UnicastFlood=' are now parsed by networkd and applied to the + respective bridge link device via the respective IFLA_BRPORT_* + netlink attribute. + + - A new string configuration option to override the hostname sent + to a DHCP server, called 'Hostname='. If set and 'SendHostname=' + is true, networkd will use the configured hostname instead of the + system hostname when sending DHCP requests. + + - A new tunnel configuration option called 'IPv6FlowLabel='. If set, + networkd will configure the IPv6 flow-label of the tunnel device + according to RFC2460. * systemd-resolved now implements RFC5452 to improve resilience against cache poisoning. Additionally, source port randomization is enabled diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 6287a00f9d..298cf0eb84 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -586,7 +586,7 @@ </listitem> </varlistentry> <varlistentry> - <term><varname>VnetHeader=</varname></term> + <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) diff --git a/src/basic/util.c b/src/basic/util.c index 7896be8788..1c15fbc172 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -954,7 +954,12 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) { return 0; } -/* https://tools.ietf.org/html/rfc4648#section-6 */ +/* https://tools.ietf.org/html/rfc4648#section-6 + * Notice that base32hex differs from base32 in the alphabet it uses. + * The distinction is that the base32hex representation preserves the + * order of the underlying data when compared as bytestrings, this is + * useful when representing NSEC3 hashes, as one can then verify the + * order of hashes directly from their representation. */ char base32hexchar(int x) { static const char table[32] = "0123456789" "ABCDEFGHIJKLMNOPQRSTUV"; diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index a973bca84c..c25293e5e9 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -173,6 +173,7 @@ static int add_subtree_to_set( sd_bus *bus, const char *prefix, struct node *n, + bool skip_subhierarchies, Set *s, sd_bus_error *error) { @@ -204,11 +205,13 @@ static int add_subtree_to_set( if (r < 0 && r != -EEXIST) return r; - r = add_subtree_to_set(bus, prefix, i, s, error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; + if (!skip_subhierarchies || !i->object_managers) { + r = add_subtree_to_set(bus, prefix, i, skip_subhierarchies, s, error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } } return 0; @@ -218,6 +221,7 @@ static int get_child_nodes( sd_bus *bus, const char *prefix, struct node *n, + bool skip_subhierarchies, Set **_s, sd_bus_error *error) { @@ -233,7 +237,7 @@ static int get_child_nodes( if (!s) return -ENOMEM; - r = add_subtree_to_set(bus, prefix, n, s, error); + r = add_subtree_to_set(bus, prefix, n, skip_subhierarchies, s, error); if (r < 0) { set_free_free(s); return r; @@ -900,7 +904,7 @@ static int process_introspect( assert(n); assert(found_object); - r = get_child_nodes(bus, m->path, n, &s, &error); + r = get_child_nodes(bus, m->path, n, false, &s, &error); if (r < 0) return bus_maybe_reply_error(m, r, &error); if (bus->nodes_modified) @@ -1166,7 +1170,7 @@ static int process_get_managed_objects( if (require_fallback || !n->object_managers) return 0; - r = get_child_nodes(bus, m->path, n, &s, &error); + r = get_child_nodes(bus, m->path, n, true, &s, &error); if (r < 0) return r; if (bus->nodes_modified) @@ -1475,6 +1479,32 @@ void bus_node_gc(sd_bus *b, struct node *n) { free(n); } +static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) { + struct node *n; + + assert(bus); + assert(path); + + n = hashmap_get(bus->nodes, path); + if (!n) { + char *prefix; + + prefix = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + n = hashmap_get(bus->nodes, prefix); + if (n) + break; + } + } + + while (n && !n->object_managers) + n = n->parent; + + if (out) + *out = n; + return !!n; +} + static int bus_add_object( sd_bus *bus, sd_bus_slot **slot, @@ -2277,6 +2307,7 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { BUS_DONT_DESTROY(bus); _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + struct node *object_manager; int r; /* @@ -2297,11 +2328,17 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + do { bus->nodes_modified = false; m = sd_bus_message_unref(m); - r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); if (r < 0) return r; @@ -2440,6 +2477,7 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) { BUS_DONT_DESTROY(bus); _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + struct node *object_manager; int r; /* @@ -2460,11 +2498,17 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) { if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + do { bus->nodes_modified = false; m = sd_bus_message_unref(m); - r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); if (r < 0) return r; @@ -2596,6 +2640,7 @@ _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, ch BUS_DONT_DESTROY(bus); _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + struct node *object_manager; char **i; int r; @@ -2609,11 +2654,17 @@ _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, ch if (strv_isempty(interfaces)) return 0; + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + do { bus->nodes_modified = false; m = sd_bus_message_unref(m); - r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); if (r < 0) return r; @@ -2673,6 +2724,7 @@ _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const c _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + struct node *object_manager; int r; assert_return(bus, -EINVAL); @@ -2685,7 +2737,13 @@ _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, if (strv_isempty(interfaces)) return 0; - r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); if (r < 0) return r; diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c index 1db67ecfac..359984c7f3 100644 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ b/src/libsystemd/sd-bus/test-bus-objects.c @@ -153,7 +153,7 @@ static int notify_test2(sd_bus_message *m, void *userdata, sd_bus_error *error) static int emit_interfaces_added(sd_bus_message *m, void *userdata, sd_bus_error *error) { int r; - assert_se(sd_bus_emit_interfaces_added(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.test", NULL) >= 0); + assert_se(sd_bus_emit_interfaces_added(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0); r = sd_bus_reply_method_return(m, NULL); assert_se(r >= 0); @@ -164,7 +164,7 @@ static int emit_interfaces_added(sd_bus_message *m, void *userdata, sd_bus_error static int emit_interfaces_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { int r; - assert_se(sd_bus_emit_interfaces_removed(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.test", NULL) >= 0); + assert_se(sd_bus_emit_interfaces_removed(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0); r = sd_bus_reply_method_return(m, NULL); assert_se(r >= 0); @@ -175,7 +175,7 @@ static int emit_interfaces_removed(sd_bus_message *m, void *userdata, sd_bus_err static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *error) { int r; - assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), m->path) >= 0); + assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), "/value/a/x") >= 0); r = sd_bus_reply_method_return(m, NULL); assert_se(r >= 0); @@ -186,7 +186,7 @@ static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *er static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { int r; - assert_se(sd_bus_emit_object_removed(sd_bus_message_get_bus(m), m->path) >= 0); + assert_se(sd_bus_emit_object_removed(sd_bus_message_get_bus(m), "/value/a/x") >= 0); r = sd_bus_reply_method_return(m, NULL); assert_se(r >= 0); @@ -228,6 +228,14 @@ static int enumerator_callback(sd_bus *bus, const char *path, void *userdata, ch return 1; } +static int enumerator2_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + + if (object_path_startswith("/value/a", path)) + assert_se(*nodes = strv_new("/value/a/x", "/value/a/y", "/value/a/z", NULL)); + + return 1; +} + static void *server(void *p) { struct context *c = p; sd_bus *bus = NULL; @@ -246,7 +254,9 @@ static void *server(void *p) { assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0); assert_se(sd_bus_add_fallback_vtable(bus, NULL, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0); assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value", enumerator_callback, NULL) >= 0); + assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value/a", enumerator2_callback, NULL) >= 0); assert_se(sd_bus_add_object_manager(bus, NULL, "/value") >= 0); + assert_se(sd_bus_add_object_manager(bus, NULL, "/value/a") >= 0); assert_se(sd_bus_start(bus) >= 0); diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index be76022bc7..fa3ba38a3b 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -61,7 +61,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.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/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 0aab1e35d3..927a1ddc26 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -125,18 +125,17 @@ void dns_scope_next_dns_server(DnsScope *s) { manager_next_dns_server(s->manager); } -int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **server) { - DnsServer *srv = NULL; +int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) { union in_addr_union addr; int ifindex = 0, r; int family; uint16_t port; uint32_t mtu; - int fd; assert(s); assert(p); assert(p->protocol == s->protocol); + assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0)); if (s->link) { mtu = s->link->mtu; @@ -148,28 +147,15 @@ int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **ser if (DNS_PACKET_QDCOUNT(p) > 1) return -EOPNOTSUPP; - srv = dns_scope_get_dns_server(s); - if (!srv) - return -ESRCH; - - family = srv->family; - addr = srv->address; - port = 53; - if (p->size > DNS_PACKET_UNICAST_SIZE_MAX) return -EMSGSIZE; if (p->size + UDP_PACKET_HEADER_SIZE > mtu) return -EMSGSIZE; - if (family == AF_INET) - fd = transaction_dns_ipv4_fd(t); - else if (family == AF_INET6) - fd = transaction_dns_ipv6_fd(t); - else - return -EAFNOSUPPORT; - if (fd < 0) - return fd; + r = manager_write(s->manager, fd, p); + if (r < 0) + return r; } else if (s->protocol == DNS_PROTOCOL_LLMNR) { @@ -192,20 +178,17 @@ int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **ser return -EAFNOSUPPORT; if (fd < 0) return fd; + + r = manager_send(s->manager, fd, ifindex, family, &addr, port, p); + if (r < 0) + return r; } else return -EAFNOSUPPORT; - r = manager_send(s->manager, fd, ifindex, family, &addr, port, 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, DnsServer **server) { +static int dns_scope_socket(DnsScope *s, int type, 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 = {}; @@ -249,13 +232,15 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add return -EAFNOSUPPORT; } - fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + fd = socket(sa.sa.sa_family, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) return -errno; - r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); - if (r < 0) - return -errno; + if (type == SOCK_STREAM) { + r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); + if (r < 0) + return -errno; + } if (s->link) { uint32_t ifindex = htobe32(s->link->ifindex); @@ -298,6 +283,14 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add return ret; } +int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server) { + return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, 53, server); +} + +int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) { + return dns_scope_socket(s, SOCK_STREAM, family, address, port, server); +} + DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) { char **i; @@ -687,7 +680,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata return 0; } - r = dns_scope_emit(scope, NULL, p, NULL); + r = dns_scope_emit(scope, -1, p); 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 21a160ea39..29479ad550 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -65,8 +65,9 @@ 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, DnsTransaction *t, DnsPacket *p, DnsServer **server); +int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p); int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server); +int dns_scope_udp_dns_socket(DnsScope *s, 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-transaction.c b/src/resolve/resolved-dns-transaction.c index 3d46c99df8..b235fda3d2 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -39,10 +39,8 @@ 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); + sd_event_source_unref(t->dns_event_source); + safe_close(t->dns_fd); dns_server_unref(t->server); dns_stream_free(t->stream); @@ -94,7 +92,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) { if (!t) return -ENOMEM; - t->dns_ipv4_fd = t->dns_ipv6_fd = -1; + t->dns_fd = -1; t->question = dns_question_ref(q); @@ -245,7 +243,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; + DnsServer *server = NULL; _cleanup_close_ int fd = -1; int r; @@ -310,6 +308,16 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { return 0; } +static void dns_transaction_next_dns_server(DnsTransaction *t) { + assert(t); + + t->server = dns_server_unref(t->server); + t->dns_event_source = sd_event_source_unref(t->dns_event_source); + t->dns_fd = safe_close(t->dns_fd); + + dns_scope_next_dns_server(t->scope); +} + void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { int r; @@ -342,24 +350,6 @@ 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 the - * one of the DNS server we queried */ - - assert(t->server); - - if (t->server->family != p->family) - return; - - if (!in_addr_equal(p->family, &p->sender, &t->server->address)) - return; - - if (p->sender_port != 53) - return; - } - if (t->received != p) { dns_packet_unref(t->received); t->received = dns_packet_ref(p); @@ -396,7 +386,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { } /* On DNS, couldn't send? Try immediately again, with a new server */ - dns_scope_next_dns_server(t->scope); + dns_transaction_next_dns_server(t); r = dns_transaction_go(t); if (r < 0) { @@ -431,6 +421,56 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { dns_transaction_complete(t, DNS_TRANSACTION_FAILURE); } +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; +} + +static int dns_transaction_emit(DnsTransaction *t) { + int r; + + assert(t); + + if (t->scope->protocol == DNS_PROTOCOL_DNS && !t->server) { + DnsServer *server = NULL; + _cleanup_close_ int fd = -1; + + fd = dns_scope_udp_dns_socket(t->scope, &server); + if (fd < 0) + return fd; + + r = sd_event_add_io(t->scope->manager->event, &t->dns_event_source, fd, EPOLLIN, on_dns_packet, t); + if (r < 0) + return r; + + t->dns_fd = fd; + fd = -1; + t->server = dns_server_ref(server); + } + + r = dns_scope_emit(t->scope, t->dns_fd, t->sent); + if (r < 0) + return r; + + return 0; +} + static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) { DnsTransaction *t = userdata; int r; @@ -439,7 +479,7 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat assert(t); /* Timeout reached? Try again, with a new server */ - dns_scope_next_dns_server(t->scope); + dns_transaction_next_dns_server(t); r = dns_transaction_go(t); if (r < 0) @@ -516,7 +556,6 @@ 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; @@ -596,13 +635,9 @@ 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, t->sent, &server); - if (r >= 0) - t->server = dns_server_ref(server); - else if (r == -EMSGSIZE) + r = dns_transaction_emit(t); + if (r == -EMSGSIZE) r = dns_transaction_open_tcp(t); } if (r == -ESRCH) { @@ -616,7 +651,7 @@ int dns_transaction_go(DnsTransaction *t) { } /* Couldn't send? Try immediately again, with a new server */ - dns_scope_next_dns_server(t->scope); + dns_transaction_next_dns_server(t); return dns_transaction_go(t); } @@ -634,91 +669,6 @@ 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 87f342ca11..a8f4267bc8 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -61,11 +61,8 @@ 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; + int dns_fd; + sd_event_source *dns_event_source; /* the active server */ DnsServer *server; @@ -95,9 +92,6 @@ 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-manager.c b/src/resolve/resolved-manager.c index 17de14bae1..5be01d3cb8 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -912,10 +912,12 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { if (p->ifindex == LOOPBACK_IFINDEX) p->ifindex = 0; - /* If we don't know the interface index still, we look for the - * first local interface with a matching address. Yuck! */ - if (p->ifindex <= 0) - p->ifindex = manager_find_ifindex(m, p->family, &p->destination); + if (protocol != DNS_PROTOCOL_DNS) { + /* If we don't know the interface index still, we look for the + * first local interface with a matching address. Yuck! */ + if (p->ifindex <= 0) + p->ifindex = manager_find_ifindex(m, p->family, &p->destination); + } *ret = p; p = NULL; @@ -947,6 +949,42 @@ static int sendmsg_loop(int fd, struct msghdr *mh, int flags) { } } +static int write_loop(int fd, void *message, size_t length) { + int r; + + assert(fd >= 0); + assert(message); + + for (;;) { + if (write(fd, message, length) >= 0) + return 0; + + if (errno == EINTR) + continue; + + if (errno != EAGAIN) + return -errno; + + r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC); + if (r < 0) + return r; + if (r == 0) + return -ETIMEDOUT; + } +} + +int manager_write(Manager *m, int fd, DnsPacket *p) { + int r; + + log_debug("Sending %s packet with id %u", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p)); + + r = write_loop(fd, DNS_PACKET_DATA(p), p->size); + if (r < 0) + return r; + + return 0; +} + static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_addr *addr, uint16_t port, DnsPacket *p) { union sockaddr_union sa = { .in.sin_family = AF_INET, diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 005f844df2..53b5acb33c 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -119,6 +119,7 @@ void manager_next_dns_server(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_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret); |