summaryrefslogtreecommitdiff
path: root/src/libknot/nameserver/name-server.h
blob: 58ea212b9cc6a3722974c846a8c41eb3b444b658 (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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/*!
 * \file name-server.h
 *
 * \author Lubos Slovak <lubos.slovak@nic.cz>
 *
 * Contains the "name server" structure and interface for the main DNS
 * functions. Currently only supports answering simple queries, without any
 * extensions.
 *
 * \todo Consider saving pointer to the zdb_find_name() function in the
 *       nameserver structure. Probably not needed, these modules can be
 *       inter-connected.
 * \todo Provide interface for other DNS functions - zone transfers, dynamic
 *       updates, etc.
 *
 * \addtogroup query_processing
 * @{
 */
/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    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
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _KNOT_NAME_SERVER_H_
#define _KNOT_NAME_SERVER_H_

#include <stdint.h>
#include <string.h>
#include <sys/time.h>

#include "zone/zonedb.h"
#include "edns.h"
#include "consts.h"
#include "tsig.h"
#include "packet/packet.h"
#include "common/sockaddr.h"
#include "common/lists.h"
#include "updates/changesets.h"

struct conf_t;
struct server_t;

/*----------------------------------------------------------------------------*/
/*!
 * \brief Name server structure. Holds all important data needed for the
 *        supported DNS functions.
 *
 * Currently only holds pointer to the zone database for answering queries.
 */
typedef struct knot_nameserver {
	/*!
	 * \brief Pointer to the zone database structure used for answering
	 *        queries.
	 */
	knot_zonedb_t *zone_db;
	uint8_t *err_response;    /*!< Prepared generic error response. */
	size_t err_resp_size;     /*!< Size of the prepared error response. */
	knot_opt_rr_t *opt_rr;  /*!< OPT RR with the server's EDNS0 info. */

	const char *identity; //!< RFC 4892, server identity (id.server).
	const char *version;  //!< RFC 4892, server version (version.server).
	const char *hostname; //!< RFC 4892, server host name (hostname.server).

	void *data;
} knot_nameserver_t;

/*! \brief Callback for sending one packet back through a TCP connection. */
typedef int (*xfr_callback_t)(int session, sockaddr_t *addr,
			      uint8_t *packet, size_t size);

/*!
 * \brief Single XFR operation structure.
 *
 * Used for communication with XFR handler.
 */
typedef struct knot_ns_xfr {
	node n;
	int type;
	int flags;
	sockaddr_t addr, saddr;
	knot_packet_t *query;
	knot_packet_t *response;
	knot_rcode_t rcode;
	xfr_callback_t send;
	xfr_callback_t recv;
	int session;
	struct timeval t_start, t_end;

	/*!
	 * XFR-out: Output buffer.
	 * XFR-in: Buffer for query or incoming packet.
	 */
	uint8_t *wire;

	/*!
	 * XFR-out: Size of the output buffer.
	 * XFR-in: Size of the current packet.
	 */
	size_t wire_size;
	size_t wire_maxlen;
	void *data;
	knot_zone_t *zone;
	char* zname;
	knot_zone_contents_t *new_contents;
	char *msg;

	/*! \note [TSIG] TSIG fields */
	/*! \brief Message(s) to sign in wireformat.
	 *
	 *  This field should be allocated at the start of transfer and
	 *  freed at the end. During the transfer it is only rewritten.
	 */
	uint8_t *tsig_data;
	size_t tsig_data_size;	/*!< Size of the message(s) in bytes */
	size_t tsig_size;	/*!< Size of the TSIG RR wireformat in bytes.*/
	knot_tsig_key_t *tsig_key; /*!< Associated TSIG key for signing. */

	uint8_t *digest;     /*!< Buffer for counting digest. */
	size_t digest_size;  /*!< Size of the digest. */
	size_t digest_max_size; /*!< Size of the buffer. */

	/*! \note [DDNS] Update forwarding fields. */
	int fwd_src_fd;           /*!< Query originator fd. */
	sockaddr_t fwd_addr;

	uint16_t tsig_rcode;
	uint64_t tsig_prev_time_signed;

	/*!
	 * \brief Number of the packet currently assembled.
	 *
	 * In case of XFR-in, this is not the overall number of packet, just
	 * number counted from last TSIG check.
	 */
	int packet_nr;

	hattrie_t *lookup_tree;
} knot_ns_xfr_t;


static const int KNOT_NS_TSIG_FREQ = 100;

static const size_t KNOT_NS_TSIG_DATA_MAX_SIZE = 100 * 64 * 1024;

/*!
 * \brief XFR request flags.
 */
enum knot_ns_xfr_flag_t {
	XFR_FLAG_TCP = 1 << 0, /*!< XFR request is on TCP. */
	XFR_FLAG_UDP = 1 << 1,  /*!< XFR request is on UDP. */
	XFR_FLAG_AXFR_FINISHED = 1 << 2 /*!< Transfer is finished. */
};

typedef enum knot_ns_transport {
	NS_TRANSPORT_UDP = 1 << 0,
	NS_TRANSPORT_TCP = 1 << 1
} knot_ns_transport_t;

/*!
 * \brief XFR request types.
 */
typedef enum knot_ns_xfr_type_t {
	/* DNS events. */
	XFR_TYPE_AIN = 0, /*!< AXFR-IN request (start transfer). */
	XFR_TYPE_AOUT,    /*!< AXFR-OUT request (incoming transfer). */
	XFR_TYPE_IIN,     /*!< IXFR-IN request (start transfer). */
	XFR_TYPE_IOUT,    /*!< IXFR-OUT request (incoming transfer). */
	XFR_TYPE_SOA,     /*!< Pending SOA request. */
	XFR_TYPE_NOTIFY,  /*!< Pending NOTIFY query. */
	XFR_TYPE_UPDATE,  /*!< UPDATE request (incoming UPDATE). */
	XFR_TYPE_FORWARD  /*!< UPDATE forward request. */
} knot_ns_xfr_type_t;

/*----------------------------------------------------------------------------*/
/*!
 * \brief Allocates and initializes the name server structure.
 *
 * \return Pointer to the name server structure.
 */
knot_nameserver_t *knot_ns_create();

/*! \todo Document me. */
void knot_ns_set_nsid(knot_nameserver_t *nameserver, const char *nsid,
                      size_t nsid_length);

/*!
 * \brief Parses the given query into the response structure and recognizes
 *        type of the query.
 *
 * Some query types are distinguished by OPCODE (NOTIFY, UPDATE, etc.), some
 * by QTYPE (AXFR, IXFR). As these information are needed on the same layer
 * to decide what to do with the query, the knot_query_t type is used for this
 * purpose.
 *
 * \param query_wire Wire format of the query.
 * \param qsize Size of the query in octets.
 * \param packet Packet structure to be filled with the parsed query.
 * \param type Type of the query.
 *
 * \retval KNOT_EOK
 * \retval KNOT_EMALF if the query is totally unusable. Such query must be
 *                    ignored.
 * \retval KNOT_RCODE_SERVFAIL if there was some internal error. Call
 *                               ns_error_response() with \a rcode set to this
 *                               value to get proper error response.
 * \retval KNOT_RCODE_FORMERR if the query was malformed, but can be used to
 *                              construct an error response. Call
 *                              ns_error_response() with \a rcode set to this
 *                              value to get proper error response.
 * \retval KNOT_RCODE_NOTIMPL if the query has an unsupported type. Call
 *                              ns_error_response() with \a rcode set to this
 *                              value to get proper error response.
 */
int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize,
                    knot_packet_t *packet, knot_packet_type_t *type);

