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