1/* $NetBSD$ */ 2 3/* 4 * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 1999-2003 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20/* Id: notify.c,v 1.37 2007/06/19 23:46:59 tbox Exp */ 21 22#include <config.h> 23 24#include <isc/log.h> 25#include <isc/print.h> 26 27#include <dns/message.h> 28#include <dns/rdataset.h> 29#include <dns/result.h> 30#include <dns/tsig.h> 31#include <dns/view.h> 32#include <dns/zone.h> 33#include <dns/zt.h> 34 35#include <named/log.h> 36#include <named/notify.h> 37 38/*! \file 39 * \brief 40 * This module implements notify as in RFC1996. 41 */ 42 43static void 44notify_log(ns_client_t *client, int level, const char *fmt, ...) { 45 va_list ap; 46 47 va_start(ap, fmt); 48 ns_client_logv(client, DNS_LOGCATEGORY_NOTIFY, NS_LOGMODULE_NOTIFY, 49 level, fmt, ap); 50 va_end(ap); 51} 52 53static void 54respond(ns_client_t *client, isc_result_t result) { 55 dns_rcode_t rcode; 56 dns_message_t *message; 57 isc_result_t msg_result; 58 59 message = client->message; 60 rcode = dns_result_torcode(result); 61 62 msg_result = dns_message_reply(message, ISC_TRUE); 63 if (msg_result != ISC_R_SUCCESS) 64 msg_result = dns_message_reply(message, ISC_FALSE); 65 if (msg_result != ISC_R_SUCCESS) { 66 ns_client_next(client, msg_result); 67 return; 68 } 69 message->rcode = rcode; 70 if (rcode == dns_rcode_noerror) 71 message->flags |= DNS_MESSAGEFLAG_AA; 72 else 73 message->flags &= ~DNS_MESSAGEFLAG_AA; 74 ns_client_send(client); 75} 76 77void 78ns_notify_start(ns_client_t *client) { 79 dns_message_t *request = client->message; 80 isc_result_t result; 81 dns_name_t *zonename; 82 dns_rdataset_t *zone_rdataset; 83 dns_zone_t *zone = NULL; 84 char namebuf[DNS_NAME_FORMATSIZE]; 85 char tsigbuf[DNS_NAME_FORMATSIZE + sizeof(": TSIG ''")]; 86 dns_tsigkey_t *tsigkey; 87 88 /* 89 * Interpret the question section. 90 */ 91 result = dns_message_firstname(request, DNS_SECTION_QUESTION); 92 if (result != ISC_R_SUCCESS) { 93 notify_log(client, ISC_LOG_NOTICE, 94 "notify question section empty"); 95 goto formerr; 96 } 97 98 /* 99 * The question section must contain exactly one question. 100 */ 101 zonename = NULL; 102 dns_message_currentname(request, DNS_SECTION_QUESTION, &zonename); 103 zone_rdataset = ISC_LIST_HEAD(zonename->list); 104 if (ISC_LIST_NEXT(zone_rdataset, link) != NULL) { 105 notify_log(client, ISC_LOG_NOTICE, 106 "notify question section contains multiple RRs"); 107 goto formerr; 108 } 109 110 /* The zone section must have exactly one name. */ 111 result = dns_message_nextname(request, DNS_SECTION_ZONE); 112 if (result != ISC_R_NOMORE) { 113 notify_log(client, ISC_LOG_NOTICE, 114 "notify question section contains multiple RRs"); 115 goto formerr; 116 } 117 118 /* The one rdataset must be an SOA. */ 119 if (zone_rdataset->type != dns_rdatatype_soa) { 120 notify_log(client, ISC_LOG_NOTICE, 121 "notify question section contains no SOA"); 122 goto formerr; 123 } 124 125 tsigkey = dns_message_gettsigkey(request); 126 if (tsigkey != NULL) { 127 dns_name_format(&tsigkey->name, namebuf, sizeof(namebuf)); 128 129 if (tsigkey->generated) { 130 char cnamebuf[DNS_NAME_FORMATSIZE]; 131 dns_name_format(tsigkey->creator, cnamebuf, 132 sizeof(cnamebuf)); 133 snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s' (%s)", 134 namebuf, cnamebuf); 135 } else { 136 snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s'", 137 namebuf); 138 } 139 } else 140 tsigbuf[0] = '\0'; 141 dns_name_format(zonename, namebuf, sizeof(namebuf)); 142 result = dns_zt_find(client->view->zonetable, zonename, 0, NULL, 143 &zone); 144 if (result != ISC_R_SUCCESS) 145 goto notauth; 146 147 switch (dns_zone_gettype(zone)) { 148 case dns_zone_master: 149 case dns_zone_slave: 150 case dns_zone_stub: /* Allow dialup passive to work. */ 151 notify_log(client, ISC_LOG_INFO, 152 "received notify for zone '%s'%s", namebuf, tsigbuf); 153 respond(client, dns_zone_notifyreceive(zone, 154 ns_client_getsockaddr(client), request)); 155 break; 156 default: 157 goto notauth; 158 } 159 dns_zone_detach(&zone); 160 return; 161 162 notauth: 163 notify_log(client, ISC_LOG_NOTICE, 164 "received notify for zone '%s'%s: not authoritative", 165 namebuf, tsigbuf); 166 result = DNS_R_NOTAUTH; 167 goto failure; 168 169 formerr: 170 result = DNS_R_FORMERR; 171 172 failure: 173 if (zone != NULL) 174 dns_zone_detach(&zone); 175 respond(client, result); 176} 177