int knot_ns_error_response_from_query_wire(const knot_nameserver_t *nameserver,
                                      const uint8_t *query, size_t size,
                                      uint8_t rcode, uint8_t *response_wire,
                                      size_t *rsize);

int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver,
                                      const knot_packet_t *query,
                                      uint8_t rcode, uint8_t *response_wire,
                                      size_t *rsize);

void knot_ns_error_response_full(knot_nameserver_t *nameserver,
                                 knot_packet_t *response, uint8_t rcode,
                                 uint8_t *response_wire, size_t *rsize);

int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
                                 knot_packet_t *query, knot_packet_t **resp,
                                 const knot_zone_t **zone, size_t max_size);

int knot_ns_prep_update_response(knot_nameserver_t *nameserver,
                                 knot_packet_t *query, knot_packet_t **resp,
                                 knot_zone_t **zone, size_t max_size);

/*!
 * \brief Creates a response for the given normal query using the data of the
 *        nameserver.
 *
 * \param nameserver Name server structure to provide the needed data.
 * \param resp Response structure with parsed query.
 * \param response_wire Place for the response in wire format.
 * \param rsize Input: maximum acceptable size of the response. Output: real
 *              size of the response.
 *
 * \retval KNOT_EOK if a valid response was created.
 * \retval KNOT_EMALF if an error occured and the response is not valid.
 */
int knot_ns_answer_normal(knot_nameserver_t *nameserver,
                          const knot_zone_t *zone, knot_packet_t *resp,
                          uint8_t *response_wire, size_t *rsize, int check_any);

int knot_ns_answer_ixfr_udp(knot_nameserver_t *nameserver,
                            const knot_zone_t *zone, knot_packet_t *resp,
                            uint8_t *response_wire, size_t *rsize);

