1 /** @file 2 Copyright (c) 1999 - 2014, Intel Corporation. All rights reserved.<BR> 3 This program and the accompanying materials are licensed and made available 4 under the terms and conditions of the BSD License which accompanies this 5 distribution. The full text of the license may be found at 6 http://opensource.org/licenses/bsd-license.php. 7 8 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 9 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 10 **/ 11 /* 12 * Copyright (c) 1996 by Internet Software Consortium. 13 * 14 * Permission to use, copy, modify, and distribute this software for any 15 * purpose with or without fee is hereby granted, provided that the above 16 * copyright notice and this permission notice appear in all copies. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 19 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 21 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 22 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 23 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 24 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 25 * SOFTWARE. 26 */ 27 28 /* 29 * Portions copyright (c) 1999, 2000 30 * Intel Corporation. 31 * All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 37 * 1. Redistributions of source code must retain the above copyright 38 * notice, this list of conditions and the following disclaimer. 39 * 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 44 * 3. All advertising materials mentioning features or use of this software 45 * must display the following acknowledgement: 46 * 47 * This product includes software developed by Intel Corporation and 48 * its contributors. 49 * 50 * 4. Neither the name of Intel Corporation or its contributors may be 51 * used to endorse or promote products derived from this software 52 * without specific prior written permission. 53 * 54 * THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION AND CONTRIBUTORS ``AS IS'' 55 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 57 * ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR CONTRIBUTORS BE 58 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 59 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 60 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 61 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 62 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 63 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 64 * THE POSSIBILITY OF SUCH DAMAGE. 65 * 66 */ 67 68 /* 69 * Based on the Dynamic DNS reference implementation by Viraj Bais 70 * <viraj_bais (at) ccm.fm.intel.com> 71 */ 72 73 #include <sys/param.h> 74 #include <sys/socket.h> 75 #include <sys/time.h> 76 #include <netinet/in.h> 77 #include <arpa/inet.h> 78 #include <arpa/nameser.h> 79 #include <errno.h> 80 #include <limits.h> 81 #include <netdb.h> 82 #include <resolv.h> 83 #include <stdio.h> 84 #include <stdlib.h> 85 #include <string.h> 86 87 /* 88 * Separate a linked list of records into groups so that all records 89 * in a group will belong to a single zone on the nameserver. 90 * Create a dynamic update packet for each zone and send it to the 91 * nameservers for that zone, and await answer. 92 * Abort if error occurs in updating any zone. 93 * Return the number of zones updated on success, < 0 on error. 94 * 95 * On error, caller must deal with the unsynchronized zones 96 * eg. an A record might have been successfully added to the forward 97 * zone but the corresponding PTR record would be missing if error 98 * was encountered while updating the reverse zone. 99 */ 100 101 #define NSMAX 16 102 103 struct ns1 { 104 char nsname[MAXDNAME]; 105 struct in_addr nsaddr1; 106 }; 107 108 struct zonegrp { 109 char z_origin[MAXDNAME]; 110 int16_t z_class; 111 char z_soardata[MAXDNAME + 5 * INT32SZ]; 112 struct ns1 z_ns[NSMAX]; 113 int z_nscount; 114 ns_updrec * z_rr; 115 struct zonegrp *z_next; 116 }; 117 118 119 int 120 res_update(ns_updrec *rrecp_in) { 121 ns_updrec *rrecp, *tmprrecp; 122 u_char buf[PACKETSZ], answer[PACKETSZ], packet[2*PACKETSZ]; 123 char name[MAXDNAME], zname[MAXDNAME], primary[MAXDNAME], 124 mailaddr[MAXDNAME]; 125 u_char soardata[2*MAXCDNAME+5*INT32SZ]; 126 char *dname, *svdname, *cp1, *target; 127 u_char *cp, *eom; 128 HEADER *hp = (HEADER *) answer; 129 struct zonegrp *zptr = NULL, *tmpzptr, *prevzptr, *zgrp_start = NULL; 130 int i, j, k = 0, n, ancount, nscount, arcount, rcode, rdatasize, 131 newgroup, done, myzone, seen_before, numzones = 0; 132 u_int16_t dlen, class, qclass, type, qtype; 133 u_int32_t ttl; 134 135 if ((_res.options & RES_INIT) == 0 && res_init() == -1) { 136 h_errno = NETDB_INTERNAL; 137 return (-1); 138 } 139 140 for (rrecp = rrecp_in; rrecp; rrecp = rrecp->r_next) { 141 dname = rrecp->r_dname; 142 n = (int)strlen(dname); 143 if (dname[n-1] == '.') 144 dname[n-1] = '\0'; 145 qtype = T_SOA; 146 qclass = rrecp->r_class; 147 done = 0; 148 seen_before = 0; 149 150 while (!done && dname) { 151 if (qtype == T_SOA) { 152 for (tmpzptr = zgrp_start; 153 tmpzptr && !seen_before; 154 tmpzptr = tmpzptr->z_next) { 155 if (strcasecmp(dname, 156 tmpzptr->z_origin) == 0 && 157 tmpzptr->z_class == qclass) 158 seen_before++; 159 for (tmprrecp = tmpzptr->z_rr; 160 tmprrecp && !seen_before; 161 tmprrecp = tmprrecp->r_grpnext) 162 if (strcasecmp(dname, tmprrecp->r_dname) == 0 163 && tmprrecp->r_class == qclass) { 164 seen_before++; 165 break; 166 } 167 if (seen_before) { 168 /* 169 * Append to the end of 170 * current group. 171 */ 172 for (tmprrecp = tmpzptr->z_rr; 173 tmprrecp->r_grpnext; 174 tmprrecp = tmprrecp->r_grpnext) 175 (void)NULL; 176 tmprrecp->r_grpnext = rrecp; 177 rrecp->r_grpnext = NULL; 178 done = 1; 179 break; 180 } 181 } 182 } else if (qtype == T_A) { 183 for (tmpzptr = zgrp_start; 184 tmpzptr && !done; 185 tmpzptr = tmpzptr->z_next) 186 for (i = 0; i < tmpzptr->z_nscount; i++) 187 if (tmpzptr->z_class == qclass && 188 strcasecmp(tmpzptr->z_ns[i].nsname, 189 dname) == 0 && 190 tmpzptr->z_ns[i].nsaddr1.s_addr != 0) { 191 zptr->z_ns[k].nsaddr1.s_addr = 192 tmpzptr->z_ns[i].nsaddr1.s_addr; 193 done = 1; 194 break; 195 } 196 } 197 if (done) 198 break; 199 n = res_mkquery(QUERY, dname, qclass, qtype, NULL, 200 0, NULL, buf, sizeof buf); 201 if (n <= 0) { 202 fprintf(stderr, "res_update: mkquery failed\n"); 203 return (n); 204 } 205 n = res_send(buf, n, answer, sizeof answer); 206 if (n < 0) { 207 fprintf(stderr, "res_update: send error for %s\n", 208 rrecp->r_dname); 209 return (n); 210 } 211 if (n < HFIXEDSZ) 212 return (-1); 213 ancount = ntohs(hp->ancount); 214 nscount = ntohs(hp->nscount); 215 arcount = ntohs(hp->arcount); 216 rcode = hp->rcode; 217 cp = answer + HFIXEDSZ; 218 eom = answer + n; 219 /* skip the question section */ 220 n = dn_skipname(cp, eom); 221 if (n < 0 || cp + n + 2 * INT16SZ > eom) 222 return (-1); 223 cp += n + 2 * INT16SZ; 224 225 if (qtype == T_SOA) { 226 if (ancount == 0 && nscount == 0 && arcount == 0) { 227 /* 228 * if (rcode == NOERROR) then the dname exists but 229 * has no soa record associated with it. 230 * if (rcode == NXDOMAIN) then the dname does not 231 * exist and the server is replying out of NCACHE. 232 * in either case, proceed with the next try 233 */ 234 dname = strchr(dname, '.'); 235 if (dname != NULL) 236 dname++; 237 continue; 238 } else if ((rcode == NOERROR || rcode == NXDOMAIN) && 239 ancount == 0 && 240 nscount == 1 && arcount == 0) { 241 /* 242 * name/data does not exist, soa record supplied in the 243 * authority section 244 */ 245 /* authority section must contain the soa record */ 246 if ((n = dn_expand(answer, eom, cp, zname, 247 sizeof zname)) < 0) 248 return (n); 249 cp += n; 250 if (cp + 2 * INT16SZ > eom) 251 return (-1); 252 GETSHORT(type, cp); 253 GETSHORT(class, cp); 254 if (type != T_SOA || class != qclass) { 255 fprintf(stderr, "unknown answer\n"); 256 return (-1); 257 } 258 myzone = 0; 259 svdname = dname; 260 while (dname) 261 if (strcasecmp(dname, zname) == 0) { 262 myzone = 1; 263 break; 264 } else if ((dname = strchr(dname, '.')) != NULL) 265 dname++; 266 if (!myzone) { 267 dname = strchr(svdname, '.'); 268 if (dname != NULL) 269 dname++; 270 continue; 271 } 272 nscount = 0; 273 /* fallthrough */ 274 } else if (rcode == NOERROR && ancount == 1) { 275 /* 276 * found the zone name 277 * new servers will supply NS records for the zone 278 * in authority section and A records for those 279 * nameservers in the additional section 280 * older servers have to be explicitly queried for 281 * NS records for the zone 282 */ 283 /* answer section must contain the soa record */ 284 if ((n = dn_expand(answer, eom, cp, zname, 285 sizeof zname)) < 0) 286 return (n); 287 else 288 cp += n; 289 if (cp + 2 * INT16SZ > eom) 290 return (-1); 291 GETSHORT(type, cp); 292 GETSHORT(class, cp); 293 if (type == T_CNAME) { 294 dname = strchr(dname, '.'); 295 if (dname != NULL) 296 dname++; 297 continue; 298 } 299 if (strcasecmp(dname, zname) != 0 || 300 type != T_SOA || 301 class != rrecp->r_class) { 302 fprintf(stderr, "unknown answer\n"); 303 return (-1); 304 } 305 /* FALLTHROUGH */ 306 } else { 307 fprintf(stderr, 308 "unknown response: ans=%d, auth=%d, add=%d, rcode=%d\n", 309 ancount, nscount, arcount, hp->rcode); 310 return (-1); 311 } 312 if (cp + INT32SZ + INT16SZ > eom) 313 return (-1); 314 /* continue processing the soa record */ 315 GETLONG(ttl, cp); 316 GETSHORT(dlen, cp); 317 if (cp + dlen > eom) 318 return (-1); 319 newgroup = 1; 320 zptr = zgrp_start; 321 prevzptr = NULL; 322 while (zptr) { 323 if (strcasecmp(zname, zptr->z_origin) == 0 && 324 type == T_SOA && class == qclass) { 325 newgroup = 0; 326 break; 327 } 328 prevzptr = zptr; 329 zptr = zptr->z_next; 330 } 331 if (!newgroup) { 332 for (tmprrecp = zptr->z_rr; 333 tmprrecp->r_grpnext; 334 tmprrecp = tmprrecp->r_grpnext) 335 ; 336 tmprrecp->r_grpnext = rrecp; 337 rrecp->r_grpnext = NULL; 338 done = 1; 339 cp += dlen; 340 break; 341 } else { 342 if ((n = dn_expand(answer, eom, cp, primary, 343 sizeof primary)) < 0) 344 return (n); 345 cp += n; 346 /* 347 * We don't have to bounds check here because the 348 * next use of 'cp' is in dn_expand(). 349 */ 350 cp1 = (char *)soardata; 351 strcpy(cp1, primary); 352 cp1 += strlen(cp1) + 1; 353 if ((n = dn_expand(answer, eom, cp, mailaddr, 354 sizeof mailaddr)) < 0) 355 return (n); 356 cp += n; 357 strcpy(cp1, mailaddr); 358 cp1 += strlen(cp1) + 1; 359 if (cp + 5*INT32SZ > eom) 360 return (-1); 361 memcpy(cp1, cp, 5*INT32SZ); 362 cp += 5*INT32SZ; 363 cp1 += 5*INT32SZ; 364 rdatasize = (int)((u_char *)cp1 - soardata); 365 zptr = calloc(1, sizeof(struct zonegrp)); 366 if (zptr == NULL) 367 return (-1); 368 if (zgrp_start == NULL) 369 zgrp_start = zptr; 370 else 371 prevzptr->z_next = zptr; 372 zptr->z_rr = rrecp; 373 rrecp->r_grpnext = NULL; 374 strcpy(zptr->z_origin, zname); 375 zptr->z_class = class; 376 memcpy(zptr->z_soardata, soardata, rdatasize); 377 /* fallthrough to process NS and A records */ 378 } 379 } else if (qtype == T_NS) { 380 if (rcode == NOERROR && ancount > 0) { 381 strcpy(zname, dname); 382 for (zptr = zgrp_start; zptr; zptr = zptr->z_next) { 383 if (strcasecmp(zname, zptr->z_origin) == 0) 384 break; 385 } 386 if (zptr == NULL) 387 /* should not happen */ 388 return (-1); 389 if (nscount > 0) { 390 /* 391 * answer and authority sections contain 392 * the same information, skip answer section 393 */ 394 for (j = 0; j < ancount; j++) { 395 n = dn_skipname(cp, eom); 396 if (n < 0) 397 return (-1); 398 n += 2*INT16SZ + INT32SZ; 399 if (cp + n + INT16SZ > eom) 400 return (-1); 401 cp += n; 402 GETSHORT(dlen, cp); 403 cp += dlen; 404 } 405 } else 406 nscount = ancount; 407 /* fallthrough to process NS and A records */ 408 } else { 409 fprintf(stderr, "cannot determine nameservers for %s:\ 410 ans=%d, auth=%d, add=%d, rcode=%d\n", 411 dname, ancount, nscount, arcount, hp->rcode); 412 return (-1); 413 } 414 } else if (qtype == T_A) { 415 if (rcode == NOERROR && ancount > 0) { 416 arcount = ancount; 417 ancount = nscount = 0; 418 /* fallthrough to process A records */ 419 } else { 420 fprintf(stderr, "cannot determine address for %s:\ 421 ans=%d, auth=%d, add=%d, rcode=%d\n", 422 dname, ancount, nscount, arcount, hp->rcode); 423 return (-1); 424 } 425 } 426 /* process NS records for the zone */ 427 j = 0; 428 for (i = 0; i < nscount; i++) { 429 if ((n = dn_expand(answer, eom, cp, name, 430 sizeof name)) < 0) 431 return (n); 432 cp += n; 433 if (cp + 3 * INT16SZ + INT32SZ > eom) 434 return (-1); 435 GETSHORT(type, cp); 436 GETSHORT(class, cp); 437 GETLONG(ttl, cp); 438 GETSHORT(dlen, cp); 439 if (cp + dlen > eom) 440 return (-1); 441 if (strcasecmp(name, zname) == 0 && 442 type == T_NS && class == qclass) { 443 if ((n = dn_expand(answer, eom, cp, 444 name, sizeof name)) < 0) 445 return (n); 446 target = zptr->z_ns[j++].nsname; 447 strcpy(target, name); 448 } 449 cp += dlen; 450 } 451 if (zptr->z_nscount == 0) 452 zptr->z_nscount = j; 453 /* get addresses for the nameservers */ 454 for (i = 0; i < arcount; i++) { 455 if ((n = dn_expand(answer, eom, cp, name, 456 sizeof name)) < 0) 457 return (n); 458 cp += n; 459 if (cp + 3 * INT16SZ + INT32SZ > eom) 460 return (-1); 461 GETSHORT(type, cp); 462 GETSHORT(class, cp); 463 GETLONG(ttl, cp); 464 GETSHORT(dlen, cp); 465 if (cp + dlen > eom) 466 return (-1); 467 if (type == T_A && dlen == INT32SZ && class == qclass) { 468 for (j = 0; j < zptr->z_nscount; j++) 469 if (strcasecmp(name, zptr->z_ns[j].nsname) == 0) { 470 memcpy(&zptr->z_ns[j].nsaddr1.s_addr, cp, 471 INT32SZ); 472 break; 473 } 474 } 475 cp += dlen; 476 } 477 if (zptr->z_nscount == 0) { 478 dname = zname; 479 qtype = T_NS; 480 continue; 481 } 482 done = 1; 483 for (k = 0; k < zptr->z_nscount; k++) 484 if (zptr->z_ns[k].nsaddr1.s_addr == 0) { 485 done = 0; 486 dname = zptr->z_ns[k].nsname; 487 qtype = T_A; 488 } 489 } /* while */ 490 } 491 --ttl; // Suppress the "Set but not used" warning/error for ttl. 492 493 _res.options |= RES_DEBUG; 494 for (zptr = zgrp_start; zptr; zptr = zptr->z_next) { 495 496 /* append zone section */ 497 rrecp = res_mkupdrec(ns_s_zn, zptr->z_origin, 498 zptr->z_class, ns_t_soa, 0); 499 if (rrecp == NULL) { 500 fprintf(stderr, "saverrec error\n"); 501 fflush(stderr); 502 return (-1); 503 } 504 rrecp->r_grpnext = zptr->z_rr; 505 zptr->z_rr = rrecp; 506 507 n = res_mkupdate(zptr->z_rr, packet, sizeof packet); 508 if (n < 0) { 509 fprintf(stderr, "res_mkupdate error\n"); 510 fflush(stderr); 511 return (-1); 512 } else 513 fprintf(stdout, "res_mkupdate: packet size = %d\n", n); 514 515 /* Override the list of NS records from res_init() with 516 * the authoritative nameservers for the zone being updated. 517 * Sort primary to be the first in the list of nameservers. 518 */ 519 for (i = 0; i < zptr->z_nscount; i++) { 520 if (strcasecmp(zptr->z_ns[i].nsname, 521 zptr->z_soardata) == 0) { 522 struct in_addr tmpaddr; 523 524 if (i != 0) { 525 strcpy(zptr->z_ns[i].nsname, 526 zptr->z_ns[0].nsname); 527 strcpy(zptr->z_ns[0].nsname, 528 zptr->z_soardata); 529 tmpaddr = zptr->z_ns[i].nsaddr1; 530 zptr->z_ns[i].nsaddr1 = 531 zptr->z_ns[0].nsaddr1; 532 zptr->z_ns[0].nsaddr1 = tmpaddr; 533 } 534 break; 535 } 536 } 537 for (i = 0; i < MAXNS; i++) { 538 _res.nsaddr_list[i].sin_addr = zptr->z_ns[i].nsaddr1; 539 _res.nsaddr_list[i].sin_family = AF_INET; 540 _res.nsaddr_list[i].sin_port = htons(NAMESERVER_PORT); 541 } 542 _res.nscount = (zptr->z_nscount < MAXNS) ? 543 zptr->z_nscount : MAXNS; 544 n = res_send(packet, n, answer, sizeof(answer)); 545 if (n < 0) { 546 fprintf(stderr, "res_send: send error, n=%d\n", n); 547 break; 548 } else 549 numzones++; 550 } 551 552 /* free malloc'ed memory */ 553 while(zgrp_start) { 554 zptr = zgrp_start; 555 zgrp_start = zgrp_start->z_next; 556 res_freeupdrec(zptr->z_rr); /* Zone section we allocated. */ 557 free((char *)zptr); 558 } 559 560 return (numzones); 561 } 562