systemd-resolved-rs 0.1.1

A compatibility-oriented reimplementation of systemd-resolved
{
        crate::query_cancel::check()?;
        validate(query, false)?;
        let header = Header::parse(query)?;
        let question = first_question(query)?;
        let config = self.config();
        let query_generation = self.routing_generation.load(Ordering::Acquire);
        let requested_ifindex = ifindex.filter(|value| *value > 0);
        if let Some(ifindex) = ifindex.filter(|value| *value < 0) {
            return Err(LinkError::InvalidIfindex(ifindex).into());
        }
        if config.refuse_record_types.contains(&question.rr_type) {
            let response = wire::refused_for(query)?;
            return Ok((response, dns_response_flags(), requested_ifindex));
        }
        self.counters.transactions.fetch_add(1, Ordering::Relaxed);
        self.counters
            .current_transactions
            .fetch_add(1, Ordering::Relaxed);
        let _active_transaction = ActiveTransaction {
            counter: &self.counters.current_transactions,
        };

        let synthesize = request_flags
            & crate::dbus_resolve1_abi::flags::SD_RESOLVED_NO_SYNTHESIZE
            == 0;
        if synthesize {
            if let Some(response) =
                crate::static_records::answer(config.read_static_records, query)?
            {
                self.counters.local_answers.fetch_add(1, Ordering::Relaxed);
                return Ok((
                    response,
                    synthetic_response_flags(request_flags, query),
                    requested_ifindex,
                ));
            }
            let hosts = self.hosts();
            let records = if mode == QueryMode::Full {
                hosts.lookup(&question)
            } else {
                hosts.lookup_file(&question)
            };
            if let Some(records) = records {
                self.counters.local_answers.fetch_add(1, Ordering::Relaxed);
                return Ok((
                    local_response(query, &records, 0)?,
                    synthetic_response_flags(request_flags, query),
                    requested_ifindex,
                ));
            }
            if mode == QueryMode::Full {
                if dns_name_dont_resolve(question.name.text()) {
                    self.counters.local_answers.fetch_add(1, Ordering::Relaxed);
                    return Ok((
                        wire::authoritative_nxdomain_for(query)?,
                        synthetic_response_flags(request_flags, query),
                        requested_ifindex,
                    ));
                }
            }
        }
        if mode == QueryMode::Full && allow_hook {
            let hook_response =
                crate::hook::resolve(query, unicast_query, Duration::from_secs(30));
            crate::query_cancel::check()?;
            if let Some(response) = hook_response {
                if self.routing_generation.load(Ordering::Acquire) != query_generation {
                    return Err(ResolveError::QueryAborted);
                }
                return Ok((
                    response,
                    hook_response_flags(request_flags, query),
                    requested_ifindex,
                ));
            }
            if crate::mdns::runtime::should_handle_query(query) {
                if !request_protocol_enabled(request_flags, mdns_protocol_mask()) {
                    return Err(ResolveError::NoSuchResourceRecord);
                }
                if !self.multicast_dns_resolve_enabled(ifindex) {
                    return Err(ResolveError::NoSuchResourceRecord);
                }
                let cache_enabled = config.cache && config.multicast_dns_cache_size > 0;
                let response = crate::mdns::runtime::query_raw(
                    query,
                    ifindex,
                    config.query_timeout,
                    cache_enabled
                        && request_flags
                            & crate::dbus_resolve1_abi::flags::SD_RESOLVED_NO_CACHE
                            == 0,
                    cache_enabled,
                    config.multicast_dns_cache_size,
                    request_flags
                        & crate::dbus_resolve1_abi::flags::SD_RESOLVED_NO_NETWORK
                        == 0,
                );
                crate::query_cancel::check()?;
                let response =
                    response.map_err(|error| io::Error::new(io::ErrorKind::Other, error))?;
                if self.routing_generation.load(Ordering::Acquire) != query_generation {
                    return Err(ResolveError::QueryAborted);
                }
                return response
                    .map(|(response, from_cache)| {
                        (
                            response,
                            mdns_response_flags(query, from_cache),
                            requested_ifindex,
                        )
                    })
                    .ok_or(ResolveError::NoSuchResourceRecord);
            }
            if crate::llmnr::runtime::should_handle_query(query, &self.llmnr_hostname()) {
                if !request_protocol_enabled(request_flags, llmnr_protocol_mask()) {
                    if !config.resolve_unicast_single_label
                        && request_flags
                            & crate::dbus_resolve1_abi::flags::SD_RESOLVED_RELAX_SINGLE_LABEL
                            == 0
                    {
                        return Err(ResolveError::NoSuchResourceRecord);
                    }
                } else if !self.llmnr_resolve_enabled(ifindex) {
                    return Err(ResolveError::NoSuchResourceRecord);
                } else {
                    let response = self.llmnr_query_raw(
                    query,
                    ifindex,
                    request_flags
                        & crate::dbus_resolve1_abi::flags::SD_RESOLVED_NO_CACHE
                        != 0,
                    request_flags
                        & crate::dbus_resolve1_abi::flags::SD_RESOLVED_NO_NETWORK
                        == 0,
                    );
                    crate::query_cancel::check()?;
                    if let Some((response, from_cache)) = response? {
                    if self.routing_generation.load(Ordering::Acquire) != query_generation {
                        return Err(ResolveError::QueryAborted);
                    }
                    return Ok((
                        response,
                        llmnr_response_flags(query, from_cache),
                        requested_ifindex,
                    ));
                    }
                }
                if !config.resolve_unicast_single_label
                    && request_flags
                        & crate::dbus_resolve1_abi::flags::SD_RESOLVED_RELAX_SINGLE_LABEL
                        == 0
                {
                    return Err(ResolveError::NoSuchResourceRecord);
    }
}

        }

        let query = unicast_query;
        let header = Header::parse(query)?;
        let question = first_question(query)?;
        if self.routing_generation.load(Ordering::Acquire) != query_generation {
            return Err(ResolveError::QueryAborted);
        }
        let route = route_cache_id(query_generation, ifindex);
        let key = CacheKey {
            name: question.name.canonical_wire().to_vec(),
            rr_type: question.rr_type,
            class: question.class,
            checking_disabled: header.checking_disabled(),
            route,
        };
        let cache_enabled = config.cache;
        let read_cache = cache_enabled
            && request_flags & crate::dbus_resolve1_abi::flags::SD_RESOLVED_NO_CACHE == 0;
        if read_cache {
            if let Some((response, source_ifindex)) = self.cache.get_scoped(&key, header.id, false) {
                self.counters.cache_hits.fetch_add(1, Ordering::Relaxed);
                return Ok((
                    response.clone(),
                    cache_response_flags(&response),
                    source_ifindex,
                ));
            }
            self.counters.cache_misses.fetch_add(1, Ordering::Relaxed);
        }
        let allow_stale = read_cache
            && request_flags & crate::dbus_resolve1_abi::flags::SD_RESOLVED_NO_STALE == 0;
        let stale_response = allow_stale
            .then(|| self.cache.get_scoped(&key, header.id, true))
            .flatten();

        if request_flags & crate::dbus_resolve1_abi::flags::SD_RESOLVED_NO_NETWORK != 0
            || !request_protocol_enabled(
                request_flags,
                crate::dbus_resolve1_abi::flags::SD_RESOLVED_DNS,
            )
        {
            return Err(ResolveError::NoNameServers);
        }

        let global_servers = config.configured_upstreams();
        let fallback_servers = config.configured_fallback_upstreams();
        let scopes = self
            .routing()
            .select(
                question.name.text(),
                ifindex,
                &global_servers,
                &fallback_servers,
                &config.domains,
                &config.dns_delegates,
            )?
            .into_iter()
            .filter(|scope| match scope.kind {
                ScopeKind::Link(ifindex) => self.networkd_link_relevant(ifindex),
                ScopeKind::Global | ScopeKind::Delegate(_) | ScopeKind::Fallback => true,
            })
            .collect::<Vec<_>>();
        if scopes.is_empty() {
            self.counters.failures.fetch_add(1, Ordering::Relaxed);
            return Err(ResolveError::NoNameServers);
        }

        let inflight_key = InflightKey::new(route, query)?;
        loop {
            match self.inflight.begin(inflight_key.clone()) {
                InflightRole::Leader(transaction_leader) => {
                    let attempts = if stale_response.is_some() {
                        1
                    } else {
                        config.attempts
                    };
                    let scope_result = self.query_scopes_with_attempts(&scopes, query, request_flags, attempts);
                    let scope_result = if self.routing_generation.load(Ordering::Acquire)
                        != query_generation
                    {
                        Err(ResolveError::QueryAborted)
                    } else {
                        scope_result
                    };
                    let result = match scope_result {
                        Ok((response, server, scope)) => {
                            let source_ifindex = match scope {
                                ScopeKind::Link(ifindex) => Some(ifindex),
                                ScopeKind::Global
                                | ScopeKind::Delegate(_)
                                | ScopeKind::Fallback => None,
                            };
                            if cache_enabled
                                && (config.cache_from_localhost || !server.ip().is_loopback())
                            {
                                let _ = self.cache.insert_scoped(
                                    key.clone(),
                                    &response,
                                    source_ifindex,
                                    scope.into(),
                                );
                            }
                            Ok((response, source_ifindex))
                        }
                        Err(error @ ResolveError::QueryAborted) => Err(error),
                        Err(error) => {
                            let timed_out = error.is_timeout();
                            if read_cache {
                                let response = stale_response
                                    .clone()
                                    .or_else(|| self.cache.get_scoped(&key, header.id, allow_stale));
                                if let Some((response, source_ifindex)) = response {
                                    self.counters
                                        .cache_hits
                                        .fetch_add(1, Ordering::Relaxed);
                                    if timed_out {
                                        self.counters.timeouts.fetch_add(1, Ordering::Relaxed);
                                        self.counters
                                            .timeouts_served_stale
                                            .fetch_add(1, Ordering::Relaxed);
                                    } else {
                                        self.counters
                                            .failures_served_stale
                                            .fetch_add(1, Ordering::Relaxed);
                                    }
                                    transaction_leader.complete(normalize_shared_response(
                                        &response,
                                        source_ifindex,
                                    ));
                                    return Ok((
                                        response.clone(),
                                        cache_response_flags(&response),
                                        source_ifindex,
                                    ));
                                }
                            }
                            if timed_out {
                                self.counters.timeouts.fetch_add(1, Ordering::Relaxed);
                            }
                            self.counters.failures.fetch_add(1, Ordering::Relaxed);
                            Err(error)
                        }
                    };
                    transaction_leader.complete(
                        result
                            .as_ref()
                            .ok()
                            .and_then(|(response, source_ifindex)| {
                                normalize_shared_response(response, *source_ifindex)
                            }),
                    );
                    return result.map(|(response, source_ifindex)| {
                        let flags = dns_network_response_flags(&response);
                        (response, flags, source_ifindex)
                    });
                }
                InflightRole::Follower(entry) => {
                    if let Some(mut response) = entry.wait()? {
                        wire::rewrite_id(&mut response.packet, header.id)?;
                        let flags = dns_network_response_flags(&response.packet);
                        return Ok((response.packet, flags, response.ifindex));
                    }
                }
            }
        }
    }