int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);
int knot_ns_init_xfr_resp(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);

/*!
 * \brief Compares two zone serials.
 *
 * \retval < 0 if s1 is less than s2.
 * \retval > 0 if s1 is larger than s2.
 * \retval == 0 if s1 is equal to s2.
 */
int ns_serial_compare(uint32_t s1, uint32_t s2);

int ns_ixfr_load_serials(const knot_ns_xfr_t *xfr, uint32_t *serial_from,
                         uint32_t *serial_to);

int knot_ns_xfr_send_error(const knot_nameserver_t *nameserver,
                           knot_ns_xfr_t *xfr, knot_rcode_t rcode);

/*!
 * \brief Processes an AXFR query.
 *
 * This function sequentially creates DNS packets to be sent as a response
 * to the AXFR query and sends each packet using the given callback (\a
 * send_packet).
 *
 * \param nameserver Name server structure to provide the data for answering.
 * \param xfr Persistent transfer-specific data.
 *
 * \note Currently only a stub which sends one error response using the given
 *       callback.
 *
 * \retval KNOT_EOK
 * \retval KNOT_EINVAL
 * \retval KNOT_ENOMEM
 * \retval KNOT_ERROR
 *
 * \todo Maybe the place for the wire format should be passed in as in
 *       the ns_answer_request() function...?
 */
int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);

/*!
 * \brief Processes an IXFR query.
 *
 * \param nameserver Name server structure to provide the data for answering.
 * \param xfr Persistent transfer-specific data.
 *
 * \todo Document properly.
 */
int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);

/*!
 * \brief Processes an AXFR-IN packet.
 *
 * \param nameserver Name server structure to provide the data for answering.
 * \param xfr Persistent transfer-specific data.
 *
 */
int knot_ns_process_axfrin(knot_nameserver_t *nameserver,
                             knot_ns_xfr_t *xfr);

/*! \todo Document me. */
int knot_ns_switch_zone(knot_nameserver_t *nameserver,
                          knot_ns_xfr_t *xfr);

/*!
 * \brief Processes an IXFR-IN packet.
 *
 * \param nameserver Name server structure to provide the data for answering.
 * \param xfr Persistent transfer-specific data.
 *
 * \retval KNOT_EOK If this packet was processed successfuly and another packet
 *                  is expected. (RFC1995bis, case c)
 * \retval KNOT_ENOXFR If the transfer is not taking place because server's
 *                     SERIAL is the same as this client's SERIAL. The client
 *                     should close the connection and do no further processing.
 *                     (RFC1995bis case a).
 * \retval KNOT_EAGAIN If the server could not fit the transfer into the packet.
 *                     This should happen only if UDP was used. In this case
 *                     the client should retry the request via TCP. If UDP was
 *                     not used, it should be considered that the transfer was
 *                     malformed and the connection should be closed.
 *                     (RFC1995bis case b).
 * \retval >0 Transfer successully finished. Changesets are created and furter
 *            processing is needed.
 * \retval Other If any other error occured. The connection should be closed.
 *
 * \todo Document me.
 */
int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
                             knot_ns_xfr_t *xfr);

int knot_ns_process_update(const knot_packet_t *query,
                           const knot_zone_contents_t *zone,
                           knot_changeset_t *changeset, knot_rcode_t *rcode);

int knot_ns_process_update2(const knot_packet_t *query,
                            knot_zone_contents_t *old_contents,
                            knot_zone_contents_t **new_contents,
                            knot_changesets_t *chgs, knot_rcode_t *rcode);

int knot_ns_create_forward_query(const knot_packet_t *query,
                                 uint8_t *query_wire, size_t *size);

int knot_ns_process_forward_response(const knot_packet_t *response,
                                     uint16_t original_id,
                                     uint8_t *response_wire, size_t *size);

void *knot_ns_data(knot_nameserver_t *nameserver);

void *knot_ns_get_data(knot_nameserver_t *nameserver);

void knot_ns_set_data(knot_nameserver_t *nameserver, void *data);

int knot_ns_tsig_required(int packet_nr);

/*!
 * \brief Converts the response to wire format.
 *
 * \param resp Response to convert.
 * \param wire Place for the wire format of the response.
 * \param wire_size In: space available for the wire format in bytes.
 *                  Out: actual size of the wire format in bytes.
 *
 * \retval KNOT_EOK
 * \retval NS_ERR_SERVFAIL
 */
int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire,
                        size_t *wire_size);

/*!
 * \brief Properly destroys the name server structure.
 *
 * \param nameserver Nameserver to destroy.
 */
void knot_ns_destroy(knot_nameserver_t **nameserver);


#endif /* _KNOTNAME_SERVER_H_ */

/*! @} */