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