OpenDNSSEC-signer  1.4.1
notify.c
Go to the documentation of this file.
1 /*
2  * $Id: notify.c 4958 2011-04-18 07:11:09Z matthijs $
3  *
4  * Copyright (c) 2011 NLNet Labs. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
34 #include "config.h"
35 #include "adapter/addns.h"
36 #include "daemon/xfrhandler.h"
37 #include "signer/domain.h"
38 #include "signer/zone.h"
39 #include "wire/notify.h"
40 #include "wire/xfrd.h"
41 
42 #include <sys/socket.h>
43 
44 static const char* notify_str = "notify";
45 
46 static void notify_handle_zone(netio_type* netio,
47  netio_handler_type* handler, netio_events_type event_types);
48 
49 
54 static time_t
55 notify_time(notify_type* notify)
56 {
57  ods_log_assert(notify);
58  ods_log_assert(notify->xfrhandler);
59  return xfrhandler_time((xfrhandler_type*) notify->xfrhandler);
60 }
61 
62 
67 static void
68 notify_set_timer(notify_type* notify, time_t t)
69 {
70  if (!notify || !notify->xfrhandler) {
71  return;
72  }
77  if(t > notify_time(notify) + 10) {
78  time_t extra = t - notify_time(notify);
79  time_t base = extra*9/10;
80  t = notify_time(notify) + base +
81  random()%(extra-base);
82  }
83  notify->handler.timeout = &notify->timeout;
84  notify->timeout.tv_sec = t;
85  notify->timeout.tv_nsec = 0;
86  return;
87 }
88 
89 
95 notify_create(void* xfrhandler, void* zone)
96 {
97  notify_type* notify = NULL;
98  allocator_type* allocator = NULL;
99  if (!xfrhandler || !zone) {
100  return NULL;
101  }
102  allocator = allocator_create(malloc, free);
103  if (!allocator) {
104  ods_log_error("[%s] unable to create notify structure: "
105  "allocator_create() failed", notify_str);
106  return NULL;
107  }
108  notify = (notify_type*) allocator_alloc(allocator, sizeof(notify_type));
109  if (!notify) {
110  ods_log_error("[%s] unable to create notify structure: "
111  " allocator_alloc() failed", notify_str);
112  allocator_cleanup(allocator);
113  return NULL;
114  }
115  notify->allocator = allocator;
116  notify->zone = zone;
117  notify->xfrhandler = xfrhandler;
118  notify->waiting_next = NULL;
119  notify->secondary = NULL;
120  notify->soa = NULL;
121  notify->tsig_rr = tsig_rr_create(allocator);
122  if (!notify->tsig_rr) {
123  notify_cleanup(notify);
124  return NULL;
125  }
126  notify->retry = 0;
127  notify->query_id = 0;
128  notify->is_waiting = 0;
129  notify->handler.fd = -1;
130  notify->timeout.tv_sec = 0;
131  notify->timeout.tv_nsec = 0;
132  notify->handler.timeout = NULL;
133  notify->handler.user_data = notify;
134  notify->handler.event_types =
136  notify->handler.event_handler = notify_handle_zone;
137  return notify;
138 }
139 
140 
145 static void
146 notify_setup(notify_type* notify)
147 {
148  zone_type* zone = NULL;
149  dnsout_type* dnsout = NULL;
150  if (!notify) {
151  return;
152  }
153  zone = (zone_type*) notify->zone;
154  ods_log_assert(zone);
155  ods_log_assert(zone->adoutbound);
158  dnsout = (dnsout_type*) zone->adoutbound->config;
159  notify->retry = 0;
160  notify->secondary = dnsout->do_notify;
161  ods_log_debug("[%s] setup notify for zone %s", notify_str, zone->name);
162  notify_set_timer(notify, notify_time(notify));
163  return;
164 }
165 
166 
171 static void
172 notify_disable(notify_type* notify)
173 {
174  xfrhandler_type* xfrhandler = NULL;
175  zone_type* zone = NULL;
176  if (!notify) {
177  return;
178  }
179  xfrhandler = (xfrhandler_type*) notify->xfrhandler;
180  ods_log_assert(xfrhandler);
181  zone = (zone_type*) notify->zone;
182  ods_log_assert(zone);
183  ods_log_assert(zone->name);
184  notify->secondary = NULL;
185  notify->handler.timeout = NULL;
186  if (notify->handler.fd != -1) {
187  close(notify->handler.fd);
188  notify->handler.fd = -1;
189  }
190  if (xfrhandler->notify_udp_num == NOTIFY_MAX_UDP) {
191  while (xfrhandler->notify_waiting_first) {
192  notify_type* wn = xfrhandler->notify_waiting_first;
194  wn->is_waiting = 0;
195  xfrhandler->notify_waiting_first = wn->waiting_next;
196  if (xfrhandler->notify_waiting_last == wn) {
197  xfrhandler->notify_waiting_last = NULL;
198  }
199  if (wn->secondary) {
200  ods_log_debug("[%s] zone %s notify off waiting list",
201  notify_str, zone->name);
202  notify_setup(wn);
203  return;
204  }
205  }
206  }
207  ods_log_debug("[%s] notify for zone %s disabled", notify_str, zone->name);
208  xfrhandler->notify_udp_num--;
209  return;
210 }
211 
212 
217 static void
218 notify_next(notify_type* notify)
219 {
220  if (!notify || !notify->secondary) {
221  return;
222  }
223  notify->secondary = notify->secondary->next;
224  notify->retry = 0;
225  if (!notify->secondary) {
226  zone_type* zone = (zone_type*) notify->zone;
227  ods_log_assert(zone);
228  ods_log_assert(zone->name);
229  ods_log_debug("[%s] zone %s no more secondaries, disable notify",
230  notify_str, zone->name);
231  notify_disable(notify);
232  }
233  return;
234 }
235 
236 
241 static int
242 notify_udp_read_packet(notify_type* notify)
243 {
244  xfrhandler_type* xfrhandler = NULL;
245  ssize_t received = 0;
246  ods_log_assert(notify);
247  xfrhandler = (xfrhandler_type*) notify->xfrhandler;
248  ods_log_assert(xfrhandler);
249  buffer_clear(xfrhandler->packet);
250  received = recvfrom(notify->handler.fd, buffer_begin(xfrhandler->packet),
251  buffer_remaining(xfrhandler->packet), 0, NULL, NULL);
252  if (received == -1) {
253  ods_log_error("[%s] unable to read packet: recvfrom() failed fd %d "
254  "(%s)", notify_str, notify->handler.fd, strerror(errno));
255  return 0;
256  }
257  buffer_set_limit(xfrhandler->packet, received);
258  return 1;
259 }
260 
261 
266 static int
267 notify_handle_reply(notify_type* notify)
268 {
269  xfrhandler_type* xfrhandler = NULL;
270  zone_type* zone = NULL;
271  ods_log_assert(notify);
272  ods_log_assert(notify->secondary);
273  ods_log_assert(notify->secondary->address);
274  xfrhandler = (xfrhandler_type*) notify->xfrhandler;
275  zone = (zone_type*) notify->zone;
276  ods_log_assert(xfrhandler);
277  ods_log_assert(zone);
278  ods_log_assert(zone->name);
279  if ((buffer_pkt_opcode(xfrhandler->packet) != LDNS_PACKET_NOTIFY) ||
280  (buffer_pkt_qr(xfrhandler->packet) == 0)) {
281  ods_log_error("[%s] zone %s received bad notify reply opcode/qr",
282  notify_str, zone->name);
283  return 0;
284  }
285  if (buffer_pkt_id(xfrhandler->packet) != notify->query_id) {
286  ods_log_error("[%s] zone %s received bad notify reply id",
287  notify_str, zone->name);
288  return 0;
289  }
290  /* could check tsig */
291  if (buffer_pkt_rcode(xfrhandler->packet) != LDNS_RCODE_NOERROR) {
292  ods_log_error("[%s] zone %s received bad notify rcode %d",
293  notify_str, zone->name, buffer_pkt_rcode(xfrhandler->packet));
294  if (buffer_pkt_rcode(xfrhandler->packet) != LDNS_RCODE_NOTIMPL) {
295  return 1;
296  }
297  return 0;
298  }
299  ods_log_debug("[%s] zone %s secondary %s notify reply ok", notify_str,
300  zone->name, notify->secondary->address);
301  return 1;
302 }
303 
304 
309 static int
310 notify_send_udp(notify_type* notify, buffer_type* buffer)
311 {
312  struct sockaddr_storage to;
313  socklen_t to_len = 0;
314  int fd = -1;
315  int family = PF_INET;
316  ssize_t nb = 0;
317  ods_log_assert(buffer);
318  ods_log_assert(notify);
319  ods_log_assert(notify->secondary);
320  ods_log_assert(notify->secondary->address);
321  /* this will set the remote port to acl->port or TCP_PORT */
322  to_len = xfrd_acl_sockaddr_to(notify->secondary, &to);
323  /* get the address family of the remote host */
324  if (notify->secondary->family == AF_INET6) {
325  family = PF_INET6;
326  }
327  /* create socket */
328  fd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
329  if (fd == -1) {
330  ods_log_error("[%s] unable to send data over udp to %s: "
331  "socket() failed (%s)", notify_str, notify->secondary->address,
332  strerror(errno));
333  return -1;
334  }
335  /* bind it? */
336 
337  /* send it (udp) */
338  ods_log_deeebug("[%s] send %d bytes over udp to %s", notify_str,
339  buffer_remaining(buffer), notify->secondary->address);
340  nb = sendto(fd, buffer_current(buffer), buffer_remaining(buffer), 0,
341  (struct sockaddr*)&to, to_len);
342  if (nb == -1) {
343  ods_log_error("[%s] unable to send data over udp to %s: "
344  "sendto() failed (%s)", notify_str, notify->secondary->address,
345  strerror(errno));
346  close(fd);
347  return -1;
348  }
349  return fd;
350 }
351 
352 
357 static void
358 notify_tsig_sign(notify_type* notify, buffer_type* buffer)
359 {
360  tsig_algo_type* algo = NULL;
361  if (!notify || !notify->tsig_rr || !notify->secondary ||
362  !notify->secondary->tsig || !notify->secondary->tsig->key ||
363  !buffer) {
364  return; /* no tsig configured */
365  }
366  algo = tsig_lookup_algo(notify->secondary->tsig->algorithm);
367  if (!algo) {
368  ods_log_error("[%s] unable to sign notify: tsig unknown algorithm "
369  "%s", notify_str, notify->secondary->tsig->algorithm);
370  return;
371  }
372  ods_log_assert(algo);
373  tsig_rr_reset(notify->tsig_rr, algo, notify->secondary->tsig->key);
374  notify->tsig_rr->original_query_id = buffer_pkt_id(buffer);
375  notify->tsig_rr->algo_name =
376  ldns_rdf_clone(notify->tsig_rr->algo->wf_name);
377  notify->tsig_rr->key_name = ldns_rdf_clone(notify->tsig_rr->key->dname);
378  log_dname(notify->tsig_rr->key_name, "tsig sign notify with key %s",
379  LOG_DEBUG);
380  log_dname(notify->tsig_rr->algo_name, "tsig sign notify with algorithm %s",
381  LOG_DEBUG);
382  tsig_rr_prepare(notify->tsig_rr);
383  tsig_rr_update(notify->tsig_rr, buffer, buffer_position(buffer));
384  tsig_rr_sign(notify->tsig_rr);
385  ods_log_debug("[%s] tsig append rr to notify id=%u", notify_str,
386  buffer_pkt_id(buffer));
387  tsig_rr_append(notify->tsig_rr, buffer);
388  buffer_pkt_set_arcount(buffer, buffer_pkt_arcount(buffer)+1);
389  tsig_rr_prepare(notify->tsig_rr);
390  return;
391 }
392 
393 
398 void
400 {
401  xfrhandler_type* xfrhandler = NULL;
402  zone_type* zone = NULL;
403  ods_log_assert(notify);
404  ods_log_assert(notify->secondary);
405  ods_log_assert(notify->secondary->address);
406  xfrhandler = (xfrhandler_type*) notify->xfrhandler;
407  zone = (zone_type*) notify->zone;
408  ods_log_assert(xfrhandler);
409  ods_log_assert(zone);
410  ods_log_assert(zone->name);
411  if (notify->handler.fd != -1) {
412  close(notify->handler.fd);
413  }
414  notify->handler.fd = -1;
415  notify->timeout.tv_sec = notify_time(notify) + NOTIFY_RETRY_TIMEOUT;
416  buffer_pkt_notify(xfrhandler->packet, zone->apex, LDNS_RR_CLASS_IN);
417  notify->query_id = buffer_pkt_id(xfrhandler->packet);
418  buffer_pkt_set_aa(xfrhandler->packet);
419  /* add current SOA to answer section */
420  if (notify->soa) {
421  if (buffer_write_rr(xfrhandler->packet, notify->soa)) {
422  buffer_pkt_set_ancount(xfrhandler->packet, 1);
423  }
424  }
425  if (notify->secondary->tsig) {
426  notify_tsig_sign(notify, xfrhandler->packet);
427  }
428  buffer_flip(xfrhandler->packet);
429  notify->handler.fd = notify_send_udp(notify, xfrhandler->packet);
430  if (notify->handler.fd == -1) {
431  ods_log_error("[%s] unable to send notify retry %u for zone %s to "
432  "%s: notify_send_udp() failed", notify_str, notify->retry,
433  zone->name, notify->secondary->address);
434  return;
435  }
436  ods_log_verbose("[%s] notify retry %u for zone %s sent to %s", notify_str,
437  notify->retry, zone->name, notify->secondary->address);
438  return;
439 }
440 
441 
446 static void
447 notify_handle_zone(netio_type* ATTR_UNUSED(netio),
448  netio_handler_type* handler, netio_events_type event_types)
449 {
450  notify_type* notify = NULL;
451  xfrhandler_type* xfrhandler = NULL;
452  zone_type* zone = NULL;
453  if (!handler) {
454  return;
455  }
456  notify = (notify_type*) handler->user_data;
457  ods_log_assert(notify);
458  xfrhandler = (xfrhandler_type*) notify->xfrhandler;
459  zone = (zone_type*) notify->zone;
460  ods_log_assert(xfrhandler);
461  ods_log_assert(zone);
462  ods_log_assert(zone->name);
463  ods_log_debug("[%s] handle notify for zone %s", notify_str, zone->name);
464 
465  if (notify->is_waiting) {
466  ods_log_debug("[%s] already waiting, skipping notify for zone %s",
467  notify_str, zone->name);
468  ods_log_assert(notify->handler.fd == -1);
469  return;
470  }
471  if (event_types & NETIO_EVENT_READ) {
472  ods_log_debug("[%s] read notify ok for zone %s", notify_str,
473  zone->name);
474  ods_log_assert(notify->handler.fd != -1);
475  if (notify_udp_read_packet(notify)) {
476  if (notify_handle_reply(notify)) {
477  notify_next(notify);
478  }
479  }
480  } else if(event_types & NETIO_EVENT_TIMEOUT) {
481  ods_log_debug("[%s] notify timeout for zone %s", notify_str,
482  zone->name);
483  /* timeout, try again */
484  }
485  /* see if notify is still enabled */
486  if (notify->secondary) {
487  ods_log_assert(notify->secondary->address);
488  notify->retry++;
489  if (notify->retry > NOTIFY_MAX_RETRY) {
490  ods_log_verbose("[%s] notify max retry for zone %s, %s unreachable",
491  notify_str, zone->name, notify->secondary->address);
492  notify_next(notify);
493  } else {
494  notify_send(notify);
495  }
496  }
497  return;
498 }
499 
500 
505 static void
506 notify_update_soa(notify_type* notify, ldns_rr* soa)
507 {
508  if (!notify) {
509  return;
510  }
511  if (notify->soa) {
512  ldns_rr_free(notify->soa);
513  }
514  notify->soa = soa;
515  return;
516 }
517 
518 
523 void
524 notify_enable(notify_type* notify, ldns_rr* soa)
525 {
526  xfrhandler_type* xfrhandler = NULL;
527  zone_type* zone = NULL;
528  dnsout_type* dnsout = NULL;
529  if (!notify) {
530  return;
531  }
532  xfrhandler = (xfrhandler_type*) notify->xfrhandler;
533  ods_log_assert(xfrhandler);
534  zone = (zone_type*) notify->zone;
535  ods_log_assert(zone);
536  ods_log_assert(zone->name);
537  ods_log_assert(zone->adoutbound);
540  dnsout = (dnsout_type*) zone->adoutbound->config;
541  if (!dnsout->do_notify) {
542  ods_log_warning("[%s] zone %s has no notify acl", notify_str,
543  zone->name);
544  return; /* nothing to do */
545  }
546  notify_update_soa(notify, soa);
547  if (notify->is_waiting) {
548  ods_log_debug("[%s] zone %s already on waiting list", notify_str,
549  zone->name);
550  return;
551  }
552  if (xfrhandler->notify_udp_num < NOTIFY_MAX_UDP) {
553  notify_setup(notify);
554  xfrhandler->notify_udp_num++;
555  ods_log_debug("[%s] zone %s notify enabled", notify_str,
556  zone->name);
557  return;
558  }
559  /* put it in waiting list */
560  notify->secondary = dnsout->do_notify;
561  notify->is_waiting = 1;
562  notify->waiting_next = NULL;
563  if (xfrhandler->notify_waiting_last) {
564  xfrhandler->notify_waiting_last->waiting_next = notify;
565  } else {
566  xfrhandler->notify_waiting_first = notify;
567  }
568  xfrhandler->notify_waiting_last = notify;
569  notify->handler.timeout = NULL;
570  ods_log_debug("[%s] zone %s notify on waiting list", notify_str,
571  zone->name);
572  return;
573 }
574 
575 
580 void
582 {
583  allocator_type* allocator = NULL;
584  if (!notify) {
585  return;
586  }
587  allocator = notify->allocator;
588  if (notify->handler.fd != -1) {
589  close(notify->handler.fd);
590  notify->handler.fd = -1;
591  }
592  if (notify->soa) {
593  ldns_rr_free(notify->soa);
594  }
595  tsig_rr_cleanup(notify->tsig_rr);
596  allocator_deallocate(allocator, (void*) notify);
597  allocator_cleanup(allocator);
598  return;
599 }