]> git.ipfire.org Git - thirdparty/dhcp.git/blame - server/ddns.c
Fix handling of agent options.
[thirdparty/dhcp.git] / server / ddns.c
CommitLineData
b992d7e2
DN
1/* ddns.c
2
3 Dynamic DNS updates. */
4
5/*
385fcb27 6 * Copyright (c) 2000-2001 Internet Software Consortium.
b992d7e2
DN
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 * of its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
0598e123
TL
36 * This software has been donated to the Internet Software Consortium
37 * by Damien Neil of Nominum, Inc.
38 *
b992d7e2 39 * To learn more about the Internet Software Consortium, see
0598e123 40 * ``http://www.isc.org/''. To learn more about Nominum, Inc., see
b992d7e2
DN
41 * ``http://www.nominum.com''.
42 */
43
44#ifndef lint
45static char copyright[] =
e9d45f83 46"$Id: ddns.c,v 1.11 2001/01/19 10:59:10 mellon Exp $ Copyright (c) 2000-2001 The Internet Software Consortium. All rights reserved.\n";
b992d7e2
DN
47#endif /* not lint */
48
49#include "dhcpd.h"
ec64b462
TL
50#include "minires/md5.h"
51#include "minires/minires.h"
b992d7e2
DN
52
53#ifdef NSUPDATE
54
55/* Have to use TXT records for now. */
56#define T_DHCID T_TXT
57
0598e123
TL
58/* DN: No way of checking that there is enough space in a data_string's
59 buffer. Be certain to allocate enough!
385fcb27 60 TL: This is why the expression evaluation code allocates a *new*
0598e123 61 data_string. :') */
b992d7e2
DN
62static void data_string_append (struct data_string *ds1,
63 struct data_string *ds2)
64{
65 memcpy (ds1 -> buffer -> data + ds1 -> len,
66 ds2 -> data,
67 ds2 -> len);
68 ds1 -> len += ds2 -> len;
69}
70
ec64b462
TL
71static isc_result_t ddns_update_a (struct data_string *ddns_fwd_name,
72 struct iaddr ddns_addr,
73 struct data_string *ddns_dhcid,
74 unsigned long ttl)
b992d7e2 75{
385fcb27 76 ns_updque updqueue;
b992d7e2 77 ns_updrec *updrec;
ec64b462 78 isc_result_t result;
bdad826f
TL
79 char ddns_address [16];
80
81 if (ddns_addr.len != 4)
ec64b462 82 return ISC_R_INVALIDARG;
bdad826f
TL
83#ifndef NO_SNPRINTF
84 snprintf (ddns_address, 16, "%d.%d.%d.%d",
85 ddns_addr.iabuf[0], ddns_addr.iabuf[1],
86 ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
87#else
88 sprintf (ddns_address, "%d.%d.%d.%d",
89 ddns_addr.iabuf[0], ddns_addr.iabuf[1],
90 ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
91#endif
b992d7e2
DN
92
93 /*
94 * When a DHCP client or server intends to update an A RR, it first
95 * prepares a DNS UPDATE query which includes as a prerequisite the
96 * assertion that the name does not exist. The update section of the
97 * query attempts to add the new name and its IP address mapping (an A
98 * RR), and the DHCID RR with its unique client-identity.
99 * -- "Interaction between DHCP and DNS"
100 */
101
385fcb27 102 ISC_LIST_INIT (updqueue);
b992d7e2
DN
103
104 /*
105 * A RR does not exist.
106 */
478028e7
TL
107 updrec = minires_mkupdrec (S_PREREQ,
108 (const char *)ddns_fwd_name -> data,
b992d7e2 109 C_IN, T_A, 0);
ec64b462
TL
110 if (!updrec) {
111 result = ISC_R_NOMEMORY;
112 goto error;
113 }
b992d7e2 114
478028e7
TL
115 updrec -> r_data = (unsigned char *)0;
116 updrec -> r_size = 0;
b992d7e2
DN
117 updrec -> r_opcode = NXDOMAIN;
118
385fcb27 119 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2
DN
120
121
122 /*
123 * Add A RR.
124 */
478028e7
TL
125 updrec = minires_mkupdrec (S_UPDATE,
126 (const char *)ddns_fwd_name -> data,
b992d7e2 127 C_IN, T_A, ttl);
ec64b462
TL
128 if (!updrec) {
129 result = ISC_R_NOMEMORY;
130 goto error;
131 }
b992d7e2 132
478028e7 133 updrec -> r_data = (unsigned char *)ddns_address;
bdad826f 134 updrec -> r_size = strlen (ddns_address);
b992d7e2
DN
135 updrec -> r_opcode = ADD;
136
385fcb27 137 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2
DN
138
139
140 /*
141 * Add DHCID RR.
142 */
478028e7
TL
143 updrec = minires_mkupdrec (S_UPDATE,
144 (const char *)ddns_fwd_name -> data,
b992d7e2 145 C_IN, T_DHCID, ttl);
ec64b462
TL
146 if (!updrec) {
147 result = ISC_R_NOMEMORY;
148 goto error;
149 }
b992d7e2 150
ee83afae 151 updrec -> r_data = ddns_dhcid -> data;
b992d7e2
DN
152 updrec -> r_size = ddns_dhcid -> len;
153 updrec -> r_opcode = ADD;
154
385fcb27 155 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2
DN
156
157
158 /*
159 * Attempt to perform the update.
160 */
385fcb27
TL
161 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
162
163 print_dns_status ((int)result, &updqueue);
b992d7e2 164
385fcb27
TL
165 while (!ISC_LIST_EMPTY (updqueue)) {
166 updrec = ISC_LIST_HEAD (updqueue);
167 ISC_LIST_UNLINK (updqueue, updrec, r_link);
b992d7e2
DN
168 minires_freeupdrec (updrec);
169 }
170
171
172 /*
173 * If this update operation succeeds, the updater can conclude that it
174 * has added a new name whose only RRs are the A and DHCID RR records.
175 * The A RR update is now complete (and a client updater is finished,
176 * while a server might proceed to perform a PTR RR update).
177 * -- "Interaction between DHCP and DNS"
178 */
179
ec64b462 180 if (result == ISC_R_SUCCESS)
b992d7e2
DN
181 return result;
182
183
184 /*
185 * If the first update operation fails with YXDOMAIN, the updater can
186 * conclude that the intended name is in use. The updater then
187 * attempts to confirm that the DNS name is not being used by some
188 * other host. The updater prepares a second UPDATE query in which the
189 * prerequisite is that the desired name has attached to it a DHCID RR
190 * whose contents match the client identity. The update section of
191 * this query deletes the existing A records on the name, and adds the
192 * A record that matches the DHCP binding and the DHCID RR with the
193 * client identity.
194 * -- "Interaction between DHCP and DNS"
195 */
196
ec64b462 197 if (result != ISC_R_YXDOMAIN)
b992d7e2
DN
198 return result;
199
200
201 /*
202 * DHCID RR exists, and matches client identity.
203 */
478028e7
TL
204 updrec = minires_mkupdrec (S_PREREQ,
205 (const char *)ddns_fwd_name -> data,
b992d7e2 206 C_IN, T_DHCID, 0);
ec64b462
TL
207 if (!updrec) {
208 result = ISC_R_NOMEMORY;
209 goto error;
210 }
b992d7e2 211
ee83afae 212 updrec -> r_data = ddns_dhcid -> data;
b992d7e2
DN
213 updrec -> r_size = ddns_dhcid -> len;
214 updrec -> r_opcode = YXRRSET;
215
385fcb27 216 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2
DN
217
218
219 /*
220 * Delete A RRset.
221 */
478028e7
TL
222 updrec = minires_mkupdrec (S_UPDATE,
223 (const char *)ddns_fwd_name -> data,
b992d7e2 224 C_IN, T_A, 0);
ec64b462
TL
225 if (!updrec) {
226 result = ISC_R_NOMEMORY;
227 goto error;
228 }
b992d7e2
DN
229
230 updrec -> r_data = (unsigned char *)0;
231 updrec -> r_size = 0;
232 updrec -> r_opcode = DELETE;
233
385fcb27 234 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2
DN
235
236
237 /*
238 * Add A RR.
239 */
478028e7
TL
240 updrec = minires_mkupdrec (S_UPDATE,
241 (const char *)ddns_fwd_name -> data,
b992d7e2 242 C_IN, T_A, ttl);
ec64b462
TL
243 if (!updrec) {
244 result = ISC_R_NOMEMORY;
245 goto error;
246 }
b992d7e2 247
478028e7 248 updrec -> r_data = (unsigned char *)ddns_address;
bdad826f 249 updrec -> r_size = strlen (ddns_address);
b992d7e2
DN
250 updrec -> r_opcode = ADD;
251
385fcb27 252 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2
DN
253
254
255 /*
256 * Attempt to perform the update.
257 */
385fcb27
TL
258 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
259
260 print_dns_status ((int)result, &updqueue);
b992d7e2 261
385fcb27
TL
262 while (!ISC_LIST_EMPTY (updqueue)) {
263 updrec = ISC_LIST_HEAD (updqueue);
264 ISC_LIST_UNLINK (updqueue, updrec, r_link);
b992d7e2
DN
265 minires_freeupdrec (updrec);
266 }
267
268
269 /*
270 * If this query succeeds, the updater can conclude that the current
271 * client was the last client associated with the domain name, and that
272 * the name now contains the updated A RR. The A RR update is now
273 * complete (and a client updater is finished, while a server would
274 * then proceed to perform a PTR RR update).
275 * -- "Interaction between DHCP and DNS"
276 */
277
ec64b462 278 if (result == ISC_R_SUCCESS)
b992d7e2
DN
279 return result;
280
281
282 /*
283 * If the second query fails with NXRRSET, the updater must conclude
284 * that the client's desired name is in use by another host. At this
285 * juncture, the updater can decide (based on some administrative
286 * configuration outside of the scope of this document) whether to let
287 * the existing owner of the name keep that name, and to (possibly)
288 * perform some name disambiguation operation on behalf of the current
289 * client, or to replace the RRs on the name with RRs that represent
290 * the current client. If the configured policy allows replacement of
291 * existing records, the updater submits a query that deletes the
292 * existing A RR and the existing DHCID RR, adding A and DHCID RRs that
293 * represent the IP address and client-identity of the new client.
294 * -- "Interaction between DHCP and DNS"
295 */
296
297 return result;
298
299
300 error:
385fcb27
TL
301 while (!ISC_LIST_EMPTY (updqueue)) {
302 updrec = ISC_LIST_HEAD (updqueue);
303 ISC_LIST_UNLINK (updqueue, updrec, r_link);
b992d7e2
DN
304 minires_freeupdrec (updrec);
305 }
306
307 return SERVFAIL;
308}
309
310
311static ns_rcode ddns_update_ptr (struct data_string *ddns_fwd_name,
312 struct data_string *ddns_rev_name,
b992d7e2
DN
313 unsigned long ttl)
314{
385fcb27 315 ns_updque updqueue;
b992d7e2
DN
316 ns_updrec *updrec;
317 ns_rcode result = SERVFAIL;
318
319 /*
320 * The DHCP server submits a DNS query which deletes all of the PTR RRs
321 * associated with the lease IP address, and adds a PTR RR whose data
322 * is the client's (possibly disambiguated) host name. The server also
323 * adds a DHCID RR specified in Section 4.3.
324 * -- "Interaction between DHCP and DNS"
325 */
326
385fcb27 327 ISC_LIST_INIT (updqueue);
b992d7e2
DN
328
329 /*
330 * Delete all PTR RRs.
331 */
478028e7
TL
332 updrec = minires_mkupdrec (S_UPDATE,
333 (const char *)ddns_rev_name -> data,
b992d7e2 334 C_IN, T_PTR, 0);
ec64b462
TL
335 if (!updrec) {
336 result = ISC_R_NOMEMORY;
337 goto error;
338 }
b992d7e2 339
478028e7
TL
340 updrec -> r_data = (unsigned char *)0;
341 updrec -> r_size = 0;
b992d7e2
DN
342 updrec -> r_opcode = DELETE;
343
385fcb27 344 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2 345
b992d7e2
DN
346 /*
347 * Add PTR RR.
348 */
478028e7
TL
349 updrec = minires_mkupdrec (S_UPDATE,
350 (const char *)ddns_rev_name -> data,
b992d7e2 351 C_IN, T_PTR, ttl);
ec64b462
TL
352 if (!updrec) {
353 result = ISC_R_NOMEMORY;
354 goto error;
355 }
b992d7e2 356
ee83afae 357 updrec -> r_data = ddns_fwd_name -> data;
478028e7 358 updrec -> r_size = ddns_fwd_name -> len;
b992d7e2
DN
359 updrec -> r_opcode = ADD;
360
385fcb27 361 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2 362
b992d7e2
DN
363 /*
364 * Attempt to perform the update.
365 */
385fcb27
TL
366 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
367 print_dns_status ((int)result, &updqueue);
b992d7e2
DN
368
369 /* Fall through. */
ec64b462 370 error:
b992d7e2 371
385fcb27
TL
372 while (!ISC_LIST_EMPTY (updqueue)) {
373 updrec = ISC_LIST_HEAD (updqueue);
374 ISC_LIST_UNLINK (updqueue, updrec, r_link);
b992d7e2
DN
375 minires_freeupdrec (updrec);
376 }
377
378 return result;
379}
380
381
382static ns_rcode ddns_remove_a (struct data_string *ddns_fwd_name,
bdad826f 383 struct iaddr ddns_addr,
b992d7e2
DN
384 struct data_string *ddns_dhcid)
385{
385fcb27 386 ns_updque updqueue;
b992d7e2
DN
387 ns_updrec *updrec;
388 ns_rcode result = SERVFAIL;
bdad826f
TL
389 char ddns_address [16];
390
391 if (ddns_addr.len != 4)
392 return SERVFAIL;
393
394#ifndef NO_SNPRINTF
395 snprintf (ddns_address, 16, "%d.%d.%d.%d",
396 ddns_addr.iabuf[0], ddns_addr.iabuf[1],
397 ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
398#else
399 sprintf (ddns_address, "%d.%d.%d.%d",
400 ddns_addr.iabuf[0], ddns_addr.iabuf[1],
401 ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
402#endif
403
b992d7e2
DN
404
405 /*
406 * The entity chosen to handle the A record for this client (either the
407 * client or the server) SHOULD delete the A record that was added when
408 * the lease was made to the client.
409 *
410 * In order to perform this delete, the updater prepares an UPDATE
411 * query which contains two prerequisites. The first prerequisite
412 * asserts that the DHCID RR exists whose data is the client identity
413 * described in Section 4.3. The second prerequisite asserts that the
414 * data in the A RR contains the IP address of the lease that has
415 * expired or been released.
416 * -- "Interaction between DHCP and DNS"
417 */
418
385fcb27 419 ISC_LIST_INIT (updqueue);
b992d7e2
DN
420
421 /*
422 * DHCID RR exists, and matches client identity.
423 */
87fec475
TL
424 updrec = minires_mkupdrec (S_PREREQ,
425 (const char *)ddns_fwd_name -> data,
b992d7e2 426 C_IN, T_DHCID,0);
ec64b462
TL
427 if (!updrec) {
428 result = ISC_R_NOMEMORY;
429 goto error;
430 }
b992d7e2 431
ee83afae 432 updrec -> r_data = ddns_dhcid -> data;
b992d7e2
DN
433 updrec -> r_size = ddns_dhcid -> len;
434 updrec -> r_opcode = YXRRSET;
435
385fcb27 436 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2
DN
437
438
439 /*
440 * A RR matches the expiring lease.
441 */
87fec475
TL
442 updrec = minires_mkupdrec (S_PREREQ,
443 (const char *)ddns_fwd_name -> data,
b992d7e2 444 C_IN, T_A, 0);
ec64b462
TL
445 if (!updrec) {
446 result = ISC_R_NOMEMORY;
447 goto error;
448 }
b992d7e2 449
87fec475 450 updrec -> r_data = (unsigned char *)ddns_address;
bdad826f 451 updrec -> r_size = strlen (ddns_address);
b992d7e2
DN
452 updrec -> r_opcode = YXRRSET;
453
385fcb27 454 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2
DN
455
456
457 /*
458 * Delete appropriate A RR.
459 */
87fec475
TL
460 updrec = minires_mkupdrec (S_UPDATE,
461 (const char *)ddns_fwd_name -> data,
b992d7e2 462 C_IN, T_A, 0);
ec64b462
TL
463 if (!updrec) {
464 result = ISC_R_NOMEMORY;
465 goto error;
466 }
b992d7e2 467
87fec475 468 updrec -> r_data = (unsigned char *)ddns_address;
478028e7 469 updrec -> r_size = strlen (ddns_address);
b992d7e2
DN
470 updrec -> r_opcode = DELETE;
471
385fcb27 472 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2 473
b992d7e2
DN
474 /*
475 * Attempt to perform the update.
476 */
385fcb27
TL
477 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
478 print_dns_status ((int)result, &updqueue);
b992d7e2
DN
479
480 /*
481 * If the query fails, the updater MUST NOT delete the DNS name. It
482 * may be that the host whose lease on the server has expired has moved
483 * to another network and obtained a lease from a different server,
484 * which has caused the client's A RR to be replaced. It may also be
485 * that some other client has been configured with a name that matches
486 * the name of the DHCP client, and the policy was that the last client
487 * to specify the name would get the name. In this case, the DHCID RR
488 * will no longer match the updater's notion of the client-identity of
489 * the host pointed to by the DNS name.
490 * -- "Interaction between DHCP and DNS"
491 */
492
ec64b462 493 if (result != ISC_R_SUCCESS)
bdad826f
TL
494 goto error;
495
496 while (!ISC_LIST_EMPTY (updqueue)) {
497 updrec = ISC_LIST_HEAD (updqueue);
498 ISC_LIST_UNLINK (updqueue, updrec, r_link);
499 minires_freeupdrec (updrec);
500 }
501
502 /* If the deletion of the A succeeded, and there are no A records
503 left for this domain, then we can blow away the DHCID record
504 as well. We can't blow away the DHCID record above because
505 it's possible that more than one A has been added to this
506 domain name. */
507 ISC_LIST_INIT (updqueue);
508
509 /*
510 * A RR does not exist.
511 */
87fec475
TL
512 updrec = minires_mkupdrec (S_PREREQ,
513 (const char *)ddns_fwd_name -> data,
bdad826f 514 C_IN, T_A, 0);
ec64b462
TL
515 if (!updrec) {
516 result = ISC_R_NOMEMORY;
517 goto error;
518 }
bdad826f 519
478028e7 520 updrec -> r_data = (unsigned char *)0;
bdad826f
TL
521 updrec -> r_size = 0;
522 updrec -> r_opcode = NXRRSET;
523
524 ISC_LIST_APPEND (updqueue, updrec, r_link);
525
526 /*
527 * Delete appropriate DHCID RR.
528 */
87fec475
TL
529 updrec = minires_mkupdrec (S_UPDATE,
530 (const char *)ddns_fwd_name -> data,
bdad826f 531 C_IN, T_DHCID, 0);
ec64b462
TL
532 if (!updrec) {
533 result = ISC_R_NOMEMORY;
bdad826f 534 goto error;
ec64b462 535 }
bdad826f 536
ee83afae 537 updrec -> r_data = ddns_dhcid -> data;
478028e7 538 updrec -> r_size = ddns_dhcid -> len;
bdad826f
TL
539 updrec -> r_opcode = DELETE;
540
541 ISC_LIST_APPEND (updqueue, updrec, r_link);
542
543 /*
544 * Attempt to perform the update.
545 */
546 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
547 print_dns_status ((int)result, &updqueue);
b992d7e2
DN
548
549 /* Fall through. */
550 error:
551
385fcb27
TL
552 while (!ISC_LIST_EMPTY (updqueue)) {
553 updrec = ISC_LIST_HEAD (updqueue);
554 ISC_LIST_UNLINK (updqueue, updrec, r_link);
b992d7e2
DN
555 minires_freeupdrec (updrec);
556 }
557
558 return result;
559}
560
561
ee83afae 562static ns_rcode ddns_remove_ptr (struct data_string *ddns_rev_name)
b992d7e2 563{
385fcb27 564 ns_updque updqueue;
b992d7e2 565 ns_updrec *updrec;
ec64b462 566 isc_result_t result;
b992d7e2
DN
567
568 /*
569 * When a lease expires or a DHCP client issues a DHCPRELEASE request,
570 * the DHCP server SHOULD delete the PTR RR that matches the DHCP
571 * binding, if one was successfully added. The server's update query
572 * SHOULD assert that the name in the PTR record matches the name of
573 * the client whose lease has expired or been released.
574 * -- "Interaction between DHCP and DNS"
575 */
576
385fcb27 577 ISC_LIST_INIT (updqueue);
b992d7e2
DN
578
579 /*
ee83afae 580 * Delete the PTR RRset for the leased address.
b992d7e2 581 */
478028e7
TL
582 updrec = minires_mkupdrec (S_UPDATE,
583 (const char *)ddns_rev_name -> data,
b992d7e2 584 C_IN, T_PTR, 0);
ec64b462
TL
585 if (!updrec) {
586 result = ISC_R_NOMEMORY;
587 goto error;
588 }
b992d7e2 589
ee83afae
TL
590 updrec -> r_data = (unsigned char *)0;
591 updrec -> r_size = 0;
b992d7e2
DN
592 updrec -> r_opcode = DELETE;
593
385fcb27 594 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2 595
b992d7e2
DN
596 /*
597 * Attempt to perform the update.
598 */
385fcb27
TL
599 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
600 print_dns_status ((int)result, &updqueue);
b992d7e2
DN
601
602 /* Fall through. */
ec64b462 603 error:
b992d7e2 604
385fcb27
TL
605 while (!ISC_LIST_EMPTY (updqueue)) {
606 updrec = ISC_LIST_HEAD (updqueue);
607 ISC_LIST_UNLINK (updqueue, updrec, r_link);
b992d7e2
DN
608 minires_freeupdrec (updrec);
609 }
610
611 return result;
612}
613
614
615int ddns_updates (struct packet *packet,
ec64b462 616 struct lease *lease, struct lease *old,
b992d7e2
DN
617 struct lease_state *state)
618{
619 unsigned long ddns_ttl = DEFAULT_DDNS_TTL;
620 struct data_string ddns_hostname;
621 struct data_string ddns_domainname;
478028e7 622 struct data_string old_ddns_fwd_name;
b992d7e2
DN
623 struct data_string ddns_fwd_name;
624 struct data_string ddns_rev_name;
b992d7e2
DN
625 struct data_string ddns_dhcid;
626 unsigned len;
b992d7e2
DN
627 struct data_string d1;
628 struct option_cache *oc;
629 int s1, s2;
630 int result = 0;
ec64b462 631 isc_result_t rcode1 = ISC_R_SUCCESS, rcode2 = ISC_R_SUCCESS;
478028e7
TL
632 int server_updates_a = 1;
633 struct buffer *bp = (struct buffer *)0;
634 int ignorep = 0;
b992d7e2 635
5fac73d6
TL
636 if (ddns_update_style != 2)
637 return 0;
638
b992d7e2
DN
639 /* Can only cope with IPv4 addrs at the moment. */
640 if (lease -> ip_addr . len != 4)
641 return 0;
642
b992d7e2
DN
643 memset (&ddns_hostname, 0, sizeof (ddns_hostname));
644 memset (&ddns_domainname, 0, sizeof (ddns_domainname));
478028e7 645 memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name));
b992d7e2
DN
646 memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
647 memset (&ddns_rev_name, 0, sizeof (ddns_rev_name));
b992d7e2
DN
648 memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
649
478028e7
TL
650 /* If we are allowed to accept the client's update of its own A
651 record, see if the client wants to update its own A record. */
652 if (!(oc = lookup_option (&server_universe, state -> options,
ebd0310b 653 SV_CLIENT_UPDATES)) ||
478028e7 654 evaluate_boolean_option_cache (&ignorep, packet, lease,
b992d7e2
DN
655 (struct client_state *)0,
656 packet -> options,
657 state -> options,
658 &lease -> scope, oc, MDL)) {
478028e7
TL
659 /* If there's no fqdn.no-client-update or if it's
660 nonzero, don't try to use the client-supplied
661 XXX */
ee83afae 662 if (!(oc = lookup_option (&fqdn_universe, packet -> options,
478028e7
TL
663 FQDN_NO_CLIENT_UPDATE)) ||
664 evaluate_boolean_option_cache (&ignorep, packet, lease,
665 (struct client_state *)0,
666 packet -> options,
667 state -> options,
668 &lease -> scope, oc, MDL))
669 goto noclient;
e9d45f83
TL
670 /* Win98 and Win2k will happily claim to be willing to
671 update an unqualified domain name. */
672 if (!(oc = lookup_option (&fqdn_universe, packet -> options,
673 FQDN_DOMAINNAME)))
674 goto noclient;
ee83afae 675 if (!(oc = lookup_option (&fqdn_universe, packet -> options,
478028e7 676 FQDN_FQDN)) ||
ee83afae
TL
677 !evaluate_option_cache (&ddns_fwd_name, packet, lease,
678 (struct client_state *)0,
679 packet -> options,
680 state -> options,
681 &lease -> scope, oc, MDL))
478028e7
TL
682 goto noclient;
683 server_updates_a = 0;
684 goto client_updates;
b992d7e2 685 }
478028e7 686 noclient:
b992d7e2 687
ec64b462
TL
688 /* If it's a static lease, then don't do the DNS update unless we're
689 specifically configured to do so. If the client asked to do its
690 own update and we allowed that, we don't do this test. */
691 if (lease -> flags & STATIC_LEASE) {
692 if (!(oc = lookup_option (&fqdn_universe, packet -> options,
693 SV_UPDATE_STATIC_LEASES)) ||
694 !evaluate_boolean_option_cache (&ignorep, packet, lease,
695 (struct client_state *)0,
696 packet -> options,
697 state -> options,
698 &lease -> scope, oc, MDL))
699 return 0;
700 }
701
b992d7e2 702 /*
478028e7 703 * Compute the name for the A record.
b992d7e2
DN
704 */
705 s1 = s2 = 0;
706
707 oc = lookup_option (&server_universe, state -> options,
708 SV_DDNS_HOST_NAME);
709 if (oc)
710 s1 = evaluate_option_cache (&ddns_hostname, packet, lease,
711 (struct client_state *)0,
712 packet -> options,
713 state -> options,
714 &lease -> scope, oc, MDL);
715
716 oc = lookup_option (&server_universe, state -> options,
717 SV_DDNS_DOMAIN_NAME);
718 if (oc)
719 s2 = evaluate_option_cache (&ddns_domainname, packet, lease,
720 (struct client_state *)0,
721 packet -> options,
722 state -> options,
723 &lease -> scope, oc, MDL);
724
725 if (s1 && s2) {
726 buffer_allocate (&ddns_fwd_name.buffer,
727 ddns_hostname.len + ddns_domainname.len + 2,
728 MDL);
729 if (ddns_fwd_name.buffer) {
730 ddns_fwd_name.data = ddns_fwd_name.buffer -> data;
731 data_string_append (&ddns_fwd_name, &ddns_hostname);
732 ddns_fwd_name.buffer -> data [ddns_fwd_name.len] = '.';
733 ddns_fwd_name.len++;
734 data_string_append (&ddns_fwd_name, &ddns_domainname);
735 ddns_fwd_name.buffer -> data [ddns_fwd_name.len] ='\0';
736 ddns_fwd_name.terminated = 1;
737 }
738 }
478028e7
TL
739 client_updates:
740
741 /* See if there's a name already stored on the lease. */
742 if (find_bound_string (&old_ddns_fwd_name,
743 lease -> scope, "ddns-fwd-name")) {
744 /* If there is, see if it's different. */
745 if (old_ddns_fwd_name.len != ddns_fwd_name.len ||
746 memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
747 old_ddns_fwd_name.len)) {
748 /* If the name is different, try to delete
749 the old A record. */
750 if (!ddns_removals (lease))
751 goto out;
752 /* If the delete succeeded, go install the new
753 record. */
754 goto in;
755 }
756
757 /* See if there's a DHCID on the lease. */
758 if (!find_bound_string (&ddns_dhcid,
759 lease -> scope, "ddns-txt")) {
760 /* If there's no DHCID, the update was probably
761 done with the old-style ad-hoc DDNS updates.
762 So if the expiry and release events look like
763 they're the same, run them. This should delete
764 the old DDNS data. */
ec64b462 765 if (old -> on_expiry == old -> on_release) {
478028e7
TL
766 execute_statements ((struct binding_value **)0,
767 (struct packet *)0, lease,
768 (struct client_state *)0,
769 (struct option_state *)0,
770 (struct option_state *)0,
771 &lease -> scope,
ec64b462
TL
772 old -> on_expiry);
773 if (old -> on_expiry)
478028e7 774 executable_statement_dereference
ec64b462
TL
775 (&old -> on_expiry, MDL);
776 if (old -> on_release)
478028e7 777 executable_statement_dereference
ec64b462 778 (&old -> on_release, MDL);
478028e7
TL
779 /* Now, install the DDNS data the new way. */
780 goto in;
781 }
782 }
783
ebd0310b
TL
784 /* See if the administrator wants to do updates even
785 in cases where the update already appears to have been
786 done. */
787 if (!(oc = lookup_option (&server_universe, state -> options,
788 SV_UPDATE_OPTIMIZATION)) ||
789 evaluate_boolean_option_cache (&ignorep, packet, lease,
790 (struct client_state *)0,
791 packet -> options,
792 state -> options,
793 &lease -> scope, oc, MDL)) {
794 result = 1;
795 goto noerror;
796 }
478028e7 797 }
b992d7e2 798
478028e7
TL
799 /* If there's no ddns-fwd-name on the lease, see if there's
800 a ddns-client-fqdn, indicating a prior client FQDN update.
801 If there is, and if we're still doing the client update,
802 see if the name has changed. If it hasn't, don't do the
803 PTR update. */
804 if (find_bound_string (&old_ddns_fwd_name,
805 lease -> scope, "ddns-client-fqdn")) {
ee83afae
TL
806 if (old_ddns_fwd_name.len == ddns_fwd_name.len &&
807 !memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
808 old_ddns_fwd_name.len)) {
478028e7
TL
809 /* If the name is not different, no need to update
810 the PTR record. */
811 goto noerror;
812 }
813 }
814 in:
815
b992d7e2 816 /*
478028e7
TL
817 * Compute the RR TTL.
818 */
819 ddns_ttl = DEFAULT_DDNS_TTL;
820 memset (&d1, 0, sizeof d1);
821 if ((oc = lookup_option (&server_universe, state -> options,
822 SV_DDNS_TTL))) {
823 if (evaluate_option_cache (&d1, packet, lease,
824 (struct client_state *)0,
825 packet -> options,
826 state -> options,
827 &lease -> scope, oc, MDL)) {
828 if (d1.len == sizeof (u_int32_t))
829 ddns_ttl = getULong (d1.data);
830 data_string_forget (&d1, MDL);
831 }
832 }
833
834
835 /*
836 * Compute the reverse IP name.
b992d7e2
DN
837 */
838 oc = lookup_option (&server_universe, state -> options,
839 SV_DDNS_REV_DOMAIN_NAME);
840 if (oc)
841 s1 = evaluate_option_cache (&d1, packet, lease,
842 (struct client_state *)0,
843 packet -> options,
844 state -> options,
845 &lease -> scope, oc, MDL);
846
847 if (oc && s1) {
848 /* Buffer length:
849 XXX.XXX.XXX.XXX.<ddns-rev-domain-name>\0 */
850 buffer_allocate (&ddns_rev_name.buffer,
851 d1.len + 17, MDL);
852 if (ddns_rev_name.buffer) {
853 ddns_rev_name.data = ddns_rev_name.buffer -> data;
854#ifndef NO_SNPRINTF
87fec475 855 snprintf ((char *)ddns_rev_name.buffer -> data, 17,
b992d7e2
DN
856 "%d.%d.%d.%d.",
857 lease -> ip_addr . iabuf[3],
858 lease -> ip_addr . iabuf[2],
859 lease -> ip_addr . iabuf[1],
860 lease -> ip_addr . iabuf[0]);
861#else
87fec475 862 sprintf ((char *)ddns_rev_name.buffer -> data,
b992d7e2
DN
863 "%d.%d.%d.%d.",
864 lease -> ip_addr . iabuf[3],
865 lease -> ip_addr . iabuf[2],
866 lease -> ip_addr . iabuf[1],
867 lease -> ip_addr . iabuf[0]);
868#endif
478028e7
TL
869 ddns_rev_name.len =
870 strlen ((const char *)ddns_rev_name.data);
b992d7e2
DN
871 data_string_append (&ddns_rev_name, &d1);
872 ddns_rev_name.buffer -> data [ddns_rev_name.len] ='\0';
873 ddns_rev_name.terminated = 1;
874 }
875
876 data_string_forget (&d1, MDL);
877 }
878
b992d7e2 879 /*
478028e7 880 * If we are updating the A record, compute the DHCID value.
b992d7e2 881 */
478028e7
TL
882 if (server_updates_a) {
883 memset (&ddns_dhcid, 0, sizeof ddns_dhcid);
884 get_dhcid (&ddns_dhcid, lease);
885 }
b992d7e2 886
b992d7e2
DN
887 /*
888 * Start the resolver, if necessary.
889 */
890 if (!resolver_inited) {
891 minires_ninit (&resolver_state);
892 resolver_inited = 1;
ec64b462
TL
893 resolver_state.retrans = 1;
894 resolver_state.retry = 1;
b992d7e2
DN
895 }
896
b992d7e2
DN
897 /*
898 * Perform updates.
899 */
bdad826f
TL
900 if (ddns_fwd_name.len && ddns_dhcid.len)
901 rcode1 = ddns_update_a (&ddns_fwd_name, lease -> ip_addr,
902 &ddns_dhcid, ddns_ttl);
b992d7e2 903
ec64b462 904 if (rcode1 == ISC_R_SUCCESS) {
478028e7 905 if (ddns_fwd_name.len && ddns_rev_name.len)
bdad826f
TL
906 rcode2 = ddns_update_ptr (&ddns_fwd_name,
907 &ddns_rev_name, ddns_ttl);
908 } else
ec64b462 909 rcode2 = rcode1;
b992d7e2 910
ec64b462
TL
911 if (rcode1 == ISC_R_SUCCESS &&
912 (server_updates_a || rcode2 == ISC_R_SUCCESS)) {
478028e7
TL
913 bind_ds_value (&lease -> scope,
914 (server_updates_a
915 ? "ddns-fwd-name" : "ddns-client-fqdn"),
b992d7e2 916 &ddns_fwd_name);
ee83afae
TL
917 if (server_updates_a)
918 bind_ds_value (&lease -> scope, "ddns-txt",
919 &ddns_dhcid);
b992d7e2 920 }
bdad826f 921
ec64b462 922 if (rcode2 == ISC_R_SUCCESS) {
b992d7e2
DN
923 bind_ds_value (&lease -> scope, "ddns-rev-name",
924 &ddns_rev_name);
925 }
926
478028e7 927 /* Set up the outgoing FQDN option if there was an incoming
ee83afae
TL
928 FQDN option. If there's a valid FQDN option, there should
929 be an FQDN_ENCODED suboption, so we test the latter to
930 detect the presence of the former. */
478028e7 931 noerror:
ee83afae
TL
932 if ((oc = lookup_option (&fqdn_universe,
933 packet -> options, FQDN_ENCODED))
478028e7
TL
934 && buffer_allocate (&bp, ddns_fwd_name.len + 5, MDL)) {
935 bp -> data [0] = server_updates_a;
936 if (!save_option_buffer (&fqdn_universe, state -> options,
937 bp, &bp -> data [0], 1,
938 &fqdn_options [FQDN_SERVER_UPDATE],
939 0))
940 goto badfqdn;
941 bp -> data [1] = server_updates_a;
942 if (!save_option_buffer (&fqdn_universe, state -> options,
943 bp, &bp -> data [1], 1,
944 &fqdn_options [FQDN_NO_CLIENT_UPDATE],
945 0))
946 goto badfqdn;
947 /* Do the same encoding the client did. */
948 oc = lookup_option (&fqdn_universe, packet -> options,
949 FQDN_ENCODED);
950 if (oc &&
951 evaluate_boolean_option_cache (&ignorep, packet, lease,
952 (struct client_state *)0,
953 packet -> options,
954 state -> options,
955 &lease -> scope, oc, MDL))
956 bp -> data [2] = 1;
957 else
958 bp -> data [2] = 0;
959 if (!save_option_buffer (&fqdn_universe, state -> options,
960 bp, &bp -> data [2], 1,
961 &fqdn_options [FQDN_ENCODED],
962 0))
963 goto badfqdn;
ec64b462 964 bp -> data [3] = isc_rcode_to_ns (rcode1);
478028e7
TL
965 if (!save_option_buffer (&fqdn_universe, state -> options,
966 bp, &bp -> data [3], 1,
967 &fqdn_options [FQDN_RCODE1],
968 0))
969 goto badfqdn;
ec64b462 970 bp -> data [4] = isc_rcode_to_ns (rcode2);
478028e7
TL
971 if (!save_option_buffer (&fqdn_universe, state -> options,
972 bp, &bp -> data [4], 1,
973 &fqdn_options [FQDN_RCODE2],
974 0))
975 goto badfqdn;
976 if (ddns_fwd_name.len) {
977 memcpy (&bp -> data [5],
978 ddns_fwd_name.data, ddns_fwd_name.len);
979 if (!save_option_buffer (&fqdn_universe, state -> options,
980 bp, &bp -> data [5],
981 ddns_fwd_name.len,
982 &fqdn_options [FQDN_FQDN],
983 0))
984 goto badfqdn;
985 }
b992d7e2
DN
986 }
987
478028e7
TL
988 badfqdn:
989 out:
b992d7e2
DN
990 /*
991 * Final cleanup.
992 */
993 data_string_forget (&ddns_hostname, MDL);
994 data_string_forget (&ddns_domainname, MDL);
478028e7 995 data_string_forget (&old_ddns_fwd_name, MDL);
b992d7e2
DN
996 data_string_forget (&ddns_fwd_name, MDL);
997 data_string_forget (&ddns_rev_name, MDL);
b992d7e2
DN
998 data_string_forget (&ddns_dhcid, MDL);
999
478028e7 1000 return result;
b992d7e2
DN
1001}
1002
478028e7 1003int ddns_removals (struct lease *lease)
385fcb27 1004{
b992d7e2
DN
1005 struct data_string ddns_fwd_name;
1006 struct data_string ddns_rev_name;
b992d7e2 1007 struct data_string ddns_dhcid;
ec64b462 1008 isc_result_t rcode;
385fcb27 1009 struct binding *binding;
478028e7 1010 int result = 0;
ee83afae 1011 int client_updated = 0;
b992d7e2
DN
1012
1013 /* No scope implies that DDNS has not been performed for this lease. */
1014 if (!lease -> scope)
478028e7 1015 return 0;
b992d7e2 1016
b992d7e2
DN
1017 /*
1018 * Look up stored names.
1019 */
1020 memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
1021 memset (&ddns_rev_name, 0, sizeof (ddns_rev_name));
b992d7e2
DN
1022 memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
1023
b992d7e2
DN
1024 /*
1025 * Start the resolver, if necessary.
1026 */
1027 if (!resolver_inited) {
1028 minires_ninit (&resolver_state);
1029 resolver_inited = 1;
1030 }
1031
478028e7
TL
1032 /* We need the fwd name whether we are deleting both records or just
1033 the PTR record, so if it's not there, we can't proceed. */
1034 if (!find_bound_string (&ddns_fwd_name,
ee83afae
TL
1035 lease -> scope, "ddns-fwd-name")) {
1036 /* If there's no ddns-fwd-name, look for the client fqdn,
1037 in case the client did the update. */
1038 if (!find_bound_string (&ddns_fwd_name,
1039 lease -> scope, "ddns-client-fqdn"))
1040 return 0;
1041 client_updated = 1;
1042 goto try_rev;
1043 }
478028e7
TL
1044
1045 /* If the ddns-txt binding isn't there, this isn't an interim
1046 or rfc3??? record, so we can't delete the A record using
1047 this mechanism, but we can delete the PTR record. */
1048 if (!find_bound_string (&ddns_dhcid, lease -> scope, "ddns-txt")) {
1049 result = 1;
1050 goto try_rev;
1051 }
1052
b992d7e2
DN
1053 /*
1054 * Perform removals.
1055 */
478028e7
TL
1056 rcode = ddns_remove_a (&ddns_fwd_name, lease -> ip_addr, &ddns_dhcid);
1057
ec64b462 1058 if (rcode == ISC_R_SUCCESS) {
478028e7
TL
1059 result = 1;
1060 unset (lease -> scope, "ddns-fwd-name");
1061 unset (lease -> scope, "ddns-txt");
1062 try_rev:
1063 if (find_bound_string (&ddns_rev_name,
1064 lease -> scope, "ddns-rev-name")) {
ee83afae 1065 if (ddns_remove_ptr(&ddns_rev_name) == NOERROR) {
478028e7 1066 unset (lease -> scope, "ddns-rev-name");
ee83afae
TL
1067 if (client_updated)
1068 unset (lease -> scope,
1069 "ddns-client-fqdn");
1070 } else
478028e7
TL
1071 result = 0;
1072 }
b992d7e2
DN
1073 }
1074
b992d7e2
DN
1075 data_string_forget (&ddns_fwd_name, MDL);
1076 data_string_forget (&ddns_rev_name, MDL);
b992d7e2
DN
1077 data_string_forget (&ddns_dhcid, MDL);
1078
478028e7 1079 return result;
b992d7e2
DN
1080}
1081
b992d7e2 1082#endif /* NSUPDATE */