diff options
Diffstat (limited to 'src')
27 files changed, 585 insertions, 316 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index c0863e4167..db1e7f3f37 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -88,6 +88,18 @@ struct boot_times { usec_t generators_finish_time; usec_t unitsload_start_time; usec_t unitsload_finish_time; + + /* + * If we're analyzing the user instance, all timestamps will be offset + * by its own start-up timestamp, which may be arbitrarily big. + * With "plot", this causes arbitrarily wide output SVG files which almost + * completely consist of empty space. Thus we cancel out this offset. + * + * This offset is subtracted from times above by acquire_boot_times(), + * but it still needs to be subtracted from unit-specific timestamps + * (so it is stored here for reference). + */ + usec_t reverse_offset; }; struct unit_times { @@ -188,95 +200,13 @@ static void free_unit_times(struct unit_times *t, unsigned n) { free(t); } -static int acquire_time_data(sd_bus *bus, struct unit_times **out) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - int r, c = 0; - struct unit_times *unit_times = NULL; - size_t size = 0; - UnitInfo u; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits", - &error, &reply, - NULL); - if (r < 0) { - log_error("Failed to list units: %s", bus_error_message(&error, -r)); - goto fail; - } +static void subtract_timestamp(usec_t *a, usec_t b) { + assert(a); - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); - if (r < 0) { - bus_log_parse_error(r); - goto fail; + if (*a > 0) { + assert(*a >= b); + *a -= b; } - - while ((r = bus_parse_unit_info(reply, &u)) > 0) { - struct unit_times *t; - - if (!GREEDY_REALLOC(unit_times, size, c+1)) { - r = log_oom(); - goto fail; - } - - t = unit_times+c; - t->name = NULL; - - assert_cc(sizeof(usec_t) == sizeof(uint64_t)); - - if (bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "InactiveExitTimestampMonotonic", - &t->activating) < 0 || - bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "ActiveEnterTimestampMonotonic", - &t->activated) < 0 || - bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "ActiveExitTimestampMonotonic", - &t->deactivating) < 0 || - bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "InactiveEnterTimestampMonotonic", - &t->deactivated) < 0) { - r = -EIO; - goto fail; - } - - if (t->activated >= t->activating) - t->time = t->activated - t->activating; - else if (t->deactivated >= t->activating) - t->time = t->deactivated - t->activating; - else - t->time = 0; - - if (t->activating == 0) - continue; - - t->name = strdup(u.id); - if (t->name == NULL) { - r = log_oom(); - goto fail; - } - c++; - } - if (r < 0) { - bus_log_parse_error(r); - goto fail; - } - - *out = unit_times; - return c; - -fail: - if (unit_times) - free_unit_times(unit_times, (unsigned) c); - return r; } static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) { @@ -355,10 +285,30 @@ static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) { return -EINPROGRESS; } - if (times.initrd_time) - times.kernel_done_time = times.initrd_time; - else - times.kernel_done_time = times.userspace_time; + if (arg_user) { + /* + * User-instance-specific timestamps processing + * (see comment to reverse_offset in struct boot_times). + */ + times.reverse_offset = times.userspace_time; + + times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0; + subtract_timestamp(×.finish_time, times.reverse_offset); + + subtract_timestamp(×.security_start_time, times.reverse_offset); + subtract_timestamp(×.security_finish_time, times.reverse_offset); + + subtract_timestamp(×.generators_start_time, times.reverse_offset); + subtract_timestamp(×.generators_finish_time, times.reverse_offset); + + subtract_timestamp(×.unitsload_start_time, times.reverse_offset); + subtract_timestamp(×.unitsload_finish_time, times.reverse_offset); + } else { + if (times.initrd_time) + times.kernel_done_time = times.initrd_time; + else + times.kernel_done_time = times.userspace_time; + } cached = true; @@ -378,6 +328,107 @@ static void free_host_info(struct host_info *hi) { free(hi); } +static int acquire_time_data(sd_bus *bus, struct unit_times **out) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + int r, c = 0; + struct boot_times *boot_times = NULL; + struct unit_times *unit_times = NULL; + size_t size = 0; + UnitInfo u; + + r = acquire_boot_times(bus, &boot_times); + if (r < 0) + goto fail; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnits", + &error, &reply, + NULL); + if (r < 0) { + log_error("Failed to list units: %s", bus_error_message(&error, -r)); + goto fail; + } + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) { + bus_log_parse_error(r); + goto fail; + } + + while ((r = bus_parse_unit_info(reply, &u)) > 0) { + struct unit_times *t; + + if (!GREEDY_REALLOC(unit_times, size, c+1)) { + r = log_oom(); + goto fail; + } + + t = unit_times+c; + t->name = NULL; + + assert_cc(sizeof(usec_t) == sizeof(uint64_t)); + + if (bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "InactiveExitTimestampMonotonic", + &t->activating) < 0 || + bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "ActiveEnterTimestampMonotonic", + &t->activated) < 0 || + bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "ActiveExitTimestampMonotonic", + &t->deactivating) < 0 || + bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "InactiveEnterTimestampMonotonic", + &t->deactivated) < 0) { + r = -EIO; + goto fail; + } + + subtract_timestamp(&t->activating, boot_times->reverse_offset); + subtract_timestamp(&t->activated, boot_times->reverse_offset); + subtract_timestamp(&t->deactivating, boot_times->reverse_offset); + subtract_timestamp(&t->deactivated, boot_times->reverse_offset); + + if (t->activated >= t->activating) + t->time = t->activated - t->activating; + else if (t->deactivated >= t->activating) + t->time = t->deactivated - t->activating; + else + t->time = 0; + + if (t->activating == 0) + continue; + + t->name = strdup(u.id); + if (t->name == NULL) { + r = log_oom(); + goto fail; + } + c++; + } + if (r < 0) { + bus_log_parse_error(r); + goto fail; + } + + *out = unit_times; + return c; + +fail: + if (unit_times) + free_unit_times(unit_times, (unsigned) c); + return r; +} + static int acquire_host_info(sd_bus *bus, struct host_info **hi) { int r; struct host_info *host; @@ -450,10 +501,7 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) { size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC)); size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC)); - if (t->kernel_time > 0) - strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC)); - else - strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC)); + strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC)); ptr = strdup(buf); if (!ptr) 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/core/mount.c b/src/core/mount.c index bf8e52bf0e..c0d1cdfbd4 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -834,8 +834,6 @@ static void mount_enter_unmounting(Mount *m) { m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT; r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL); - if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM) - r = exec_command_append(m->control_command, "-n", NULL); if (r < 0) goto fail; @@ -886,8 +884,6 @@ static void mount_enter_mounting(Mount *m) { r = exec_command_set(m->control_command, MOUNT_PATH, m->parameters_fragment.what, m->where, NULL); - if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM) - r = exec_command_append(m->control_command, "-n", NULL); if (r >= 0 && m->sloppy_options) r = exec_command_append(m->control_command, "-s", NULL); if (r >= 0 && m->parameters_fragment.fstype) @@ -934,8 +930,6 @@ static void mount_enter_remounting(Mount *m) { r = exec_command_set(m->control_command, MOUNT_PATH, m->parameters_fragment.what, m->where, "-o", o, NULL); - if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM) - r = exec_command_append(m->control_command, "-n", NULL); if (r >= 0 && m->sloppy_options) r = exec_command_append(m->control_command, "-s", NULL); if (r >= 0 && m->parameters_fragment.fstype) diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index e9a9a020de..50a90b0bac 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -302,12 +302,12 @@ int mac_selinux_unit_access_check_strv( int r; STRV_FOREACH(i, units) { - u = manager_get_unit(m, *i); - if (u) { - r = mac_selinux_unit_access_check(u, message, permission, error); - if (r < 0) - return r; - } + r = manager_load_unit(m, *i, NULL, error, &u); + if (r < 0) + return r; + r = mac_selinux_unit_access_check(u, message, permission, error); + if (r < 0) + return r; } #endif return 0; diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index 9b68935245..1e747abb24 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -248,6 +248,7 @@ static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_ [NL_UNION_LINK_INFO_DATA_VETH] = "veth", [NL_UNION_LINK_INFO_DATA_DUMMY] = "dummy", [NL_UNION_LINK_INFO_DATA_MACVLAN] = "macvlan", + [NL_UNION_LINK_INFO_DATA_MACVTAP] = "macvtap", [NL_UNION_LINK_INFO_DATA_IPVLAN] = "ipvlan", [NL_UNION_LINK_INFO_DATA_VXLAN] = "vxlan", [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = "ipip", @@ -274,6 +275,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[_NL_UNION_LINK_INFO_D .types = rtnl_link_info_data_veth_types }, [NL_UNION_LINK_INFO_DATA_MACVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), .types = rtnl_link_info_data_macvlan_types }, + [NL_UNION_LINK_INFO_DATA_MACVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), + .types = rtnl_link_info_data_macvlan_types }, [NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types), .types = rtnl_link_info_data_ipvlan_types }, [NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types), diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h index a210163241..758ffad1b7 100644 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ b/src/libsystemd/sd-netlink/netlink-types.h @@ -73,6 +73,7 @@ typedef enum NLUnionLinkInfoData { NL_UNION_LINK_INFO_DATA_VETH, NL_UNION_LINK_INFO_DATA_DUMMY, NL_UNION_LINK_INFO_DATA_MACVLAN, + NL_UNION_LINK_INFO_DATA_MACVTAP, NL_UNION_LINK_INFO_DATA_IPVLAN, NL_UNION_LINK_INFO_DATA_VXLAN, NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL, diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index be76022bc7..7e46293a06 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -29,6 +29,7 @@ NetDev.MTUBytes, config_parse_iec_size, 0, NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) VLAN.Id, config_parse_uint64, 0, offsetof(VLan, id) MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) @@ -61,7 +62,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/network/networkd-netdev-macvlan.c b/src/network/networkd-netdev-macvlan.c index c2c564935c..e17de793ce 100644 --- a/src/network/networkd-netdev-macvlan.c +++ b/src/network/networkd-netdev-macvlan.c @@ -35,14 +35,20 @@ DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode); DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode"); static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) { - MacVlan *m = MACVLAN(netdev); + MacVlan *m; int r; assert(netdev); - assert(m); assert(link); assert(netdev->ifname); + if (netdev->kind == NETDEV_KIND_MACVLAN) + m = MACVLAN(netdev); + else + m = MACVTAP(netdev); + + assert(m); + if (m->mode != _NETDEV_MACVLAN_MODE_INVALID) { r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_MODE, m->mode); if (r < 0) @@ -53,14 +59,28 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_net } static void macvlan_init(NetDev *n) { - MacVlan *m = MACVLAN(n); + MacVlan *m; assert(n); + + if (n->kind == NETDEV_KIND_MACVLAN) + m = MACVLAN(n); + else + m = MACVTAP(n); + assert(m); m->mode = _NETDEV_MACVLAN_MODE_INVALID; } +const NetDevVTable macvtap_vtable = { + .object_size = sizeof(MacVlan), + .init = macvlan_init, + .sections = "Match\0NetDev\0MACVTAP\0", + .fill_message_create = netdev_macvlan_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, +}; + const NetDevVTable macvlan_vtable = { .object_size = sizeof(MacVlan), .init = macvlan_init, diff --git a/src/network/networkd-netdev-macvlan.h b/src/network/networkd-netdev-macvlan.h index d61efc16d4..c491bfa312 100644 --- a/src/network/networkd-netdev-macvlan.h +++ b/src/network/networkd-netdev-macvlan.h @@ -41,6 +41,7 @@ struct MacVlan { }; extern const NetDevVTable macvlan_vtable; +extern const NetDevVTable macvtap_vtable; const char *macvlan_mode_to_string(MacVlanMode d) _const_; MacVlanMode macvlan_mode_from_string(const char *d) _pure_; diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index 6949b403c8..cd31387b41 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/networkd-netdev.c @@ -34,6 +34,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_BOND] = &bond_vtable, [NETDEV_KIND_VLAN] = &vlan_vtable, [NETDEV_KIND_MACVLAN] = &macvlan_vtable, + [NETDEV_KIND_MACVTAP] = &macvtap_vtable, [NETDEV_KIND_IPVLAN] = &ipvlan_vtable, [NETDEV_KIND_VXLAN] = &vxlan_vtable, [NETDEV_KIND_IPIP] = &ipip_vtable, @@ -56,6 +57,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { [NETDEV_KIND_BOND] = "bond", [NETDEV_KIND_VLAN] = "vlan", [NETDEV_KIND_MACVLAN] = "macvlan", + [NETDEV_KIND_MACVTAP] = "macvtap", [NETDEV_KIND_IPVLAN] = "ipvlan", [NETDEV_KIND_VXLAN] = "vxlan", [NETDEV_KIND_IPIP] = "ipip", diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h index a004f2fe5f..19fb5bb185 100644 --- a/src/network/networkd-netdev.h +++ b/src/network/networkd-netdev.h @@ -40,6 +40,7 @@ typedef enum NetDevKind { NETDEV_KIND_BOND, NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, + NETDEV_KIND_MACVTAP, NETDEV_KIND_IPVLAN, NETDEV_KIND_VXLAN, NETDEV_KIND_IPIP, @@ -161,6 +162,7 @@ DEFINE_CAST(BRIDGE, Bridge); DEFINE_CAST(BOND, Bond); DEFINE_CAST(VLAN, VLan); DEFINE_CAST(MACVLAN, MacVlan); +DEFINE_CAST(MACVTAP, MacVlan); DEFINE_CAST(IPVLAN, IPVlan); DEFINE_CAST(VXLAN, VxLan); DEFINE_CAST(IPIP, Tunnel); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 83224d7109..8735b39581 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -31,6 +31,7 @@ Network.Bridge, config_parse_netdev, 0 Network.Bond, config_parse_netdev, 0, offsetof(Network, bond) Network.VLAN, config_parse_netdev, 0, 0 Network.MACVLAN, config_parse_netdev, 0, 0 +Network.MACVTAP, config_parse_netdev, 0, 0 Network.IPVLAN, config_parse_netdev, 0, 0 Network.VXLAN, config_parse_netdev, 0, 0 Network.Tunnel, config_parse_tunnel, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index d8f42621af..e3593fc0ea 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -428,6 +428,7 @@ int config_parse_netdev(const char *unit, break; case NETDEV_KIND_VLAN: case NETDEV_KIND_MACVLAN: + case NETDEV_KIND_MACVTAP: case NETDEV_KIND_IPVLAN: case NETDEV_KIND_VXLAN: r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev); diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 649e8b74e1..39951a362c 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -508,23 +508,21 @@ static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t l assert(p); assert(types); + assert(length > 0); saved_size = p->size; - if (length != 0) { - - r = dns_packet_append_uint8(p, window, NULL); - if (r < 0) - goto fail; + r = dns_packet_append_uint8(p, window, NULL); + if (r < 0) + goto fail; - r = dns_packet_append_uint8(p, length, NULL); - if (r < 0) - goto fail; + r = dns_packet_append_uint8(p, length, NULL); + if (r < 0) + goto fail; - r = dns_packet_append_blob(p, types, length, NULL); - if (r < 0) - goto fail; - } + r = dns_packet_append_blob(p, types, length, NULL); + if (r < 0) + goto fail; if (start) *start = saved_size; @@ -538,7 +536,7 @@ fail: static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) { Iterator i; uint8_t window = 0; - uint8_t len = 0; + uint8_t entry = 0; uint8_t bitmaps[32] = {}; unsigned n; size_t saved_size; @@ -550,30 +548,24 @@ static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) { saved_size = p->size; BITMAP_FOREACH(n, types, i) { - uint8_t entry; - assert(n <= 0xffff); - if ((n << 8) != window) { - r = dns_packet_append_type_window(p, window, len, bitmaps, NULL); + if ((n >> 8) != window && bitmaps[entry / 8] != 0) { + r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL); if (r < 0) goto fail; - if (len > 0) { - len = 0; - zero(bitmaps); - } + zero(bitmaps); } - window = n << 8; - len ++; + window = n >> 8; entry = n & 255; bitmaps[entry / 8] |= 1 << (7 - (entry % 8)); } - r = dns_packet_append_type_window(p, window, len, bitmaps, NULL); + r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL); if (r < 0) goto fail; @@ -1164,6 +1156,7 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta uint8_t window; uint8_t length; const uint8_t *bitmap; + uint8_t bit = 0; unsigned i; bool found = false; size_t saved_rindex; @@ -1195,10 +1188,10 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta for (i = 0; i < length; i++) { uint8_t bitmask = 1 << 7; - uint8_t bit = 0; if (!bitmap[i]) { found = false; + bit += 8; continue; } @@ -1673,8 +1666,12 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { if (r < 0) goto fail; - /* NSEC RRs with empty bitmpas makes no sense, but the RFC does not explicitly forbid them - so we allow it */ + /* The types bitmap must contain at least the NSEC record itself, so an empty bitmap means + something went wrong */ + if (bitmap_isclear(rr->nsec.types)) { + r = -EBADMSG; + goto fail; + } break; diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index 4d71f5e3d4..0efe740d1a 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -188,6 +188,46 @@ int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) { return 1; } +int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) { + unsigned j; + int r; + + assert(a); + assert(k); + + for (j = 0; j < a->n_keys; j++) { + r = dns_resource_key_equal(a->keys[j], k); + if (r != 0) + return r; + } + + return 0; +} + +int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) { + unsigned j; + int r; + + assert(a); + assert(b); + + /* Checks if all keys in a are also contained b, and vice versa */ + + for (j = 0; j < a->n_keys; j++) { + r = dns_question_contains(b, a->keys[j]); + if (r <= 0) + return r; + } + + for (j = 0; j < b->n_keys; j++) { + r = dns_question_contains(a, b->keys[j]); + if (r <= 0) + return r; + } + + return 1; +} + int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret) { _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL; bool same = true; diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h index 4ba2fe9f0e..fc98677798 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -43,6 +43,8 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr); int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr); int dns_question_is_valid(DnsQuestion *q); int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other); +int dns_question_contains(DnsQuestion *a, DnsResourceKey *k); +int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b); int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret); 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..8a93b265c6 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) { @@ -416,8 +406,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { } /* 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)) { + r = dns_question_is_equal(p->question, t->question); + if (r <= 0) { dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); return; } @@ -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); diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 20a44ce4e1..8a0dec1540 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -114,6 +114,68 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { return r; } +/* @label_terminal: terminal character of a label, updated to point to the terminal character of + * the previous label (always skipping one dot) or to NULL if there are no more + * labels. */ +int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) { + const char *terminal; + int r; + + assert(name); + assert(label_terminal); + assert(dest); + + /* no more labels */ + if (!*label_terminal) { + if (sz >= 1) + *dest = 0; + + return 0; + } + + assert(**label_terminal == '.' || **label_terminal == 0); + + /* skip current terminal character */ + terminal = *label_terminal - 1; + + /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */ + for (;;) { + if (terminal < name) { + /* reached the first label, so indicate that there are no more */ + terminal = NULL; + break; + } + + /* find the start of the last label */ + if (*terminal == '.') { + const char *y; + unsigned slashes = 0; + + for (y = terminal - 1; y >= name && *y == '\\'; y--) + slashes ++; + + if (slashes % 2 == 0) { + /* the '.' was not escaped */ + name = terminal + 1; + break; + } else { + terminal = y; + continue; + } + } + + terminal --; + } + + r = dns_label_unescape(&name, dest, sz); + if (r < 0) + return r; + + *label_terminal = terminal; + + return r; +} + int dns_label_escape(const char *p, size_t l, char **ret) { _cleanup_free_ char *s = NULL; char *q; @@ -338,20 +400,23 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_ } int dns_name_compare_func(const void *a, const void *b) { - const char *x = a, *y = b; + const char *x, *y; int r, q, k, w; assert(a); assert(b); + x = (const char *) a + strlen(a); + y = (const char *) b + strlen(b); + for (;;) { char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1]; - if (*x == 0 && *y == 0) + if (x == NULL && y == NULL) return 0; - r = dns_label_unescape(&x, la, sizeof(la)); - q = dns_label_unescape(&y, lb, sizeof(lb)); + r = dns_label_unescape_suffix(a, &x, la, sizeof(la)); + q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb)); if (r < 0 || q < 0) return r - q; @@ -464,6 +529,28 @@ int dns_name_endswith(const char *name, const char *suffix) { } } +int dns_name_between(const char *a, const char *b, const char *c) { + int n; + + /* Determine if b is strictly greater than a and strictly smaller than c. + We consider the order of names to be circular, so that if a is + strictly greater than c, we consider b to be between them if it is + either greater than a or smaller than c. This is how the canonical + DNS name order used in NSEC records work. */ + + n = dns_name_compare_func(a, c); + if (n == 0) + return -EINVAL; + else if (n < 0) + /* a<---b--->c */ + return dns_name_compare_func(a, b) < 0 && + dns_name_compare_func(b, c) < 0; + else + /* <--b--c a--b--> */ + return dns_name_compare_func(b, c) < 0 || + dns_name_compare_func(a, b) < 0; +} + int dns_name_reverse(int family, const union in_addr_union *a, char **ret) { const uint8_t *p; int r; diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 00caf5d700..bd50ad3e6d 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -29,6 +29,7 @@ #define DNS_NAME_MAX 255 int dns_label_unescape(const char **name, char *dest, size_t sz); +int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz); int dns_label_escape(const char *p, size_t l, char **ret); int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); @@ -49,6 +50,7 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_ int dns_name_compare_func(const void *a, const void *b); extern const struct hash_ops dns_name_hash_ops; +int dns_name_between(const char *a, const char *b, const char *c); int dns_name_equal(const char *x, const char *y); int dns_name_endswith(const char *name, const char *suffix); diff --git a/src/shared/install.c b/src/shared/install.c index c37cf1948a..3d2b5ae77f 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -2190,6 +2190,7 @@ int unit_file_get_list( _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL; struct dirent *de; _cleanup_free_ char *path = NULL; + bool also = false; errno = 0; de = readdir(d); @@ -2243,7 +2244,7 @@ int unit_file_get_list( if (!path) return -ENOMEM; - r = unit_file_can_install(&paths, root_dir, path, true, NULL); + r = unit_file_can_install(&paths, root_dir, path, true, &also); if (r == -EINVAL || /* Invalid setting? */ r == -EBADMSG || /* Invalid format? */ r == -ENOENT /* Included file not found? */) @@ -2253,7 +2254,7 @@ int unit_file_get_list( else if (r > 0) f->state = UNIT_FILE_DISABLED; else - f->state = UNIT_FILE_STATIC; + f->state = also ? UNIT_FILE_INDIRECT : UNIT_FILE_STATIC; found: r = hashmap_put(h, basename(f->path), f); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index 527cdd3b54..31e110cf0d 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -50,6 +50,46 @@ static void test_dns_label_unescape(void) { test_dns_label_unescape_one("foobar.", "foobar", 20, 6); } +static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) { + char buffer[buffer_sz]; + const char *label; + int r; + + label = what + strlen(what); + + r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz); + assert_se(r == ret1); + if (r >= 0) + assert_se(streq(buffer, expect1)); + + r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz); + assert_se(r == ret2); + if (r >= 0) + assert_se(streq(buffer, expect2)); +} + +static void test_dns_label_unescape_suffix(void) { + test_dns_label_unescape_suffix_one("hallo", "hallo", "", 6, 5, 0); + test_dns_label_unescape_suffix_one("hallo", "hallo", "", 4, -ENOSPC, -ENOSPC); + test_dns_label_unescape_suffix_one("", "", "", 10, 0, 0); + test_dns_label_unescape_suffix_one("hallo\\.foobar", "hallo.foobar", "", 20, 12, 0); + test_dns_label_unescape_suffix_one("hallo.foobar", "foobar", "hallo", 10, 6, 5); + test_dns_label_unescape_suffix_one("hallo.foobar\n", "foobar", "foobar", 20, -EINVAL, -EINVAL); + test_dns_label_unescape_suffix_one("hallo\\", "hallo", "hallo", 20, -EINVAL, -EINVAL); + test_dns_label_unescape_suffix_one("hallo\\032 ", "hallo ", "", 20, 7, 0); + test_dns_label_unescape_suffix_one(".", "", "", 20, 0, 0); + test_dns_label_unescape_suffix_one("..", "", "", 20, 0, 0); + test_dns_label_unescape_suffix_one(".foobar", "foobar", "", 20, 6, -EINVAL); + test_dns_label_unescape_suffix_one("foobar.", "", "foobar", 20, 0, 6); + test_dns_label_unescape_suffix_one("foo\\\\bar", "foo\\bar", "", 20, 7, 0); + test_dns_label_unescape_suffix_one("foo.bar", "bar", "foo", 20, 3, 3); + test_dns_label_unescape_suffix_one("foo..bar", "bar", "", 20, 3, -EINVAL); + test_dns_label_unescape_suffix_one("foo...bar", "bar", "", 20, 3, -EINVAL); + test_dns_label_unescape_suffix_one("foo\\.bar", "foo.bar", "", 20, 7, 0); + test_dns_label_unescape_suffix_one("foo\\\\.bar", "bar", "foo\\", 20, 3, 4); + test_dns_label_unescape_suffix_one("foo\\\\\\.bar", "foo\\.bar", "", 20, 8, 0); +} + static void test_dns_label_escape_one(const char *what, size_t l, const char *expect, int ret) { _cleanup_free_ char *t = NULL; int r; @@ -120,6 +160,38 @@ static void test_dns_name_equal(void) { test_dns_name_equal_one("..", "..", -EINVAL); } +static void test_dns_name_between_one(const char *a, const char *b, const char *c, int ret) { + int r; + + r = dns_name_between(a, b, c); + assert_se(r == ret); + + r = dns_name_between(c, b, a); + if (ret >= 0) + assert_se(r == 0); + else + assert_se(r == ret); +} + +static void test_dns_name_between(void) { + /* see https://tools.ietf.org/html/rfc4034#section-6.1 + Note that we use "\033.z.example" in stead of "\001.z.example" as we + consider the latter invalid */ + test_dns_name_between_one("example", "a.example", "yljkjljk.a.example", true); + test_dns_name_between_one("a.example", "yljkjljk.a.example", "Z.a.example", true); + test_dns_name_between_one("yljkjljk.a.example", "Z.a.example", "zABC.a.EXAMPLE", true); + test_dns_name_between_one("Z.a.example", "zABC.a.EXAMPLE", "z.example", true); + test_dns_name_between_one("zABC.a.EXAMPLE", "z.example", "\\033.z.example", true); + test_dns_name_between_one("z.example", "\\033.z.example", "*.z.example", true); + test_dns_name_between_one("\\033.z.example", "*.z.example", "\\200.z.example", true); + test_dns_name_between_one("*.z.example", "\\200.z.example", "example", true); + test_dns_name_between_one("\\200.z.example", "example", "a.example", true); + + test_dns_name_between_one("example", "a.example", "example", -EINVAL); + test_dns_name_between_one("example", "example", "yljkjljk.a.example", false); + test_dns_name_between_one("example", "yljkjljk.a.example", "yljkjljk.a.example", false); +} + static void test_dns_name_endswith_one(const char *a, const char *b, int ret) { assert_se(dns_name_endswith(a, b) == ret); } @@ -180,10 +252,12 @@ static void test_dns_name_reverse(void) { int main(int argc, char *argv[]) { test_dns_label_unescape(); + test_dns_label_unescape_suffix(); test_dns_label_escape(); test_dns_name_normalize(); test_dns_name_equal(); test_dns_name_endswith(); + test_dns_name_between(); test_dns_name_root(); test_dns_name_single_label(); test_dns_name_reverse(); diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c index fabc653800..4f625251d6 100644 --- a/src/udev/udev-builtin.c +++ b/src/udev/udev-builtin.c @@ -52,7 +52,7 @@ void udev_builtin_init(struct udev *udev) { return; for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i]->init) + if (builtins[i] && builtins[i]->init) builtins[i]->init(udev); initialized = true; @@ -65,7 +65,7 @@ void udev_builtin_exit(struct udev *udev) { return; for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i]->exit) + if (builtins[i] && builtins[i]->exit) builtins[i]->exit(udev); initialized = false; @@ -75,7 +75,7 @@ bool udev_builtin_validate(struct udev *udev) { unsigned int i; for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i]->validate && builtins[i]->validate(udev)) + if (builtins[i] && builtins[i]->validate && builtins[i]->validate(udev)) return true; return false; } @@ -84,14 +84,21 @@ void udev_builtin_list(struct udev *udev) { unsigned int i; for (i = 0; i < ELEMENTSOF(builtins); i++) - fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help); + if (builtins[i]) + fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help); } const char *udev_builtin_name(enum udev_builtin_cmd cmd) { + if (!builtins[cmd]) + return NULL; + return builtins[cmd]->name; } bool udev_builtin_run_once(enum udev_builtin_cmd cmd) { + if (!builtins[cmd]) + return false; + return builtins[cmd]->run_once; } @@ -105,7 +112,7 @@ enum udev_builtin_cmd udev_builtin_lookup(const char *command) { if (pos) pos[0] = '\0'; for (i = 0; i < ELEMENTSOF(builtins); i++) - if (streq(builtins[i]->name, name)) + if (builtins[i] && streq(builtins[i]->name, name)) return i; return UDEV_BUILTIN_MAX; } @@ -115,6 +122,9 @@ int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const c int argc; char *argv[128]; + if (!builtins[cmd]) + return -EOPNOTSUPP; + /* we need '0' here to reset the internal state */ optind = 0; strscpy(arg, sizeof(arg), command); |