rplan.c 7.92 KB
Newer Older
1
/*  Copyright (C) 2014-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
Marek Vavruša's avatar
Marek Vavruša committed
2 3 4 5 6 7 8 9 10 11 12 13

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
14
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
Marek Vavruša's avatar
Marek Vavruša committed
15 16
 */

17
#include <libknot/descriptor.h>
18
#include <libknot/errcode.h>
19 20

#include "lib/rplan.h"
21
#include "lib/resolve.h"
22
#include "lib/cache/api.h"
23
#include "lib/defines.h"
24
#include "lib/layer.h"
25

26
#define VERBOSE_MSG(qry, fmt...) QRVERBOSE(qry, "plan",  fmt)
27 28
#define QUERY_PROVIDES(q, name, cls, type) \
    ((q)->sclass == (cls) && (q)->stype == type && knot_dname_is_equal((q)->sname, name))
29

30 31 32 33 34
void kr_query_set_stale_cb(struct kr_query *qry, kr_stale_cb cb)
{
	qry->stale_cb = cb;
}

35 36 37 38 39 40 41 42 43 44 45 46 47 48
inline static unsigned char chars_or(const unsigned char a, const unsigned char b)
{
	return a | b;
}

/** Bits set to 1 in variable b will be set to zero in variable a. */
inline static unsigned char chars_mask(const unsigned char a, const unsigned char b)
{
	return a & ~b;
}

/** Apply mod(a, b) to every byte a, b from fl1, fl2 and return result in fl1. */
inline static void kr_qflags_mod(struct kr_qflags *fl1, struct kr_qflags fl2,
			unsigned char mod(const unsigned char a, const unsigned char b))
49 50 51 52
{
	if (!fl1) abort();
	union {
		struct kr_qflags flags;
53 54
		/* C99 section 6.5.3.4: sizeof(char) == 1 */
		unsigned char chars[sizeof(struct kr_qflags)];
55 56 57 58
	} tmp1, tmp2;
	/* The compiler should be able to optimize all this into simple ORs. */
	tmp1.flags = *fl1;
	tmp2.flags = fl2;
59 60
	for (size_t i = 0; i < sizeof(struct kr_qflags); ++i) {
		tmp1.chars[i] = mod(tmp1.chars[i], tmp2.chars[i]);
61 62 63
	}
	*fl1 = tmp1.flags;
}
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

/**
 * Set bits from variable fl2 in variable fl1.
 * Bits which are not set in fl2 are not modified in fl1.
 *
 * @param[in,out] fl1
 * @param[in] fl2
 */
void kr_qflags_set(struct kr_qflags *fl1, struct kr_qflags fl2)
{
	kr_qflags_mod(fl1, fl2, chars_or);
}

/**
 * Clear bits from variable fl2 in variable fl1.
 * Bits which are not set in fl2 are not modified in fl1.
 *
 * @param[in,out] fl1
 * @param[in] fl2
 */
84 85
void kr_qflags_clear(struct kr_qflags *fl1, struct kr_qflags fl2)
{
86
	kr_qflags_mod(fl1, fl2, chars_mask);
87
}
88

89
static struct kr_query *query_create(knot_mm_t *pool, const knot_dname_t *name, uint32_t uid)
90 91 92 93 94 95 96
{
	struct kr_query *qry = mm_alloc(pool, sizeof(struct kr_query));
	if (qry == NULL) {
		return NULL;
	}

	memset(qry, 0, sizeof(struct kr_query));
97 98 99 100 101 102
	if (name != NULL) {
		qry->sname = knot_dname_copy(name, pool);
		if (qry->sname == NULL) {
			mm_free(pool, qry);
			return NULL;
		}
103 104
	}

105
	knot_dname_to_lower(qry->sname);
106
	qry->uid = uid;
107 108 109
	return qry;
}

110
static void query_free(knot_mm_t *pool, struct kr_query *qry)
111
{
112
	kr_zonecut_deinit(&qry->zone_cut);
113 114 115 116
	mm_free(pool, qry->sname);
	mm_free(pool, qry);
}

117
int kr_rplan_init(struct kr_rplan *rplan, struct kr_request *request, knot_mm_t *pool)
118
{
119 120 121 122
	if (rplan == NULL) {
		return KNOT_EINVAL;
	}

123 124
	memset(rplan, 0, sizeof(struct kr_rplan));

125
	rplan->pool = pool;
126
	rplan->request = request;
127 128
	array_init(rplan->pending);
	array_init(rplan->resolved);
129
	rplan->next_uid = 0;
130
	return KNOT_EOK;
131 132
}

133
void kr_rplan_deinit(struct kr_rplan *rplan)
134
{
135 136 137 138
	if (rplan == NULL) {
		return;
	}

139 140
	for (size_t i = 0; i < rplan->pending.len; ++i) {
		query_free(rplan->pool, rplan->pending.at[i]);
141
	}
142 143
	for (size_t i = 0; i < rplan->resolved.len; ++i) {
		query_free(rplan->pool, rplan->resolved.at[i]);
144
	}
145 146
	array_clear_mm(rplan->pending, mm_free, rplan->pool);
	array_clear_mm(rplan->resolved, mm_free, rplan->pool);
147 148 149 150
}

bool kr_rplan_empty(struct kr_rplan *rplan)
{
151 152 153 154
	if (rplan == NULL) {
		return true;
	}

155
	return rplan->pending.len == 0;
156 157
}

158 159 160
static struct kr_query *kr_rplan_push_query(struct kr_rplan *rplan,
                                            struct kr_query *parent,
                                            const knot_dname_t *name)
