xref: /openbsd-src/usr.sbin/nsd/dbcreate.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*
2  * dbcreate.c -- routines to create an nsd(8) name database
3  *
4  * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
5  *
6  * See LICENSE for the license.
7  *
8  */
9 
10 #include <config.h>
11 
12 #include <sys/types.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 
19 #include "namedb.h"
20 
21 static int write_db (namedb_type *db);
22 static int write_number(struct namedb *db, uint32_t number);
23 
24 struct namedb *
25 namedb_new (const char *filename)
26 {
27 	namedb_type *db;
28 	/* Make a new structure... */
29 	if ((db = namedb_create()) == NULL) {
30 		log_msg(LOG_ERR,
31 			"insufficient memory to create database");
32 		return NULL;
33 	}
34 	db->filename = region_strdup(db->region, filename);
35 	db->crc = 0xffffffff;
36 	db->diff_skip = 0;
37 	db->fd = NULL;
38 
39 	if (gettimeofday(&(db->diff_timestamp), NULL) != 0) {
40 		log_msg(LOG_ERR, "unable to load %s: cannot initialize "
41 						 "timestamp", db->filename);
42 		namedb_destroy(db);
43 		return NULL;
44 	}
45 
46 	/*
47 	 * Unlink the old database, if it exists.  This is useful to
48 	 * ensure that NSD doesn't see the changes until a reload is done.
49 	 */
50 	if (unlink(db->filename) == -1 && errno != ENOENT) {
51 		namedb_destroy(db);
52 		return NULL;
53 	}
54 
55 	/* Create the database */
56 	if ((db->fd = fopen(db->filename, "w")) == NULL) {
57 		namedb_destroy(db);
58 		return NULL;
59 	}
60 
61 	if (!write_data_crc(db->fd, NAMEDB_MAGIC, NAMEDB_MAGIC_SIZE, &db->crc)) {
62 		fclose(db->fd);
63 		namedb_discard(db);
64 		return NULL;
65 	}
66 
67 	return db;
68 }
69 
70 
71 int
72 namedb_save (struct namedb *db)
73 {
74 	if (write_db(db) != 0) {
75 		return -1;
76 	}
77 
78 	/* Finish up and write the crc */
79 	if (!write_number(db, ~db->crc)) {
80 		fclose(db->fd);
81 		return -1;
82 	}
83 
84 	/* Write the magic... */
85 	if (!write_data_crc(db->fd, NAMEDB_MAGIC, NAMEDB_MAGIC_SIZE, &db->crc)) {
86 		fclose(db->fd);
87 		return -1;
88 	}
89 
90 	/* Close the database */
91 	fclose(db->fd);
92 	namedb_destroy(db);
93 	return 0;
94 }
95 
96 
97 void
98 namedb_discard (struct namedb *db)
99 {
100 	unlink(db->filename);
101 	namedb_destroy(db);
102 }
103 
104 static int
105 write_dname(struct namedb *db, domain_type *domain)
106 {
107 	const dname_type *dname = domain_dname(domain);
108 
109 	if (!write_data_crc(db->fd, &dname->name_size, sizeof(dname->name_size), &db->crc))
110 		return -1;
111 
112 	if (!write_data_crc(db->fd, dname_name(dname), dname->name_size, &db->crc))
113 		return -1;
114 
115 	return 0;
116 }
117 
118 static int
119 write_number(struct namedb *db, uint32_t number)
120 {
121 	number = htonl(number);
122 	return write_data_crc(db->fd, &number, sizeof(number), &db->crc);
123 }
124 
125 static int
126 write_rrset(struct namedb *db, domain_type *domain, rrset_type *rrset)
127 {
128 	uint16_t rr_count;
129 	int i, j;
130 	uint16_t type;
131 	uint16_t klass;
132 
133 	assert(db);
134 	assert(domain);
135 	assert(rrset);
136 
137 	rr_count = htons(rrset->rr_count);
138 
139 	if (!write_number(db, domain->number))
140 		return 1;
141 
142 	if (!write_number(db, rrset->zone->number))
143 		return 1;
144 
145 	type = htons(rrset_rrtype(rrset));
146 	if (!write_data_crc(db->fd, &type, sizeof(type), &db->crc))
147 		return 1;
148 
149 	klass = htons(rrset_rrclass(rrset));
150 	if (!write_data_crc(db->fd, &klass, sizeof(klass), &db->crc))
151 		return 1;
152 
153 	if (!write_data_crc(db->fd, &rr_count, sizeof(rr_count), &db->crc))
154 		return 1;
155 
156 	for (i = 0; i < rrset->rr_count; ++i) {
157 		rr_type *rr = &rrset->rrs[i];
158 		uint32_t ttl;
159 		uint16_t rdata_count;
160 
161 		rdata_count = htons(rr->rdata_count);
162 		if (!write_data_crc(db->fd, &rdata_count, sizeof(rdata_count), &db->crc))
163 			return 1;
164 
165 		ttl = htonl(rr->ttl);
166 		if (!write_data_crc(db->fd, &ttl, sizeof(ttl), &db->crc))
167 			return 1;
168 
169 		for (j = 0; j < rr->rdata_count; ++j) {
170 			rdata_atom_type atom = rr->rdatas[j];
171 			if (rdata_atom_is_domain(rr->type, j)) {
172 				if (!write_number(db, rdata_atom_domain(atom)->number))
173 					return 1;
174 
175 			} else {
176 				uint16_t size = htons(rdata_atom_size(atom));
177 				if (!write_data_crc(db->fd, &size, sizeof(size), &db->crc))
178 					return 1;
179 
180 				if (!write_data_crc(db->fd,
181 						rdata_atom_data(atom),
182 						rdata_atom_size(atom), &db->crc))
183 					return 1;
184 
185 			}
186 		}
187 	}
188 
189 	return 0;
190 }
191 
192 static int
193 number_dnames_iterator(domain_type *node, void *user_data)
194 {
195 	uint32_t *current_number = (uint32_t *) user_data;
196 
197 	node->number = *current_number;
198 	++*current_number;
199 
200 	return 0;
201 }
202 
203 static int
204 write_dname_iterator(domain_type *node, void *user_data)
205 {
206 	namedb_type *db = (namedb_type *) user_data;
207 
208 	return write_dname(db, node);
209 }
210 
211 static int
212 write_domain_iterator(domain_type *node, void *user_data)
213 {
214 	namedb_type *db = (namedb_type *) user_data;
215 	rrset_type *rrset;
216 	int error = 0;
217 
218 	for (rrset = node->rrsets; rrset; rrset = rrset->next) {
219 		error += write_rrset(db, node, rrset);
220 	}
221 
222 	return error;
223 }
224 
225 /*
226  * Writes databse data into open database *db
227  *
228  * Returns zero if success.
229  */
230 static int
231 write_db(namedb_type *db)
232 {
233 	zone_type *zone;
234 	uint32_t terminator = 0;
235 	uint32_t dname_count = 1;
236 	uint32_t zone_count = 1;
237 	int errors = 0;
238 
239 	for (zone = db->zones; zone; zone = zone->next) {
240 		zone->number = zone_count;
241 		++zone_count;
242 
243 		if (!zone->soa_rrset) {
244 			fprintf(stderr, "SOA record not present in %s\n",
245 				dname_to_string(domain_dname(zone->apex),
246 						NULL));
247 			++errors;
248 		}
249 	}
250 
251 	if (errors > 0)
252 		return -1;
253 
254 	--zone_count;
255 	if (!write_number(db, zone_count))
256 		return -1;
257 	for (zone = db->zones; zone; zone = zone->next) {
258 		if (write_dname(db, zone->apex))
259 			return -1;
260 	}
261 
262 	if (domain_table_iterate(db->domains, number_dnames_iterator, &dname_count))
263 		return -1;
264 
265 	--dname_count;
266 	if (!write_number(db, dname_count))
267 		return -1;
268 
269 	DEBUG(DEBUG_ZONEC, 1,
270 	      (LOG_INFO, "Storing %lu domain names\n", (unsigned long) dname_count));
271 
272 	if (domain_table_iterate(db->domains, write_dname_iterator, db))
273 		return -1;
274 
275 	if (domain_table_iterate(db->domains, write_domain_iterator, db))
276 		return -1;
277 
278 	if (!write_data_crc(db->fd, &terminator, sizeof(terminator), &db->crc))
279 		return -1;
280 
281 	return 0;
282 }
283