summaryrefslogtreecommitdiff
path: root/usr.sbin/nsd/namedb.h
blob: 3143e867d996cfd0d75e330e5cf4aa1f1757ccd3 (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
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
/*
 * namedb.h -- nsd(8) internal namespace database definitions
 *
 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
 *
 * See LICENSE for the license.
 *
 */

#ifndef NAMEDB_H
#define	NAMEDB_H

#include <stdio.h>

#include "dname.h"
#include "dns.h"
#include "radtree.h"
#include "rbtree.h"
struct zone_options;
struct nsd_options;
struct udb_base;
struct udb_ptr;
struct nsd;
struct zone_ixfr;

typedef union rdata_atom rdata_atom_type;
typedef struct rrset rrset_type;
typedef struct rr rr_type;

/*
 * A domain name table supporting fast insert and search operations.
 */
typedef struct domain_table domain_table_type;
typedef struct domain domain_type;
typedef struct zone zone_type;
typedef struct namedb namedb_type;

struct domain_table
{
	region_type* region;
#ifdef USE_RADIX_TREE
	struct radtree *nametree;
#else
	rbtree_type      *names_to_domains;
#endif
	domain_type* root;
	/* ptr to biggest domain.number and last in list.
	 * the root is the lowest and first in the list. */
	domain_type *numlist_last;
#ifdef NSEC3
	/* the prehash list, start of the list */
	domain_type* prehash_list;
#endif /* NSEC3 */
};

#ifdef NSEC3
typedef struct nsec3_hash_node nsec3_hash_node_type;
struct nsec3_hash_node {
	/* hash value */
	uint8_t hash[NSEC3_HASH_LEN];
	/* entry in the hashtree */
	rbnode_type node;
} ATTR_PACKED;

typedef struct nsec3_hash_wc_node nsec3_hash_wc_node_type;
struct nsec3_hash_wc_node {
	nsec3_hash_node_type hash;
	nsec3_hash_node_type wc;
};

struct nsec3_domain_data {
	/* (if nsec3 chain complete) always the covering nsec3 record */
	domain_type* nsec3_cover;
	/* the nsec3 that covers the wildcard child of this domain. */
	domain_type* nsec3_wcard_child_cover;
	/* for the DS case we must answer on the parent side of zone cut */
	domain_type* nsec3_ds_parent_cover;
	/* NSEC3 domains to prehash, prev and next on the list or cleared */
	domain_type* prehash_prev, *prehash_next;
	/* entry in the nsec3tree (for NSEC3s in the chain in use) */
	rbnode_type nsec3_node;

	/* node for the precompiled domain and the precompiled wildcard */
	nsec3_hash_wc_node_type* hash_wc;

	/* node for the precompiled parent ds */
	nsec3_hash_node_type* ds_parent_hash;

	/* if the domain has an NSEC3 for it, use cover ptr to get it. */
	unsigned     nsec3_is_exact : 1;
	/* same but on parent side */
	unsigned     nsec3_ds_parent_is_exact : 1;
} ATTR_PACKED;
#endif /* NSEC3 */

struct domain
{
#ifdef USE_RADIX_TREE
	struct radnode* rnode;
	const dname_type* dname;
#else
	rbnode_type     node;
#endif
	domain_type* parent;
	domain_type* wildcard_child_closest_match;
	rrset_type* rrsets;
#ifdef NSEC3
	struct nsec3_domain_data* nsec3;
#endif
	/* double-linked list sorted by domain.number */
	domain_type* numlist_prev, *numlist_next;
	uint32_t     number; /* Unique domain name number.  */
	uint32_t     usage; /* number of ptrs to this from RRs(in rdata) and
			     from zone-apex pointers, also the root has one
			     more to make sure it cannot be deleted. */

	/*
	 * This domain name exists (see wildcard clarification draft).
	 */
	unsigned     is_existing : 1;
	unsigned     is_apex : 1;
} ATTR_PACKED;

struct zone
{
	struct radnode *node; /* this entry in zonetree */
	domain_type* apex;
	rrset_type*  soa_rrset;
	rrset_type*  soa_nx_rrset; /* see bug #103 */
	rrset_type*  ns_rrset;
#ifdef NSEC3
	rr_type* nsec3_param; /* NSEC3PARAM RR of chain in use or NULL */
	domain_type* nsec3_last; /* last domain with nsec3, wraps */
	/* in these trees, the root contains an elem ptr to the radtree* */
	rbtree_type* nsec3tree; /* tree with relevant NSEC3 domains */
	rbtree_type* hashtree; /* tree, hashed NSEC3precompiled domains */
	rbtree_type* wchashtree; /* tree, wildcard hashed domains */
	rbtree_type* dshashtree; /* tree, ds-parent-hash domains */
#endif
	struct zone_options* opts;
	struct zone_ixfr* ixfr;
	char*        filename; /* set if read from file, which file */
	char*        logstr; /* set for zone xfer, the log string */
	struct timespec mtime; /* time of last modification */
	unsigned     zonestatid; /* array index for zone stats */
	unsigned     is_secure : 1; /* zone uses DNSSEC */
	unsigned     is_ok : 1; /* zone has not expired */
	unsigned     is_changed : 1; /* zone changes must be written to disk */
	unsigned     is_updated : 1; /* zone was changed by XFR */
	unsigned     is_skipped : 1; /* subsequent zone updates are skipped */
	unsigned     is_checked : 1; /* zone already verified */
	unsigned     is_bad : 1; /* zone failed verification */
} ATTR_PACKED;

/* a RR in DNS */
struct rr {
	domain_type*     owner;
	rdata_atom_type* rdatas;
	uint32_t         ttl;
	uint16_t         type;
	uint16_t         klass;
	uint16_t         rdata_count;
} ATTR_PACKED;

/*
 * An RRset consists of at least one RR.  All RRs are from the same
 * zone.
 */
struct rrset
{
	rrset_type* next;
	zone_type*  zone;
	rr_type*    rrs;
	uint16_t    rr_count;
} ATTR_PACKED;

/*
 * The field used is based on the wireformat the atom is stored in.
 * The allowed wireformats are defined by the rdata_wireformat_type
 * enumeration.
 */
union rdata_atom
{
	/* RDATA_WF_COMPRESSED_DNAME, RDATA_WF_UNCOMPRESSED_DNAME */
	domain_type* domain;

	/* Default. */
	uint16_t*    data;
};

/*
 * Create a new domain_table containing only the root domain.
 */
domain_table_type *domain_table_create(region_type *region);

/*
 * Search the domain table for a match and the closest encloser.
 */
int domain_table_search(domain_table_type* table,
			const dname_type* dname,
			domain_type      **closest_match,
			domain_type      **closest_encloser);

/*
 * The number of domains stored in the table (minimum is one for the
 * root domain).
 */
static inline uint32_t
domain_table_count(domain_table_type* table)
{
#ifdef USE_RADIX_TREE
	return table->nametree->count;
#else
	return table->names_to_domains->count;
#endif
}

/*
 * Find the specified dname in the domain_table.  NULL is returned if
 * there is no exact match.
 */
domain_type* domain_table_find(domain_table_type* table,
			       const dname_type* dname);

/*
 * Insert a domain name in the domain table.  If the domain name is
 * not yet present in the table it is copied and a new dname_info node
 * is created (as well as for the missing parent domain names, if
 * any).  Otherwise the domain_type that is already in the
 * domain_table is returned.
 */
domain_type *domain_table_insert(domain_table_type *table,
				 const dname_type  *dname);

/* put domain into nsec3 hash space tree */
void zone_add_domain_in_hash_tree(region_type* region, rbtree_type** tree,
	int (*cmpf)(const void*, const void*), domain_type* domain,
	rbnode_type* node);
void zone_del_domain_in_hash_tree(rbtree_type* tree, rbnode_type* node);
void hash_tree_delete(region_type* region, rbtree_type* tree);
void prehash_clear(domain_table_type* table);
void prehash_add(domain_table_type* table, domain_type* domain);
void prehash_del(domain_table_type* table, domain_type* domain);
int domain_is_prehash(domain_table_type* table, domain_type* domain);

/*
 * Add an RRset to the specified domain.  Updates the is_existing flag
 * as required.
 */
void domain_add_rrset(domain_type* domain, rrset_type* rrset);

rrset_type* domain_find_rrset(domain_type* domain, zone_type* zone, uint16_t type);
rrset_type* domain_find_any_rrset(domain_type* domain, zone_type* zone);

zone_type* domain_find_zone(namedb_type* db, domain_type* domain);
zone_type* domain_find_parent_zone(namedb_type* db, zone_type* zone);

domain_type* domain_find_ns_rrsets(domain_type* domain, zone_type* zone, rrset_type **ns);
/* find DNAME rrset in domain->parent or higher and return that domain */
domain_type * find_dname_above(domain_type* domain, zone_type* zone);

int domain_is_glue(domain_type* domain, zone_type* zone);

rrset_type* domain_find_non_cname_rrset(domain_type* domain, zone_type* zone);

domain_type* domain_wildcard_child(domain_type* domain);
domain_type *domain_previous_existing_child(domain_type* domain);

int zone_is_secure(zone_type* zone);

static inline dname_type *
domain_dname(domain_type* domain)
{
#ifdef USE_RADIX_TREE
	return (dname_type *) domain->dname;
#else
	return (dname_type *) domain->node.key;
#endif
}

static inline const dname_type *
domain_dname_const(const domain_type* domain)
{
#ifdef USE_RADIX_TREE
	return domain->dname;
#else
	return (const dname_type *) domain->node.key;
#endif
}

static inline domain_type *
domain_previous(domain_type* domain)
{
#ifdef USE_RADIX_TREE
	struct radnode* prev = radix_prev(domain->rnode);
	return prev == NULL ? NULL : (domain_type*)prev->elem;
#else
	rbnode_type *prev = rbtree_previous((rbnode_type *) domain);
	return prev == RBTREE_NULL ? NULL : (domain_type *) prev;
#endif
}

static inline domain_type *
domain_next(domain_type* domain)
{
#ifdef USE_RADIX_TREE
	struct radnode* next = radix_next(domain->rnode);
	return next == NULL ? NULL : (domain_type*)next->elem;
#else
	rbnode_type *next = rbtree_next((rbnode_type *) domain);
	return next == RBTREE_NULL ? NULL : (domain_type *) next;
#endif
}

/* easy comparison for subdomain, true if d1 is subdomain of d2. */
static inline int domain_is_subdomain(domain_type* d1, domain_type* d2)
{ return dname_is_subdomain(domain_dname(d1), domain_dname(d2)); }
/* easy printout, to static buffer of dname_to_string, fqdn. */
static inline const char* domain_to_string(domain_type* domain)
{ return dname_to_string(domain_dname(domain), NULL); }

/*
 * The type covered by the signature in the specified RRSIG RR.
 */
uint16_t rr_rrsig_type_covered(rr_type* rr);

struct namedb
{
	region_type*       region;
	domain_table_type* domains;
	struct radtree*    zonetree;
	struct udb_base*   udb;
	/* the timestamp on the ixfr.db file */
	struct timeval	  diff_timestamp;
	/* if diff_skip=1, diff_pos contains the nsd.diff place to continue */
	uint8_t		  diff_skip;
	off_t		  diff_pos;
};

static inline int rdata_atom_is_domain(uint16_t type, size_t index);
static inline int rdata_atom_is_literal_domain(uint16_t type, size_t index);

static inline domain_type *
rdata_atom_domain(rdata_atom_type atom)
{
	return atom.domain;
}

static inline uint16_t
rdata_atom_size(rdata_atom_type atom)
{
	return *atom.data;
}

static inline uint8_t *
rdata_atom_data(rdata_atom_type atom)
{
	return (uint8_t *) (atom.data + 1);
}


/* Find the zone for the specified dname in DB. */
zone_type *namedb_find_zone(namedb_type *db, const dname_type *dname);
/*
 * Delete a domain name from the domain table.  Removes dname_info node.
 * Only deletes if usage is 0, has no rrsets and no children.  Checks parents
 * for deletion as well.  Adjusts numberlist(domain.number), and 
 * wcard_child closest match.
 */
void domain_table_deldomain(namedb_type* db, domain_type* domain);


/** dbcreate.c */
int udb_write_rr(struct udb_base* udb, struct udb_ptr* z, rr_type* rr);
void udb_del_rr(struct udb_base* udb, struct udb_ptr* z, rr_type* rr);
int write_zone_to_udb(struct udb_base* udb, zone_type* zone,
	struct timespec* mtime, const char* file_str);
int print_rrs(FILE* out, struct zone* zone);
/** marshal rdata into buffer, must be MAX_RDLENGTH in size */
size_t rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz);
/* dbaccess.c */
int namedb_lookup (struct namedb* db,
		   const dname_type* dname,
		   domain_type     **closest_match,
		   domain_type     **closest_encloser);
