resolved-dns-zone.c revision 07630cea1f3a845c09309f197ac7c4f11edd3b62
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "dns-domain.h"
#include "list.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-zone.h"
#include "string-util.h"
/* Never allow more than 1K entries */
#define ZONE_MAX 1024
void dns_zone_item_probe_stop(DnsZoneItem *i) {
DnsTransaction *t;
assert(i);
if (!i->probe_transaction)
return;
t = i->probe_transaction;
i->probe_transaction = NULL;
set_remove(t->zone_items, i);
}
static void dns_zone_item_free(DnsZoneItem *i) {
if (!i)
return;
free(i);
}
assert(z);
if (!i)
return;
if (first)
else
if (first)
else
}
void dns_zone_flush(DnsZone *z) {
DnsZoneItem *i;
assert(z);
while ((i = hashmap_first(z->by_key)))
}
DnsZoneItem *i;
assert(z);
return i;
return NULL;
}
DnsZoneItem *i;
assert(z);
i = dns_zone_get(z, rr);
if (i)
}
static int dns_zone_init(DnsZone *z) {
int r;
assert(z);
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
int r;
if (first) {
} else {
if (r < 0)
return r;
}
if (first) {
} else {
if (r < 0)
return r;
}
return 0;
}
static int dns_zone_item_probe_start(DnsZoneItem *i) {
DnsTransaction *t;
int r;
assert(i);
if (i->probe_transaction)
return 0;
if (!key)
return -ENOMEM;
if (!t) {
if (r < 0)
return r;
}
if (r < 0)
goto gc;
r = set_put(t->zone_items, i);
if (r < 0)
goto gc;
i->probe_transaction = t;
if (t->state == DNS_TRANSACTION_NULL) {
i->block_ready++;
r = dns_transaction_go(t);
i->block_ready--;
if (r < 0) {
return r;
}
}
return 0;
gc:
return r;
}
int r;
assert(z);
assert(s);
return -EINVAL;
return -EINVAL;
if (existing)
return 0;
r = dns_zone_init(z);
if (r < 0)
return r;
if (!i)
return -ENOMEM;
i->scope = s;
i->probing_enabled = probe;
r = dns_zone_link_item(z, i);
if (r < 0)
return r;
if (probe) {
DnsZoneItem *first, *j;
bool established = false;
/* Check if there's already an RR with the same name
* established. If so, it has been probed already, and
* we don't ned to probe again. */
if (i == j)
continue;
if (j->state == DNS_ZONE_ITEM_ESTABLISHED)
established = true;
}
if (established)
else {
i->state = DNS_ZONE_ITEM_PROBING;
r = dns_zone_item_probe_start(i);
if (r < 0) {
i = NULL;
return r;
}
}
} else
i = NULL;
return 0;
}
int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
bool tentative = true;
int r;
assert(z);
assert(q);
if (q->n_keys <= 0) {
*ret_answer = NULL;
if (ret_tentative)
*ret_tentative = false;
return 0;
}
/* First iteration, count what we have */
for (i = 0; i < q->n_keys; i++) {
DnsZoneItem *j, *first;
int k;
/* If this is a generic match, then we have to
* go through the list by the name and look
* for everything manually */
continue;
found = true;
if (k < 0)
return k;
if (k > 0) {
n_answer++;
added = true;
}
}
n_soa++;
} else {
bool found = false;
/* If this is a specific match, then look for
* the right key immediately */
continue;
found = true;
n_answer++;
}
if (!found) {
continue;
n_soa++;
break;
}
}
}
}
*ret_answer = NULL;
if (ret_tentative)
*ret_tentative = false;
return 0;
}
if (n_answer > 0) {
if (!answer)
return -ENOMEM;
}
if (n_soa > 0) {
if (!soa)
return -ENOMEM;
}
/* Second iteration, actually add the RRs to the answers */
for (i = 0; i < q->n_keys; i++) {
DnsZoneItem *j, *first;
int k;
continue;
found = true;
if (j->state != DNS_ZONE_ITEM_PROBING)
tentative = false;
if (k < 0)
return k;
if (k > 0) {
if (r < 0)
return r;
added = true;
}
}
if (r < 0)
return r;
}
} else {
bool found = false;
continue;
found = true;
if (j->state != DNS_ZONE_ITEM_PROBING)
tentative = false;
if (r < 0)
return r;
}
if (!found) {
bool add_soa = false;
continue;
if (j->state != DNS_ZONE_ITEM_PROBING)
tentative = false;
add_soa = true;
}
if (add_soa) {
if (r < 0)
return r;
}
}
}
}
*ret_answer = answer;
if (ret_tentative)
return 1;
}
void dns_zone_item_conflict(DnsZoneItem *i) {
assert(i);
return;
/* Withdraw the conflict item */
i->state = DNS_ZONE_ITEM_WITHDRAWN;
/* Maybe change the hostname */
}
void dns_zone_item_ready(DnsZoneItem *i) {
assert(i);
assert(i->probe_transaction);
if (i->block_ready > 0)
return;
return;
bool we_lost = false;
/* The probe got a successful reply. If we so far
* weren't established we just give up. If we already
* were established, and the peer has the
* lexicographically larger IP address we continue
* and defend it. */
log_debug("Got a successful probe for not yet established RR, we lost.");
we_lost = true;
} else {
we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0;
if (we_lost)
log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost.");
}
if (we_lost) {
return;
}
log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost.");
}
}
static int dns_zone_item_verify(DnsZoneItem *i) {
int r;
assert(i);
if (i->state != DNS_ZONE_ITEM_ESTABLISHED)
return 0;
i->state = DNS_ZONE_ITEM_VERIFYING;
r = dns_zone_item_probe_start(i);
if (r < 0) {
log_error_errno(r, "Failed to start probing for verifying RR: %m");
return r;
}
return 0;
}
DnsZoneItem *i, *first;
int c = 0;
/* This checks whether a response RR we received from somebody
* else is one that we actually thought was uniquely ours. If
* so, we'll verify our RRs. */
/* No conflict if we don't have the name at all. */
if (!first)
return 0;
/* No conflict if we have the exact same RR */
return 0;
/* OK, somebody else has RRs for the same name. Yuck! Let's
* start probing again */
continue;
c++;
}
return c;
}
DnsZoneItem *i, *first;
int c = 0;
/* Somebody else notified us about a possible conflict. Let's
* verify if that's true. */
if (!first)
return 0;
c++;
}
return c;
}
DnsZoneItem *i;
DnsZoneItem *j;
LIST_FOREACH(by_key, j, i)
}
}
DnsZoneItem *i;
int r;
if (!zone)
return;
if (!f)
f = stdout;
DnsZoneItem *j;
LIST_FOREACH(by_key, j, i) {
_cleanup_free_ char *t = NULL;
r = dns_resource_record_to_string(j->rr, &t);
if (r < 0) {
log_oom();
continue;
}
fputc('\t', f);
fputs(t, f);
fputc('\n', f);
}
}
}
if (!zone)
return true;
}