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