/* pass number of children (to alloc in dirty array */
struct namedb *namedb_open(const char *filename, struct nsd_options* opt);
void namedb_close_udb(struct namedb* db);
void namedb_close(struct namedb* db);
/* free ixfr data stored for zones */
void namedb_free_ixfr(struct namedb* db);
void namedb_check_zonefiles(struct nsd* nsd, struct nsd_options* opt,
	struct udb_base* taskudb, struct udb_ptr* last_task);
void namedb_check_zonefile(struct nsd* nsd, struct udb_base* taskudb,
	struct udb_ptr* last_task, struct zone_options* zo);
/** zone one zonefile into memory and revert on parse error, write to udb */
void namedb_read_zonefile(struct nsd* nsd, struct zone* zone,
	struct udb_base* taskudb, struct udb_ptr* last_task);
zone_type* namedb_zone_create(namedb_type* db, const dname_type* dname,
        struct zone_options* zopt);
void namedb_zone_delete(namedb_type* db, zone_type* zone);
void namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt);
void namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options);
int create_dirs(const char* path);
int file_get_mtime(const char* file, struct timespec* mtime, int* nonexist);
void allocate_domain_nsec3(domain_table_type *table, domain_type *result);

static inline int
rdata_atom_is_domain(uint16_t type, size_t index)
{
	const rrtype_descriptor_type *descriptor
		= rrtype_descriptor_by_type(type);
	return (index < descriptor->maximum
		&& (descriptor->wireformat[index] == RDATA_WF_COMPRESSED_DNAME
		    || descriptor->wireformat[index] == RDATA_WF_UNCOMPRESSED_DNAME));
}

