• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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
res_update(ns_updrec * rrecp_in)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