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