1 /**************************************************************************************************
2 $Id: notify.c,v 1.0 2007/09/04 10:00:57 howard Exp $
3
4 Copyright (C) 2007 Howard Wilkinson <howard@cohtech.com>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at Your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 **************************************************************************************************/
20
21 #include "named.h"
22
23 /* Make this nonzero to enable debugging for this source file */
24 #define DEBUG_IXFR 1
25
26 #define DEBUG_IXFR_SQL 1
27
28 typedef struct _ixfr_authority_rr {
29 uchar *name;
30 dns_qtype_t type;
31 dns_class_t class;
32 uint32_t ttl;
33 uchar *mname;
34 uchar *rname;
35 uint32_t serial;
36 uint32_t refresh;
37 uint32_t retry;
38 uint32_t expire;
39 uint32_t minimum;
40 } IARR;
41
42 #define IARR_NAME(__rrp) ((__rrp)->name)
43 #define IARR_MNAME(__rrp) ((__rrp)->mname)
44 #define IARR_RNAME(__rrp) ((__rrp)->rname)
45
46 typedef struct _ixfr_query {
47 /* Zone section */
48 uchar *name; /* The zone name */
49 dns_qtype_t type; /* Must be DNS_QTYPE_SOA */
50 dns_class_t class; /* The zone's class */
51
52 IARR IR;
53 } IQ;
54
55 #define IQ_NAME(__iqp) ((__iqp)->name)
56
57 static IQ *
allocate_iq(void)58 allocate_iq(void) {
59 IQ *q = ALLOCATE(sizeof(IQ), IQ);
60
61 memset(q, 0, sizeof(IQ));
62
63 return q;
64 }
65
66 static void
free_iarr_data(IARR * rr)67 free_iarr_data(IARR *rr) {
68 RELEASE(IARR_NAME(rr));
69 RELEASE(IARR_MNAME(rr));
70 RELEASE(IARR_RNAME(rr));
71 }
72
73 static void
free_iq(IQ * q)74 free_iq(IQ *q) {
75 free_iarr_data(&q->IR);
76
77 RELEASE(IQ_NAME(q));
78
79 RELEASE(q);
80 }
81
82 static uchar *
ixfr_gobble_authority_rr(TASK * t,uchar * query,size_t querylen,uchar * current,IARR * rr)83 ixfr_gobble_authority_rr(TASK *t, uchar *query, size_t querylen, uchar *current, IARR *rr){
84 uchar * src = current;
85 int rdlength = 0;
86 task_error_t errcode = TASK_FAILED;
87
88 if (!(IARR_NAME(rr) = name_unencode2(query, querylen, &src, &errcode))) {
89 formerr(t, DNS_RCODE_FORMERR, errcode, NULL);
90 return NULL;
91 }
92 DNS_GET16(rr->type, src);
93 DNS_GET16(rr->class, src);
94 DNS_GET32(rr->ttl, src);
95
96 DNS_GET16(rdlength, src);
97 if (!(IARR_MNAME(rr) = name_unencode2(query, querylen, &src, &errcode))) {
98 formerr(t, DNS_RCODE_FORMERR, errcode, NULL);
99 return NULL;
100 }
101
102 if (!(IARR_RNAME(rr) = name_unencode2(query, querylen, &src, &errcode))) {
103 formerr(t, DNS_RCODE_FORMERR, errcode, NULL);
104 return NULL;
105 }
106
107 DNS_GET32(rr->serial, src);
108 DNS_GET32(rr->refresh, src);
109 DNS_GET32(rr->retry, src);
110 DNS_GET32(rr->expire, src);
111 DNS_GET32(rr->minimum, src);
112
113 return src;
114 }
115
116 taskexec_t
ixfr(TASK * t,datasection_t section,dns_qtype_t qtype,char * fqdn,int truncateonly)117 ixfr(TASK * t, datasection_t section, dns_qtype_t qtype, char *fqdn, int truncateonly) {
118 MYDNS_SOA *soa = NULL;
119 uchar *query = (uchar*)t->query;
120 int querylen = t->len;
121 uchar *src = query + DNS_HEADERSIZE;
122 IQ *q = NULL;
123 task_error_t errcode = 0;
124
125 #if DEBUG_ENABLED && DEBUG_IXFR
126 DebugX("ixfr", 1, "%s: ixfr(%s, %s, \"%s\", %d)", desctask(t),
127 resolve_datasection_str[section], mydns_qtype_str(qtype), fqdn, truncateonly);
128 #endif
129
130 if (!dns_ixfr_enabled) {
131 dnserror(t, DNS_RCODE_REFUSED, ERR_IXFR_NOT_ENABLED);
132 return (TASK_FAILED);
133 }
134
135 /*
136 * Authority section contains the SOA record for the client's version of the zone
137 * only trust the serial number.
138 */
139
140 if (mydns_soa_load(sql, &soa, fqdn) < 0) {
141 dnserror(t, DNS_RCODE_SERVFAIL, ERR_DB_ERROR);
142 return (TASK_FAILED);
143 }
144
145 if (!soa) {
146 dnserror(t, DNS_RCODE_REFUSED, ERR_ZONE_NOT_FOUND);
147 return (TASK_FAILED);
148 }
149
150 #if DEBUG_ENABLED && DEBUG_IXFR
151 DebugX("ixfr", 1, _("%s: DNS IXFR: SOA id %u"), desctask(t), soa->id);
152 DebugX("ixfr", 1, _("%s: DNS IXFR: QDCOUNT=%d (Query)"), desctask(t), t->qdcount);
153 DebugX("ixfr", 1, _("%s: DNS IXFR: ANCOUNT=%d (Answer)"), desctask(t), t->ancount);
154 DebugX("ixfr", 1, _("%s: DNS IXFR: AUCOUNT=%d (Authority)"), desctask(t), t->nscount);
155 DebugX("ixfr", 1, _("%s: DNS IXFR: ADCOUNT=%d (Additional data)"), desctask(t), t->arcount);
156 #endif
157 if (!t->nscount)
158 return formerr(t, DNS_RCODE_FORMERR, ERR_NO_AUTHORITY,
159 _("ixfr query contains no authority data"));
160
161 if (t->nscount != 1)
162 return formerr(t, DNS_RCODE_FORMERR, ERR_MULTI_AUTHORITY,
163 _("ixfr query contains multiple authority records"));
164
165 if (!t->qdcount)
166 return formerr(t, DNS_RCODE_FORMERR, ERR_NO_QUESTION,
167 _("ixfr query does not contain question"));
168
169 if (t->qdcount != 1)
170 return formerr(t, DNS_RCODE_FORMERR, ERR_MULTI_QUESTIONS,
171 _("ixfr query contains multiple questions"));
172
173 if (t->ancount || t->arcount)
174 return formerr(t, DNS_RCODE_FORMERR, ERR_MALFORMED_REQUEST,
175 _("ixfr query has answer or additional data"));
176
177 q = allocate_iq();
178
179 if (!(IQ_NAME(q) = name_unencode2(query, querylen, &src, &errcode))) {
180 free_iq(q);
181 return formerr(t, DNS_RCODE_FORMERR, errcode, NULL);
182 }
183
184 DNS_GET16(q->type, src);
185 DNS_GET16(q->class, src);
186
187 if (!(src = ixfr_gobble_authority_rr(t, query, querylen, src, &q->IR))) {
188 free_iq(q);
189 return (TASK_FAILED);
190 }
191
192 /* Get the serial number from the RR record in the authority section */
193 #if DEBUG_ENABLED && DEBUG_IXFR
194 DebugX("ixfr", 1, _("%s: DNS IXFR Question[zone %s qclass %s qtype %s]"
195 " Authority[zone %s qclass %s qtype %s ttl %u "
196 "mname %s rname %s serial %u refresh %u retry %u expire %u minimum %u]"),
197 desctask(t), q->name, mydns_class_str(q->class), mydns_qtype_str(q->type),
198 q->IR.name, mydns_class_str(q->IR.class), mydns_qtype_str(q->IR.type), q->IR.ttl,
199 q->IR.mname, q->IR.rname, q->IR.serial, q->IR.refresh, q->IR.retry, q->IR.expire, q->IR.minimum);
200 #endif
201
202 /*
203 * As per RFC 1995 we have 3 options for a response if a delta exists.
204 *
205 * We can send a full zone transfer if it will fit in a UDP packet and is smaller
206 * than sending deltas
207 *
208 * We can send a delta transfer if it will fit into a single UDP packet and we can calculate
209 * one for the difference between the client and the current serial
210 *
211 * We can send a packet with a single SOA record for the latest SOA. This will force the client
212 * to initiate an AXFR.
213 *
214 * We can calculate the size of the response by either building both messages
215 * or by an estimation technique. In either case we need to look at the data.
216 *
217 * I have chosen to check for altered records within the database first.
218 *
219 * First check is to make sure that the serial held by the client is not the current one
220 *
221 * Next check to see if out incremental data for the transition from client serial
222 * to current serial has not expired.
223 *
224 * Then retrieve the updated records between the client serial and the latest serial.
225 * and retrieve the entire zone ... a record count is the first check.
226 *
227 * If the number of delta records is larger than the number of zone records then send the zone
228 *
229 * Calculate the size of the variable parts of the record and compare.
230 * We assume that name encoding will have an equal effect on the data.
231 * So having chosen to send either the zone or the deltas construct the packet.
232 *
233 * Check that the packet has not overflowed the UDP limit and send. If it has
234 * that abandon the packet and send one containing just the latest SOA.
235 *
236 */
237
238 if (soa->serial == q->IR.serial) {
239 /* Tell the client to do no zone transfer */
240 rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin);
241 t->sort_level++;
242 } else {
243 /* Do we have incremental information in the database */
244 if (!truncateonly && mydns_rr_use_active && mydns_rr_use_stamp && mydns_rr_use_serial) {
245 /* We can do incrementals */
246 /* Need to send an IXFR if available */
247 /*
248 * Work out when the client SOA came into being
249 */
250 MYDNS_RR *ThisRR = NULL, *rr = NULL;
251 char *deltafilter = NULL;
252 int deletecount, activecount, zonesize;
253 size_t deltasize, fullsize;
254
255 /* For very large zones we do not want to load all of the records just to give up */
256 sql_build_query(&deltafilter, "serial > %u", q->IR.serial);
257
258 /*
259 * Compare counts of changes from full zone data
260 * ... assumes records are about the same size
261 * approximate zone size by 2 * deleted count === actual number of delta records
262 */
263 deletecount = mydns_rr_count_deleted_filtered(sql,
264 soa->id, DNS_QTYPE_ANY, NULL,
265 soa->origin, deltafilter);
266 activecount = mydns_rr_count_active_filtered(sql,
267 soa->id, DNS_QTYPE_ANY, NULL,
268 soa->origin, deltafilter);
269 zonesize = mydns_rr_count_active(sql,
270 soa->id, DNS_QTYPE_ANY, NULL,
271 soa->origin);
272 deltasize = deletecount + activecount + 4;
273 fullsize = zonesize + 2;
274
275 if ((deletecount < 0) || (activecount < 0) || (zonesize < 0)) {
276 RELEASE(deltafilter);
277 dnserror(t, DNS_RCODE_SERVFAIL, ERR_DB_ERROR);
278 return (TASK_FAILED);
279 }
280 if (deletecount || activecount) {
281 if (deltasize >= fullsize) {
282 /* Send a full zone transfer */
283 /* Current Serial first */
284 rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin);
285 t->sort_level++;
286 if (mydns_rr_load_active(sql, &ThisRR, soa->id, DNS_QTYPE_ANY, NULL, soa->origin) == 0) {
287 for (rr = ThisRR; rr; rr = rr->next) {
288 char *name = mydns_rr_append_origin(MYDNS_RR_NAME(rr), soa->origin);
289 rrlist_add(t, ANSWER, DNS_RRTYPE_RR, (void *)rr, name);
290 if (name != MYDNS_RR_NAME(rr)) RELEASE(name);
291 }
292 t->sort_level++;
293 mydns_rr_free(ThisRR);
294 rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin);
295 t->sort_level++;
296 }
297 } else {
298 int latest_serial = soa->serial;
299
300 rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin);
301 t->sort_level++;
302 soa->serial = q->IR.serial;
303 rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin);
304 t->sort_level++;
305 soa->serial = latest_serial;
306 if (mydns_rr_load_deleted_filtered(sql, &ThisRR, soa->id, DNS_QTYPE_ANY, NULL, soa->origin,
307 deltafilter) == 0) {
308 for (rr = ThisRR; rr; rr = rr->next) {
309 char *name = mydns_rr_append_origin(MYDNS_RR_NAME(rr), soa->origin);
310 rrlist_add(t, ANSWER, DNS_RRTYPE_RR, (void *)rr, name);
311 if (name != MYDNS_RR_NAME(rr)) RELEASE(name);
312 }
313 t->sort_level++;
314 mydns_rr_free(ThisRR);
315 }
316 rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin);
317 t->sort_level++;
318 if (mydns_rr_load_active_filtered(sql, &ThisRR, soa->id, DNS_QTYPE_ANY, NULL, soa->origin,
319 deltafilter) == 0) {
320 for (rr = ThisRR; rr; rr = rr->next) {
321 char *name = mydns_rr_append_origin(MYDNS_RR_NAME(rr), soa->origin);
322 rrlist_add(t, ANSWER, DNS_RRTYPE_RR, (void *)rr, name);
323 if (name != MYDNS_RR_NAME(rr)) RELEASE(name);
324 }
325 t->sort_level++;
326 mydns_rr_free(ThisRR);
327 rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin);
328 t->sort_level++;
329 }
330 RELEASE(deltafilter);
331 }
332 goto FINISHEDIXFR;
333 }
334 }
335 }
336
337 /* Tell the client to do a full zone transfer or not at all */
338 rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin);
339 t->sort_level++;
340
341 FINISHEDIXFR:
342 mydns_soa_free(soa);
343
344 free_iq(q);
345
346 t->hdr.aa = 1;
347
348 return (TASK_EXECUTED);
349 }
350
351 static taskexec_t
ixfr_purge_all_soas(TASK * t,void * data)352 ixfr_purge_all_soas(TASK *t, void *data) {
353
354 /*
355 * Retrieve all zone id's that have deleted records.
356 *
357 * For each zone get the expire field and delete any records that have expired.
358 *
359 */
360
361 SQL_RES *res = NULL;
362 SQL_ROW row = NULL;
363
364 size_t querylen;
365 const char *QUERY0 = "SELECT DISTINCT zone FROM %s WHERE active='%s'";
366 const char *QUERY1 = "SELECT origin FROM %s "
367 "WHERE id=%u;";
368 const char *QUERY2 = "DELETE FROM %s WHERE zone=%u AND active='%s' "
369 " AND stamp < DATE_SUB(NOW(),INTERVAL %u SECOND);";
370 char *query = NULL;
371
372 /*
373 * Reset task timeout clock to some suitable value in the future
374 */
375 t->timeout = current_time + ixfr_gc_interval; /* Try again e.g. tomorrow */
376
377 querylen = sql_build_query(&query, QUERY0,
378 mydns_rr_table_name, mydns_rr_active_types[2]);
379
380 if (!(res = sql_query(sql, query, querylen)))
381 ErrSQL(sql, "%s: %s", desctask(t),
382 _("error loading zone id's for DELETED records"));
383
384 RELEASE(query);
385
386 while((row = sql_getrow(res, NULL))) {
387 unsigned int id = atou(row[0]);
388 char *origin = NULL;
389 MYDNS_SOA *soa = NULL;
390 SQL_RES *sres = NULL;
391
392 querylen = sql_build_query(&query, QUERY1,
393 mydns_soa_table_name, id);
394
395 if (!(res = sql_query(sql, query, querylen)))
396 ErrSQL(sql, "%s: %s", desctask(t),
397 _("error loading zone from DELETED record zone id"));
398
399 RELEASE(query);
400
401 if (!(row = sql_getrow(res, NULL))) {
402 Warnx(_("%s: no soa found for soa id %u"), desctask(t),
403 id);
404 continue;
405 }
406
407 origin = row[0];
408
409 if (mydns_soa_load(sql, &soa, origin) == 0) {
410 querylen = sql_build_query(&query, QUERY2,
411 mydns_rr_table_name, soa->id, mydns_rr_active_types[2], soa->expire);
412
413 if (sql_nrquery(sql, query, querylen) != 0)
414 WarnSQL(sql, "%s: %s %s", desctask(t),
415 _("error deleting expired records for zone "), soa->origin);
416
417 RELEASE(query);
418
419 sql_free(sres);
420 }
421 }
422
423 sql_free(res);
424 RELEASE(query);
425
426 return (TASK_CONTINUE);
427 }
428
429 void
ixfr_start()430 ixfr_start() {
431
432 TASK *inittask = NULL;
433
434 if (!ixfr_gc_enabled) return;
435
436 /* Only GC if the DB has IXFR information in it */
437 if (mydns_rr_use_active && mydns_rr_use_stamp && mydns_rr_use_serial) {
438 inittask = Ticktask_init(LOW_PRIORITY_TASK, NEED_TASK_RUN, -1, 0, AF_UNSPEC, NULL);
439 task_add_extension(inittask, NULL, NULL, NULL, ixfr_purge_all_soas);
440
441 inittask->timeout = current_time + ixfr_gc_delay; /* Run first one in e.g. 10 minutes time */
442 }
443 }
444
445 /* vi:set ts=3: */
446 /* NEED_PO */
447