summaryrefslogtreecommitdiffstats
path: root/lib/rplan.h
blob: 891781fcb29926d6fd4e35ce1a1a63fd6df1b2ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/*  Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
 *  SPDX-License-Identifier: GPL-3.0-or-later
 */

#pragma once

#include <sys/time.h>
#include <libknot/dname.h>
#include <libknot/codes.h>

#include "lib/selection.h"
#include "lib/zonecut.h"

/** Query flags */
struct kr_qflags {
	bool NO_MINIMIZE : 1;    /**< Don't minimize QNAME. */
	bool NO_IPV6 : 1;        /**< Disable IPv6 */
	bool NO_IPV4 : 1;        /**< Disable IPv4 */
	bool TCP : 1;            /**< Use TCP (or TLS) for this query. */
	bool NO_ANSWER : 1;      /**< Do not send any answer to the client.
				  *   Request state should be set to `KR_STATE_FAIL`
				  *   when this flag is set. */
	bool RESOLVED : 1;       /**< Query is resolved.  Note that kr_query gets
				  *   RESOLVED before following a CNAME chain; see .CNAME. */
	bool AWAIT_IPV4 : 1;     /**< Query is waiting for A address. */
	bool AWAIT_IPV6 : 1;     /**< Query is waiting for AAAA address. */
	bool AWAIT_CUT : 1;      /**< Query is waiting for zone cut lookup */
	bool NO_EDNS : 1;        /**< Don't use EDNS. */
	bool CACHED : 1;         /**< Query response is cached. */
	bool NO_CACHE : 1;       /**< No cache for lookup; exception: finding NSs and subqueries. */
	bool EXPIRING : 1;       /**< Query response is cached but expiring.  See is_expiring(). */
	bool ALLOW_LOCAL : 1;    /**< Allow queries to local or private address ranges. */
	bool DNSSEC_WANT : 1;    /**< Want DNSSEC secured answer; exception: +cd,
				  * i.e. knot_wire_get_cd(request->qsource.packet->wire) */
	bool DNSSEC_BOGUS : 1;   /**< Query response is DNSSEC bogus. */
	bool DNSSEC_INSECURE : 1;/**< Query response is DNSSEC insecure. */
	bool DNSSEC_CD : 1;      /**< Instruction to set CD bit in request. */
	bool STUB : 1;           /**< Stub resolution, accept received answer as solved. */
	bool ALWAYS_CUT : 1;     /**< Always recover zone cut (even if cached). */
	bool DNSSEC_WEXPAND : 1; /**< Query response has wildcard expansion. */
	bool PERMISSIVE : 1;     /**< Permissive resolver mode. */
	bool STRICT : 1;         /**< Strict resolver mode. */
	bool BADCOOKIE_AGAIN : 1;/**< Query again because bad cookie returned. */
	bool CNAME : 1;          /**< Query response contains CNAME in answer section. */
	bool REORDER_RR : 1;     /**< Reorder cached RRs. */
	bool TRACE : 1;          /**< Also log answers on debug level. */
	bool NO_0X20 : 1;        /**< Disable query case randomization . */
	bool DNSSEC_NODS : 1;    /**< DS non-existence is proven */
	bool DNSSEC_OPTOUT : 1;  /**< Closest encloser proof has optout */
	bool NONAUTH : 1;        /**< Non-authoritative in-bailiwick records are enough.
				  * TODO: utilize this also outside cache. */
	bool FORWARD : 1;        /**< Forward all queries to upstream; validate answers. */
	bool DNS64_MARK : 1;     /**< Internal mark for dns64 module. */
	bool CACHE_TRIED : 1;    /**< Internal to cache module. */
	bool NO_NS_FOUND : 1;    /**< No valid NS found during last PRODUCE stage. */
	bool PKT_IS_SANE : 1;    /**< Set by iterator in consume phase to indicate whether
				  * some basic aspects of the packet are OK, e.g. QNAME. */
	bool DNS64_DISABLE : 1;  /**< Don't do any DNS64 stuff (meant for view:addr). */
};

/** Combine flags together.  This means set union for simple flags. */
KR_EXPORT
void kr_qflags_set(struct kr_qflags *fl1, struct kr_qflags fl2);

/** Remove flags.  This means set-theoretic difference. */
KR_EXPORT
void kr_qflags_clear(struct kr_qflags *fl1, struct kr_qflags fl2);

/** Callback for serve-stale decisions.
 * @param ttl the expired TTL (i.e. it's < 0)
 * @return the adjusted TTL (typically 1) or < 0.
 */
typedef int32_t (*kr_stale_cb)(int32_t ttl, const knot_dname_t *owner, uint16_t type,
				const struct kr_query *qry);

/**
 * Single query representation.
 */
