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