161
{
162
	if (rplan == NULL) {
163 164
		return NULL;
	}
165

166 167 168 169 170 171
	/* Make sure there's enough space */
	int ret = array_reserve_mm(rplan->pending, rplan->pending.len + 1, kr_memreserve, rplan->pool);
	if (ret != 0) {
		return NULL;
	}

172
	struct kr_query *qry = query_create(rplan->pool, name, rplan->next_uid);
173 174 175
	if (qry == NULL) {
		return NULL;
	}
176
	rplan->next_uid += 1;
177
	/* Class and type must be set outside this function. */
178
	qry->flags = rplan->request->options;
179
	qry->parent = parent;
180
	qry->request = rplan->request;
181
	qry->ns.ctx = rplan->request->ctx;
182
	qry->ns.addr[0].ip.sa_family = AF_UNSPEC;
183
	gettimeofday(&qry->timestamp, NULL);
Vitezslav Kriz's avatar
Vitezslav Kriz committed
184 185
	qry->timestamp_mono = kr_now();
	qry->creation_time_mono = parent ? parent->creation_time_mono : qry->timestamp_mono;
186
	kr_zonecut_init(&qry->zone_cut, (const uint8_t *)"", rplan->pool);
187
	qry->reorder = qry->flags.REORDER_RR
188 189
		? knot_wire_get_id(rplan->request->answer->wire)
		: 0;
190 191

	/* When forwarding, keep the nameserver addresses. */
192
	if (parent && parent->flags.FORWARD && qry->flags.FORWARD) {
193 194
		ret = kr_nsrep_copy_set(&qry->ns, &parent->ns);
		if (ret) {
195
			query_free(rplan->pool, qry);
196 197 198 199
			return NULL;
		}
	}

200
	array_push(rplan->pending, qry);
201

202 203 204 205 206 207 208 209 210 211 212 213 214 215
	return qry;
}

struct kr_query *kr_rplan_push_empty(struct kr_rplan *rplan, struct kr_query *parent)
{
	if (rplan == NULL) {
		return NULL;
	}

	struct kr_query *qry = kr_rplan_push_query(rplan, parent, NULL);
	if (qry == NULL) {
		return NULL;
	}

216 217
	WITH_VERBOSE(qry) {
	VERBOSE_MSG(qry, "plan '%s' type '%s'\n", "", "");
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
	}
	return qry;
}

struct kr_query *kr_rplan_push(struct kr_rplan *rplan, struct kr_query *parent,
                               const knot_dname_t *name, uint16_t cls, uint16_t type)
{
	if (rplan == NULL || name == NULL) {
		return NULL;
	}

	struct kr_query *qry = kr_rplan_push_query(rplan, parent, name);
	if (qry == NULL) {
		return NULL;
	}

	qry->sclass = cls;
	qry->stype = type;

237
	WITH_VERBOSE(qry) {
238 239 240
	char name_str[KNOT_DNAME_MAXLEN], type_str[16];
	knot_dname_to_str(name_str, name, sizeof(name_str));
	knot_rrtype_to_string(type, type_str, sizeof(type_str));
241
	VERBOSE_MSG(parent, "plan '%s' type '%s'\n", name_str, type_str);
242
	}
243 244 245 246 247
	return qry;
}

int kr_rplan_pop(struct kr_rplan *rplan, struct kr_query *qry)
{
248 249 250 251
	if (rplan == NULL || qry == NULL) {
		return KNOT_EINVAL;
	}

252 253 254 255 256 257 258
	/* Make sure there's enough space */
	int ret = array_reserve_mm(rplan->resolved, rplan->resolved.len + 1, kr_memreserve, rplan->pool);
	if (ret != 0) {
		return ret;
	}

	/* Find the query, it will likely be on top */
259 260 261
	for (size_t i = rplan->pending.len; i > 0; i--) {
		if (rplan->pending.at[i - 1] == qry) {
			array_del(rplan->pending, i - 1);
262 263 264 265
			array_push(rplan->resolved, qry);
			break;
		}
	}
266
	return KNOT_EOK;
267 268
}

269 270
bool kr_rplan_satisfies(struct kr_query *closure, const knot_dname_t *name, uint16_t cls, uint16_t type)
{
271
	while (name && closure) {
272 273 274 275 276 277 278
		if (QUERY_PROVIDES(closure, name, cls, type)) {
			return true;
		}
		closure = closure->parent;
	}
	return false;
}
279 280 281

struct kr_query *kr_rplan_resolved(struct kr_rplan *rplan)
{
282
	if (rplan->resolved.len == 0) {
283 284
		return NULL;
	}
285
	return array_tail(rplan->resolved);
286 287
}

288 289 290 291 292 293 294 295 296
struct kr_query *kr_rplan_last(struct kr_rplan *rplan)
{
	if (!kr_rplan_empty(rplan)) {
		return array_tail(rplan->pending);
	}

	return kr_rplan_resolved(rplan);
}

Grigorii Demidov's avatar
Grigorii Demidov committed
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
struct kr_query *kr_rplan_find_resolved(struct kr_rplan *rplan, struct kr_query *parent,
                               const knot_dname_t *name, uint16_t cls, uint16_t type)
{
	struct kr_query *ret = NULL;
	for (int i = 0; i < rplan->resolved.len; ++i) {
		struct kr_query *q = rplan->resolved.at[i];
		if (q->stype == type && q->sclass == cls &&
		    (parent == NULL || q->parent == parent) &&
		    knot_dname_is_equal(q->sname, name)) {
			ret = q;
			break;
		}
	}
	return ret;
}

313
#undef VERBOSE_MSG