1135446Strhodes/* 2193149Sdougb * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2003 Internet Software Consortium. 4135446Strhodes * 5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18234010Sdougb/* $Id: notify.c,v 1.37 2007/06/19 23:46:59 tbox Exp $ */ 19135446Strhodes 20135446Strhodes#include <config.h> 21135446Strhodes 22135446Strhodes#include <isc/log.h> 23135446Strhodes#include <isc/print.h> 24135446Strhodes 25135446Strhodes#include <dns/message.h> 26135446Strhodes#include <dns/rdataset.h> 27135446Strhodes#include <dns/result.h> 28193149Sdougb#include <dns/tsig.h> 29135446Strhodes#include <dns/view.h> 30135446Strhodes#include <dns/zone.h> 31135446Strhodes#include <dns/zt.h> 32135446Strhodes 33135446Strhodes#include <named/log.h> 34135446Strhodes#include <named/notify.h> 35135446Strhodes 36170222Sdougb/*! \file 37170222Sdougb * \brief 38170222Sdougb * This module implements notify as in RFC1996. 39135446Strhodes */ 40135446Strhodes 41135446Strhodesstatic void 42135446Strhodesnotify_log(ns_client_t *client, int level, const char *fmt, ...) { 43135446Strhodes va_list ap; 44135446Strhodes 45135446Strhodes va_start(ap, fmt); 46135446Strhodes ns_client_logv(client, DNS_LOGCATEGORY_NOTIFY, NS_LOGMODULE_NOTIFY, 47135446Strhodes level, fmt, ap); 48135446Strhodes va_end(ap); 49135446Strhodes} 50135446Strhodes 51135446Strhodesstatic void 52135446Strhodesrespond(ns_client_t *client, isc_result_t result) { 53135446Strhodes dns_rcode_t rcode; 54135446Strhodes dns_message_t *message; 55135446Strhodes isc_result_t msg_result; 56135446Strhodes 57135446Strhodes message = client->message; 58135446Strhodes rcode = dns_result_torcode(result); 59135446Strhodes 60135446Strhodes msg_result = dns_message_reply(message, ISC_TRUE); 61135446Strhodes if (msg_result != ISC_R_SUCCESS) 62135446Strhodes msg_result = dns_message_reply(message, ISC_FALSE); 63135446Strhodes if (msg_result != ISC_R_SUCCESS) { 64135446Strhodes ns_client_next(client, msg_result); 65135446Strhodes return; 66135446Strhodes } 67135446Strhodes message->rcode = rcode; 68135446Strhodes if (rcode == dns_rcode_noerror) 69135446Strhodes message->flags |= DNS_MESSAGEFLAG_AA; 70135446Strhodes else 71135446Strhodes message->flags &= ~DNS_MESSAGEFLAG_AA; 72135446Strhodes ns_client_send(client); 73135446Strhodes} 74135446Strhodes 75135446Strhodesvoid 76135446Strhodesns_notify_start(ns_client_t *client) { 77135446Strhodes dns_message_t *request = client->message; 78135446Strhodes isc_result_t result; 79135446Strhodes dns_name_t *zonename; 80135446Strhodes dns_rdataset_t *zone_rdataset; 81135446Strhodes dns_zone_t *zone = NULL; 82135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 83135446Strhodes char tsigbuf[DNS_NAME_FORMATSIZE + sizeof(": TSIG ''")]; 84193149Sdougb dns_tsigkey_t *tsigkey; 85135446Strhodes 86135446Strhodes /* 87135446Strhodes * Interpret the question section. 88135446Strhodes */ 89135446Strhodes result = dns_message_firstname(request, DNS_SECTION_QUESTION); 90135446Strhodes if (result != ISC_R_SUCCESS) { 91135446Strhodes notify_log(client, ISC_LOG_NOTICE, 92135446Strhodes "notify question section empty"); 93135446Strhodes goto formerr; 94135446Strhodes } 95135446Strhodes 96135446Strhodes /* 97135446Strhodes * The question section must contain exactly one question. 98135446Strhodes */ 99135446Strhodes zonename = NULL; 100135446Strhodes dns_message_currentname(request, DNS_SECTION_QUESTION, &zonename); 101135446Strhodes zone_rdataset = ISC_LIST_HEAD(zonename->list); 102135446Strhodes if (ISC_LIST_NEXT(zone_rdataset, link) != NULL) { 103135446Strhodes notify_log(client, ISC_LOG_NOTICE, 104135446Strhodes "notify question section contains multiple RRs"); 105135446Strhodes goto formerr; 106135446Strhodes } 107135446Strhodes 108135446Strhodes /* The zone section must have exactly one name. */ 109135446Strhodes result = dns_message_nextname(request, DNS_SECTION_ZONE); 110135446Strhodes if (result != ISC_R_NOMORE) { 111135446Strhodes notify_log(client, ISC_LOG_NOTICE, 112135446Strhodes "notify question section contains multiple RRs"); 113135446Strhodes goto formerr; 114135446Strhodes } 115135446Strhodes 116135446Strhodes /* The one rdataset must be an SOA. */ 117135446Strhodes if (zone_rdataset->type != dns_rdatatype_soa) { 118135446Strhodes notify_log(client, ISC_LOG_NOTICE, 119135446Strhodes "notify question section contains no SOA"); 120135446Strhodes goto formerr; 121135446Strhodes } 122135446Strhodes 123193149Sdougb tsigkey = dns_message_gettsigkey(request); 124193149Sdougb if (tsigkey != NULL) { 125193149Sdougb dns_name_format(&tsigkey->name, namebuf, sizeof(namebuf)); 126193149Sdougb 127193149Sdougb if (tsigkey->generated) { 128193149Sdougb char cnamebuf[DNS_NAME_FORMATSIZE]; 129193149Sdougb dns_name_format(tsigkey->creator, cnamebuf, 130193149Sdougb sizeof(cnamebuf)); 131193149Sdougb snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s' (%s)", 132193149Sdougb namebuf, cnamebuf); 133193149Sdougb } else { 134193149Sdougb snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s'", 135193149Sdougb namebuf); 136193149Sdougb } 137135446Strhodes } else 138135446Strhodes tsigbuf[0] = '\0'; 139135446Strhodes dns_name_format(zonename, namebuf, sizeof(namebuf)); 140135446Strhodes result = dns_zt_find(client->view->zonetable, zonename, 0, NULL, 141135446Strhodes &zone); 142135446Strhodes if (result != ISC_R_SUCCESS) 143135446Strhodes goto notauth; 144135446Strhodes 145135446Strhodes switch (dns_zone_gettype(zone)) { 146135446Strhodes case dns_zone_master: 147135446Strhodes case dns_zone_slave: 148135446Strhodes case dns_zone_stub: /* Allow dialup passive to work. */ 149135446Strhodes notify_log(client, ISC_LOG_INFO, 150135446Strhodes "received notify for zone '%s'%s", namebuf, tsigbuf); 151135446Strhodes respond(client, dns_zone_notifyreceive(zone, 152135446Strhodes ns_client_getsockaddr(client), request)); 153135446Strhodes break; 154135446Strhodes default: 155135446Strhodes goto notauth; 156135446Strhodes } 157135446Strhodes dns_zone_detach(&zone); 158135446Strhodes return; 159135446Strhodes 160135446Strhodes notauth: 161135446Strhodes notify_log(client, ISC_LOG_NOTICE, 162135446Strhodes "received notify for zone '%s'%s: not authoritative", 163135446Strhodes namebuf, tsigbuf); 164135446Strhodes result = DNS_R_NOTAUTH; 165135446Strhodes goto failure; 166135446Strhodes 167135446Strhodes formerr: 168135446Strhodes result = DNS_R_FORMERR; 169135446Strhodes 170135446Strhodes failure: 171135446Strhodes if (zone != NULL) 172135446Strhodes dns_zone_detach(&zone); 173135446Strhodes respond(client, result); 174135446Strhodes} 175