1 /*  Copyright (C) 2014-2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2  *  SPDX-License-Identifier: GPL-3.0-or-later
3  */
4 
5 #pragma once
6 
7 /**
8  * @file selection.h
9  * Provides server selection API (see `kr_server_selection`)
10  * and functions common to both implementations.
11  */
12 
13 #include "lib/cache/api.h"
14 
15 /* After KR_NS_TIMEOUT_ROW_DEAD consecutive timeouts, we consider the upstream IP dead for KR_NS_TIMEOUT_RETRY_INTERVAL ms */
16 #define KR_NS_TIMEOUT_ROW_DEAD 4
17 #define KR_NS_TIMEOUT_RETRY_INTERVAL 1000
18 
19 /**
20  * These errors are to be reported as feedback to server selection.
21  * See `kr_server_selection::error` for more details.
22  */
23 enum kr_selection_error {
24 	KR_SELECTION_OK = 0,
25 
26 	// Network errors
27 	KR_SELECTION_QUERY_TIMEOUT,
28 	KR_SELECTION_TLS_HANDSHAKE_FAILED,
29 	KR_SELECTION_TCP_CONNECT_FAILED,
30 	KR_SELECTION_TCP_CONNECT_TIMEOUT,
31 
32 	// RCODEs
33 	KR_SELECTION_REFUSED,
34 	KR_SELECTION_SERVFAIL,
35 	KR_SELECTION_FORMERR,      /// inside an answer without an OPT record
36 	KR_SELECTION_FORMERR_EDNS, /// with an OPT record
37 	KR_SELECTION_NOTIMPL,
38 	KR_SELECTION_OTHER_RCODE,
39 
40 	// DNS errors
41 	KR_SELECTION_MALFORMED,
42 	/** Name or type mismatch. */
43 	KR_SELECTION_MISMATCHED,
44 	KR_SELECTION_TRUNCATED,
45 	KR_SELECTION_DNSSEC_ERROR,
46 	KR_SELECTION_LAME_DELEGATION,
47 	/** Too long chain, or a cycle. */
48 	KR_SELECTION_BAD_CNAME,
49 
50 	/** Leave this last, as it is used as array size. */
51 	KR_SELECTION_NUMBER_OF_ERRORS
52 };
53 
54 enum kr_transport_protocol {
55 	/** Selected name with no IPv4 address, it has to be resolved first. */
56 	KR_TRANSPORT_RESOLVE_A,
57 	/** Selected name with no IPv6 address, it has to be resolved first. */
58 	KR_TRANSPORT_RESOLVE_AAAA,
59 	KR_TRANSPORT_UDP,
60 	KR_TRANSPORT_TCP,
61 	KR_TRANSPORT_TLS,
62 };
63 
64 /**
65  * Output of the selection algorithm.
66  */
67 struct kr_transport {
68 	knot_dname_t *ns_name; /**< Set to "." for forwarding targets.*/
69 	union inaddr address;
70 	size_t address_len;
71 	enum kr_transport_protocol protocol;
72 	unsigned timeout; /**< Timeout in ms to be set for UDP transmission. */
73 	/** Timeout was capped to a maximum value based on the other candidates
74 	 * when choosing this transport. The timeout therefore can be much lower
75 	 * than what we expect it to be. We basically probe the server for a sudden
76 	 * network change but we expect it to timeout in most cases. We have to keep
77 	 * this in mind when noting the timeout in cache. */
78 	bool timeout_capped;
79 	/** True iff transport was set in worker.c:subreq_finalize,
80 	 * that means it may be different from the one originally chosen one.*/
81 	bool deduplicated;
82 };
83 
84 struct local_state {
85 	int timeouts; /**< Number of timeouts that occurred resolving this query.*/
86 	bool truncated; /**< Query was truncated, switch to TCP. */
87 	/** Force resolution of a new NS name (if possible)
88 	 * Done by selection.c:error in some cases. */
89 	bool force_resolve;
90 	/** Used to work around auths with broken TCP. */
91 	bool force_udp;
92 	void *private; /**< Inner state of the implementation.*/
93 };
94 
95 /**
96  * Specifies a API for selecting transports and giving feedback on the choices.
97  *
98  * The function pointers are to be used throughout resolver when some information about
99  * the transport is obtained. E.g. RTT in `worker.c` or RCODE in `iterate.c`,…
100  */
101 struct kr_server_selection {
102 	bool initialized;
103 	/**
104 	 * Puts a pointer to next transport of @p qry to @p transport .
105 	 *
106 	 * Allocates new kr_transport in request's mempool, chooses transport to be used for this query.
107 	 * Selection may fail, so @p transport can be set to NULL.
108 	 *
109 	 * @param transport to be filled with pointer to the chosen transport or NULL on failure
110 	 */
111 	void (*choose_transport)(struct kr_query *qry,
112 				 struct kr_transport **transport);
113 	/** Report back the RTT of network operation for transport in ms. */
114 	void (*update_rtt)(struct kr_query *qry,
115 			   const struct kr_transport *transport, unsigned rtt);
116 	/** Report back error encountered with the chosen transport. See `enum kr_selection` */
117 	void (*error)(struct kr_query *qry,
118 		      const struct kr_transport *transport,
119 		      enum kr_selection_error error);
120 
121 	struct local_state *local_state;
122 };
123 
124 /**
125  * @brief Initialize the server selection API for @p qry.
126  *
127  * The implementation is to be chosen based on qry->flags.
128  */
129 KR_EXPORT
130 void kr_server_selection_init(struct kr_query *qry);
131 
132 /**
133  * @brief Add forwarding target to request.
134  *
135  * This is exposed to Lua in order to add forwarding targets to request.
136  * These are then shared by all the queries in said request.
137  */
138 KR_EXPORT
139 int kr_forward_add_target(struct kr_request *req, const struct sockaddr *sock);
140 
141 
142 
143 
144 
145 /* Below are internal parts shared by ./selection_{forward,iter}.c */
146 
147 /**
148  * To be held per IP address in the global LMDB cache
149  */
150 struct rtt_state {
151 	int32_t srtt; /**< Smoothed RTT, i.e. an estimate of round-trip time. */
152 	int32_t variance; /**< An estimate of RTT's standard derivation (not variance). */
153 	/** Note: some TCP and TLS failures are also considered as timeouts. */
154 	int32_t consecutive_timeouts;
155 	/** Timestamp of pronouncing this IP bad based on KR_NS_TIMEOUT_ROW_DEAD */
156 	uint64_t dead_since;
157 };
158 
159 /**
160  * @brief To be held per IP address and locally "inside" query.
161  */
162 struct address_state {
163 	/** Used to distinguish old and valid records in local_state; -1 means unusable IP. */
164 	unsigned int generation;
165 	struct rtt_state rtt_state;
166 	knot_dname_t *ns_name;
167 	bool tls_capable : 1;
168 	/* TODO: uncomment these once we actually use this information in selection
169 	bool tcp_waiting : 1;
170 	bool tcp_connected : 1;
171 	*/
172 	int choice_array_index;
173 	int error_count;
174 	bool broken;
175 	int errors[KR_SELECTION_NUMBER_OF_ERRORS];
176 };
177 
178 /**
179  * @brief Array of these is one of inputs for the actual selection algorithm (`select_transport`)
180  */
181 struct choice {
182 	union inaddr address;
183 	size_t address_len;
184 	struct address_state *address_state;
185 	/** used to overwrite the port number;
186 	 * if zero, `select_transport` determines it. */
187 	uint16_t port;
188 };
189 
190 /**
191  * @brief Array of these is description of names to be resolved (i.e. name without some address)
192  */
193 struct to_resolve {
194 	knot_dname_t *name;
195 	/** Either KR_TRANSPORT_RESOLVE_A or KR_TRANSPORT_RESOLVE_AAAA is valid here. */
196 	enum kr_transport_protocol type;
197 };
198 
199 /**
200  * @brief Based on passed choices, choose the next transport.
201  *
202  * Common function to both implementations (iteration and forwarding).
203  * The `*_choose_transport` functions from `selection_*.h` preprocess the input for this one.
204  *
205  * @param choices Options to choose from, see struct above
206  * @param unresolved Array of names that can be resolved (i.e. no A/AAAA record)
207  * @param timeouts Number of timeouts that occurred in this query (used for exponential backoff)
208  * @param mempool Memory context of current request
209  * @param tcp Force TCP as transport protocol
210  * @param[out] choice_index Optionally index of the chosen transport in the @p choices array.
211  * @return Chosen transport (on mempool) or NULL when no choice is viable
212  */
213 struct kr_transport *select_transport(struct choice choices[], int choices_len,
214 				      struct to_resolve unresolved[],
215 				      int unresolved_len, int timeouts,
216 				      struct knot_mm *mempool, bool tcp,
217 				      size_t *choice_index);
218 
219 /**
220  * Common part of RTT feedback mechanism. Notes RTT to global cache.
221  */
222 void update_rtt(struct kr_query *qry, struct address_state *addr_state,
223 		const struct kr_transport *transport, unsigned rtt);
224 
225 /**
226  * Common part of error feedback mechanism.
227  */
228 void error(struct kr_query *qry, struct address_state *addr_state,
229 	   const struct kr_transport *transport,
230 	   enum kr_selection_error sel_error);
231 
232 /**
233  * Get RTT state from cache. Returns `default_rtt_state` on unknown addresses.
234  *
235  * Note that this opens a cache transaction which is usually closed by calling
236  * `put_rtt_state`, i.e. callee is responsible for its closing
237  * (e.g. calling kr_cache_commit).
238  */
239 struct rtt_state get_rtt_state(const uint8_t *ip, size_t len,
240 			       struct kr_cache *cache);
241 
242 int put_rtt_state(const uint8_t *ip, size_t len, struct rtt_state state,
243 		  struct kr_cache *cache);
244 
245 /**
246  * @internal Helper function for conversion between different IP representations.
247  */
248 void bytes_to_ip(uint8_t *bytes, size_t len, uint16_t port, union inaddr *dst);
249 
250 /**
251  * @internal Helper function for conversion between different IP representations.
252  */
253 uint8_t *ip_to_bytes(const union inaddr *src, size_t len);
254 
255 /**
256  * @internal Fetch per-address information from various sources.
257  *
258  * Note that this opens a RO cache transaction; the callee is responsible
259  * for its closing not too long afterwards (e.g. calling kr_cache_commit).
260  */
261 void update_address_state(struct address_state *state, union inaddr *address,
262 			  size_t address_len, struct kr_query *qry);
263