SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SOURCE, ESRCH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_STUB_LOOP, ELOOP),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DNSSD_SERVICE, ENOENT),
SD_BUS_ERROR_MAP(BUS_ERROR_DNSSD_SERVICE_EXISTS, EEXIST),
#define BUS_ERROR_LINK_BUSY "org.freedesktop.resolve1.LinkBusy"
#define BUS_ERROR_NETWORK_DOWN "org.freedesktop.resolve1.NetworkDown"
#define BUS_ERROR_NO_SOURCE "org.freedesktop.resolve1.NoSource"
+#define BUS_ERROR_STUB_LOOP "org.freedesktop.resolve1.StubLoop"
#define BUS_ERROR_NO_SUCH_DNSSD_SERVICE "org.freedesktop.resolve1.NoSuchDnssdService"
#define BUS_ERROR_DNSSD_SERVICE_EXISTS "org.freedesktop.resolve1.DnssdServiceExists"
#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError."
case DNS_TRANSACTION_NO_SOURCE:
return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SOURCE, "All suitable resolution sources turned off");
+ case DNS_TRANSACTION_STUB_LOOP:
+ return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_STUB_LOOP, "Configured DNS server loops back to us");
+
case DNS_TRANSACTION_RCODE_FAILURE: {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
return mfree(p);
}
+uint16_t dns_stub_listener_extra_port(DnsStubListenerExtra *p) {
+ assert(p);
+
+ if (p->port > 0)
+ return p->port;
+
+ return 53;
+}
+
static int dns_stub_collect_answer_by_question(
DnsAnswer **reply,
DnsAnswer *answer,
case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
case DNS_TRANSACTION_NETWORK_DOWN:
case DNS_TRANSACTION_NO_SOURCE:
+ case DNS_TRANSACTION_STUB_LOOP:
(void) dns_stub_send_reply(q, DNS_RCODE_SERVFAIL);
break;
if (l->family == AF_INET)
sa = (union sockaddr_union) {
.in.sin_family = l->family,
- .in.sin_port = htobe16(l->port != 0 ? l->port : 53U),
+ .in.sin_port = htobe16(dns_stub_listener_extra_port(l)),
.in.sin_addr = l->address.in,
};
else
sa = (union sockaddr_union) {
.in6.sin6_family = l->family,
- .in6.sin6_port = htobe16(l->port != 0 ? l->port : 53U),
+ .in6.sin6_port = htobe16(dns_stub_listener_extra_port(l)),
.in6.sin6_addr = l->address.in6,
};
int dns_stub_listener_extra_new(Manager *m, DnsStubListenerExtra **ret);
DnsStubListenerExtra *dns_stub_listener_extra_free(DnsStubListenerExtra *p);
+uint16_t dns_stub_listener_extra_port(DnsStubListenerExtra *p);
void manager_dns_stub_stop(Manager *m);
int manager_dns_stub_start(Manager *m);
if (r < 0)
return r;
+ if (manager_server_is_stub(t->scope->manager, t->server))
+ return -ELOOP;
+
if (!t->bypass) {
if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(dns_transaction_key(t)->type))
return -EOPNOTSUPP;
if (r < 0)
return r;
+ if (manager_server_is_stub(t->scope->manager, t->server))
+ return -ELOOP;
+
if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_UDP || DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level))
return -EAGAIN; /* Sorry, can't do UDP, try TCP! */
if (IN_SET(r, -EMSGSIZE, -EAGAIN))
r = dns_transaction_emit_tcp(t);
}
+ if (r == -ELOOP) {
+ if (t->scope->protocol != DNS_PROTOCOL_DNS)
+ return r;
+
+ /* One of our own stub listeners */
+ log_debug_errno(r, "Detected that specified DNS server is our own extra listener, switching DNS servers.");
+
+ dns_scope_next_dns_server(t->scope);
+
+ if (dns_scope_get_dns_server(t->scope) == t->server) {
+ log_debug_errno(r, "Still pointing to extra listener after switching DNS servers, refusing operation.");
+ dns_transaction_complete(t, DNS_TRANSACTION_STUB_LOOP);
+ return 0;
+ }
+ return dns_transaction_go(t);
+ }
if (r == -ESRCH) {
/* No servers to send this to? */
dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
[DNS_TRANSACTION_NETWORK_DOWN] = "network-down",
[DNS_TRANSACTION_NOT_FOUND] = "not-found",
[DNS_TRANSACTION_NO_SOURCE] = "no-source",
+ [DNS_TRANSACTION_STUB_LOOP] = "stub-loop",
};
DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);
DNS_TRANSACTION_NETWORK_DOWN,
DNS_TRANSACTION_NOT_FOUND, /* like NXDOMAIN, but when LLMNR/TCP connections fail */
DNS_TRANSACTION_NO_SOURCE, /* All suitable DnsTransactionSource turned off */
+ DNS_TRANSACTION_STUB_LOOP,
_DNS_TRANSACTION_STATE_MAX,
_DNS_TRANSACTION_STATE_INVALID = -EINVAL,
};
return tried;
}
+
+bool manager_server_is_stub(Manager *m, DnsServer *s) {
+ DnsStubListenerExtra *l;
+
+ assert(m);
+ assert(s);
+
+ /* Safety check: we generally already skip the main stub when parsing configuration. But let's be
+ * extra careful, and check here again */
+ if (s->family == AF_INET &&
+ s->address.in.s_addr == htobe32(INADDR_DNS_STUB) &&
+ dns_server_port(s) == 53)
+ return true;
+
+ /* Main reason to call this is to check server data against the extra listeners, and filter things
+ * out. */
+ ORDERED_SET_FOREACH(l, m->dns_extra_stub_listeners)
+ if (s->family == l->family &&
+ in_addr_equal(s->family, &s->address, &l->address) &&
+ dns_server_port(s) == dns_stub_listener_extra_port(l))
+ return true;
+
+ return false;
+}
void manager_cleanup_saved_user(Manager *m);
bool manager_next_dnssd_names(Manager *m);
+
+bool manager_server_is_stub(Manager *m, DnsServer *s);
case DNS_TRANSACTION_NO_SOURCE:
return varlink_error(q->varlink_request, "io.systemd.Resolve.NoSource", NULL);
+ case DNS_TRANSACTION_STUB_LOOP:
+ return varlink_error(q->varlink_request, "io.systemd.Resolve.StubLoop", NULL);
+
case DNS_TRANSACTION_NOT_FOUND:
/* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we
* thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */