1 /* $NetBSD: notify.c,v 1.7 2024/02/21 22:52:46 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 #include <isc/log.h> 17 #include <isc/print.h> 18 #include <isc/result.h> 19 20 #include <dns/message.h> 21 #include <dns/rdataset.h> 22 #include <dns/result.h> 23 #include <dns/tsig.h> 24 #include <dns/view.h> 25 #include <dns/zone.h> 26 #include <dns/zt.h> 27 28 #include <ns/log.h> 29 #include <ns/notify.h> 30 #include <ns/types.h> 31 32 /*! \file 33 * \brief 34 * This module implements notify as in RFC1996. 35 */ 36 37 static void 38 notify_log(ns_client_t *client, int level, const char *fmt, ...) { 39 va_list ap; 40 41 va_start(ap, fmt); 42 ns_client_logv(client, DNS_LOGCATEGORY_NOTIFY, NS_LOGMODULE_NOTIFY, 43 level, fmt, ap); 44 va_end(ap); 45 } 46 47 static void 48 respond(ns_client_t *client, isc_result_t result) { 49 dns_rcode_t rcode; 50 dns_message_t *message; 51 isc_result_t msg_result; 52 53 message = client->message; 54 rcode = dns_result_torcode(result); 55 56 msg_result = dns_message_reply(message, true); 57 if (msg_result != ISC_R_SUCCESS) { 58 msg_result = dns_message_reply(message, false); 59 } 60 if (msg_result != ISC_R_SUCCESS) { 61 ns_client_drop(client, msg_result); 62 isc_nmhandle_detach(&client->reqhandle); 63 return; 64 } 65 message->rcode = rcode; 66 if (rcode == dns_rcode_noerror) { 67 message->flags |= DNS_MESSAGEFLAG_AA; 68 } else { 69 message->flags &= ~DNS_MESSAGEFLAG_AA; 70 } 71 72 ns_client_send(client); 73 isc_nmhandle_detach(&client->reqhandle); 74 } 75 76 void 77 ns_notify_start(ns_client_t *client, isc_nmhandle_t *handle) { 78 dns_message_t *request = client->message; 79 isc_result_t result; 80 dns_name_t *zonename; 81 dns_rdataset_t *zone_rdataset; 82 dns_zone_t *zone = NULL; 83 char namebuf[DNS_NAME_FORMATSIZE]; 84 char tsigbuf[DNS_NAME_FORMATSIZE * 2 + sizeof(": TSIG '' ()")]; 85 dns_tsigkey_t *tsigkey; 86 87 /* 88 * Attach to the request handle 89 */ 90 isc_nmhandle_attach(handle, &client->reqhandle); 91 92 /* 93 * Interpret the question section. 94 */ 95 result = dns_message_firstname(request, DNS_SECTION_QUESTION); 96 if (result != ISC_R_SUCCESS) { 97 notify_log(client, ISC_LOG_NOTICE, 98 "notify question section empty"); 99 result = DNS_R_FORMERR; 100 goto done; 101 } 102 103 /* 104 * The question section must contain exactly one question. 105 */ 106 zonename = NULL; 107 dns_message_currentname(request, DNS_SECTION_QUESTION, &zonename); 108 zone_rdataset = ISC_LIST_HEAD(zonename->list); 109 if (ISC_LIST_NEXT(zone_rdataset, link) != NULL) { 110 notify_log(client, ISC_LOG_NOTICE, 111 "notify question section contains multiple RRs"); 112 result = DNS_R_FORMERR; 113 goto done; 114 } 115 116 /* The zone section must have exactly one name. */ 117 result = dns_message_nextname(request, DNS_SECTION_ZONE); 118 if (result != ISC_R_NOMORE) { 119 notify_log(client, ISC_LOG_NOTICE, 120 "notify question section contains multiple RRs"); 121 result = DNS_R_FORMERR; 122 goto done; 123 } 124 125 /* The one rdataset must be an SOA. */ 126 if (zone_rdataset->type != dns_rdatatype_soa) { 127 notify_log(client, ISC_LOG_NOTICE, 128 "notify question section contains no SOA"); 129 result = DNS_R_FORMERR; 130 goto done; 131 } 132 133 tsigkey = dns_message_gettsigkey(request); 134 if (tsigkey != NULL) { 135 dns_name_format(&tsigkey->name, namebuf, sizeof(namebuf)); 136 137 if (tsigkey->generated) { 138 char cnamebuf[DNS_NAME_FORMATSIZE]; 139 dns_name_format(tsigkey->creator, cnamebuf, 140 sizeof(cnamebuf)); 141 snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s' (%s)", 142 namebuf, cnamebuf); 143 } else { 144 snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s'", 145 namebuf); 146 } 147 } else { 148 tsigbuf[0] = '\0'; 149 } 150 151 dns_name_format(zonename, namebuf, sizeof(namebuf)); 152 result = dns_zt_find(client->view->zonetable, zonename, 0, NULL, &zone); 153 if (result == ISC_R_SUCCESS) { 154 dns_zonetype_t zonetype = dns_zone_gettype(zone); 155 156 if ((zonetype == dns_zone_primary) || 157 (zonetype == dns_zone_secondary) || 158 (zonetype == dns_zone_mirror) || 159 (zonetype == dns_zone_stub)) 160 { 161 isc_sockaddr_t *from = ns_client_getsockaddr(client); 162 isc_sockaddr_t *to = ns_client_getdestaddr(client); 163 notify_log(client, ISC_LOG_INFO, 164 "received notify for zone '%s'%s", namebuf, 165 tsigbuf); 166 result = dns_zone_notifyreceive(zone, from, to, 167 request); 168 goto done; 169 } 170 } 171 172 notify_log(client, ISC_LOG_NOTICE, 173 "received notify for zone '%s'%s: not authoritative", 174 namebuf, tsigbuf); 175 result = DNS_R_NOTAUTH; 176 177 done: 178 if (zone != NULL) { 179 dns_zone_detach(&zone); 180 } 181 respond(client, result); 182 } 183