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@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
103struct ns1 {
104    char nsname[MAXDNAME];
105    struct in_addr nsaddr1;
106};
107
108struct 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
119int
120res_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:\
410ans=%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:\
421ans=%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