Home | History | Annotate | Download | only in BsdSocketLib
      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