static inline int
rdata_atom_is_literal_domain(uint16_t type, size_t index)
{
	const rrtype_descriptor_type *descriptor
		= rrtype_descriptor_by_type(type);
	return (index < descriptor->maximum
		&& (descriptor->wireformat[index] == RDATA_WF_LITERAL_DNAME));
}

static inline rdata_wireformat_type
rdata_atom_wireformat_type(uint16_t type, size_t index)
{
	const rrtype_descriptor_type *descriptor
		= rrtype_descriptor_by_type(type);
	assert(index < descriptor->maximum);
	return (rdata_wireformat_type) descriptor->wireformat[index];
}

static inline uint16_t
rrset_rrtype(rrset_type* rrset)
{
	assert(rrset);
	assert(rrset->rr_count > 0);
	return rrset->rrs[0].type;
}

static inline uint16_t
rrset_rrclass(rrset_type* rrset)
{
	assert(rrset);
	assert(rrset->rr_count > 0);
	return rrset->rrs[0].klass;
}

/*
 * zone_rr_iter can be used to iterate over all RRs in a given zone. the
 * SOA RRSET is guaranteed to be returned first.
 */
typedef struct zone_rr_iter zone_rr_iter_type;

struct zone_rr_iter {
	zone_type *zone;
	domain_type *domain;
	rrset_type *rrset;
	ssize_t index;
};

void zone_rr_iter_init(zone_rr_iter_type *iter, zone_type *zone);

rr_type *zone_rr_iter_next(zone_rr_iter_type *iter);

#endif /* NAMEDB_H */