struct kr_query {
	struct kr_query *parent;
	knot_dname_t *sname; /**< The name to resolve - lower-cased, uncompressed. */
	uint16_t stype;
	uint16_t sclass;
	uint16_t id;
	uint16_t reorder; /**< Seed to reorder (cached) RRs in answer or zero. */
	struct kr_qflags flags;
	struct kr_qflags forward_flags;
	uint32_t secret;
	uint32_t uid; /**< Query iteration number, unique within the kr_rplan. */
	uint64_t creation_time_mono; /* The time of query's creation (milliseconds).
				 * Or time of creation of an oldest
				 * ancestor if it is a subquery. */
	uint64_t timestamp_mono; /**< Time of query created or time of
	                           * query to upstream resolver (milliseconds). */
	struct timeval timestamp; /**< Real time for TTL+DNSSEC checks (.tv_sec only). */
	struct kr_zonecut zone_cut;
	struct kr_layer_pickle *deferred;

	/** Current xNAME depth, set by iterator.  0 = uninitialized, 1 = no CNAME, ...
	 * See also KR_CNAME_CHAIN_LIMIT. */
	int8_t cname_depth;
	/** Pointer to the query that originated this one because of following a CNAME (or NULL). */
	struct kr_query *cname_parent;
	struct kr_request *request; /**< Parent resolution request. */
	kr_stale_cb stale_cb; /**< See the type */
	struct kr_server_selection server_selection;
};

/** @cond internal Array of queries. */
typedef array_t(struct kr_query *) kr_qarray_t;
/* @endcond */

/**
 * Query resolution plan structure.
 *
 * The structure most importantly holds the original query, answer and the
 * list of pending queries required to resolve the original query.
 * It also keeps a notion of current zone cut.
 */
struct kr_rplan {
	kr_qarray_t pending;        /**< List of pending queries.
					Beware: order is significant ATM,
					as the last is the next one to solve,
					and they may be inter-dependent. */
	kr_qarray_t resolved;       /**< List of resolved queries. */
	struct kr_query *initial;   /**< The initial query (also in pending or resolved). */

	struct kr_request *request; /**< Parent resolution request. */
	knot_mm_t *pool;            /**< Temporary memory pool. */
	uint32_t next_uid;          /**< Next value for kr_query::uid (incremental). */
};

/**
 * Initialize resolution plan (empty).
 * @param rplan plan instance
 * @param request resolution request
 * @param pool ephemeral memory pool for whole resolution
 */
KR_EXPORT
int kr_rplan_init(struct kr_rplan *rplan, struct kr_request *request, knot_mm_t *pool);

/**
 * Deinitialize resolution plan, aborting any uncommitted transactions.
 * @param rplan plan instance
 */
KR_EXPORT
void kr_rplan_deinit(struct kr_rplan *rplan);

/**
 * Return true if the resolution plan is empty (i.e. finished or initialized)
 * @param rplan plan instance
 * @return true or false
 */
KR_EXPORT KR_PURE
bool kr_rplan_empty(struct kr_rplan *rplan);

/**
 * Push empty query to the top of the resolution plan.
 * @note This query serves as a cookie query only.
 * @param rplan plan instance
 * @param parent query parent (or NULL)
 * @return query instance or NULL
 */
KR_EXPORT
struct kr_query *kr_rplan_push_empty(struct kr_rplan *rplan,
                                     struct kr_query *parent);

/**
 * Push a query to the top of the resolution plan.
 * @note This means that this query takes precedence before all pending queries.
 * @param rplan plan instance
 * @param parent query parent (or NULL)
 * @param name resolved name
 * @param cls  resolved class
 * @param type resolved type
 * @return query instance or NULL
 */
KR_EXPORT
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);

/**
 * Pop existing query from the resolution plan.
 * @note Popped queries are not discarded, but moved to the resolved list.
 * @param rplan plan instance
 * @param qry resolved query
 * @return 0 or an error
 */
KR_EXPORT
int kr_rplan_pop(struct kr_rplan *rplan, struct kr_query *qry);

/**
 * Return true if resolution chain satisfies given query.
 */
KR_EXPORT KR_PURE
bool kr_rplan_satisfies(struct kr_query *closure, const knot_dname_t *name, uint16_t cls, uint16_t type);

/** Return last resolved query. */
KR_EXPORT KR_PURE
struct kr_query *kr_rplan_resolved(struct kr_rplan *rplan);

/**
  * Return last query (either currently being solved or last resolved).
  * This is necessary to retrieve the last query in case of resolution failures (e.g. time limit reached).
  */
KR_EXPORT KR_PURE
struct kr_query *kr_rplan_last(struct kr_rplan *rplan);


/**
 * Check if a given query already resolved.
 * @param rplan plan instance
 * @param parent query parent (or NULL)
 * @param name resolved name
 * @param cls  resolved class
 * @param type resolved type
 * @return query instance or NULL
 */
KR_EXPORT KR_PURE
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);