]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/net/res_update.c
This commit was generated by cvs2svn to compensate for changes in r53796,
[FreeBSD/FreeBSD.git] / lib / libc / net / res_update.c
1 #if !defined(lint) && !defined(SABER)
2 static char rcsid[] = "$FreeBSD$";
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 <errno.h>
34 #include <limits.h>
35 #include <netdb.h>
36 #include <resolv.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.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                 h_errno = 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                 }
165                 if (n < HFIXEDSZ)
166                         return (-1);
167                 ancount = ntohs(hp->ancount);
168                 nscount = ntohs(hp->nscount);
169                 arcount = ntohs(hp->arcount);
170                 rcode = hp->rcode;
171                 cp = answer + HFIXEDSZ;
172                 eom = answer + n;
173                 /* skip the question section */
174                 n = dn_skipname(cp, eom);
175                 if (n < 0 || cp + n + 2 * INT16SZ > eom)
176                         return (-1);
177                 cp += n + 2 * INT16SZ;
178
179                 if (qtype == T_SOA) {
180                     if (ancount == 0 && nscount == 0 && arcount == 0) {
181                         /*
182                          * if (rcode == NOERROR) then the dname exists but
183                          * has no soa record associated with it.
184                          * if (rcode == NXDOMAIN) then the dname does not
185                          * exist and the server is replying out of NCACHE.
186                          * in either case, proceed with the next try
187                          */
188                         dname = strchr(dname, '.');
189                         if (dname != NULL)
190                                 dname++;
191                         continue;
192                     } else if ((rcode == NOERROR || rcode == NXDOMAIN) &&
193                                ancount == 0 &&
194                                nscount == 1 && arcount == 0) {
195                         /*
196                          * name/data does not exist, soa record supplied in the
197                          * authority section
198                          */
199                         /* authority section must contain the soa record */
200                         if ((n = dn_expand(answer, eom, cp, zname,
201                                         sizeof zname)) < 0)
202                             return (n);
203                         cp += n;
204                         if (cp + 2 * INT16SZ > eom)
205                                 return (-1);
206                         GETSHORT(type, cp);
207                         GETSHORT(class, cp);
208                         if (type != T_SOA || class != qclass) {
209                             fprintf(stderr, "unknown answer\n");
210                             return (-1);
211                         }
212                         myzone = 0;
213                         svdname = dname;
214                         while (dname)
215                             if (strcasecmp(dname, zname) == 0) {
216                                 myzone = 1;
217                                 break;
218                             } else if ((dname = strchr(dname, '.')) != NULL)
219                                 dname++;
220                         if (!myzone) {
221                             dname = strchr(svdname, '.');
222                             if (dname != NULL)
223                                 dname++;
224                             continue;
225                         }
226                         nscount = 0;
227                         /* fallthrough */
228                     } else if (rcode == NOERROR && ancount == 1) {
229                         /*
230                          * found the zone name
231                          * new servers will supply NS records for the zone
232                          * in authority section and A records for those 
233                          * nameservers in the additional section
234                          * older servers have to be explicitly queried for
235                          * NS records for the zone
236                          */
237                         /* answer section must contain the soa record */
238                         if ((n = dn_expand(answer, eom, cp, zname,
239                                            sizeof zname)) < 0)
240                                 return (n);
241                         else
242                                 cp += n;
243                         if (cp + 2 * INT16SZ > eom)
244                                 return (-1);
245                         GETSHORT(type, cp);
246                         GETSHORT(class, cp);
247                         if (type == T_CNAME) {
248                                 dname = strchr(dname, '.');
249                                 if (dname != NULL)
250                                         dname++;
251                                 continue;
252                         }
253                         if (strcasecmp(dname, zname) != 0 ||
254                             type != T_SOA ||
255                             class != rrecp->r_class) {
256                                 fprintf(stderr, "unknown answer\n");
257                                 return (-1);
258                         }
259                         /* FALLTHROUGH */
260                     } else {
261                         fprintf(stderr,
262                 "unknown response: ans=%d, auth=%d, add=%d, rcode=%d\n",
263                                 ancount, nscount, arcount, hp->rcode);
264                         return (-1);
265                     }
266                     if (cp + INT32SZ + INT16SZ > eom)
267                             return (-1);
268                     /* continue processing the soa record */
269                     GETLONG(ttl, cp);
270                     GETSHORT(dlen, cp);
271                     if (cp + dlen > eom)
272                             return (-1);
273                     newgroup = 1;
274                     zptr = zgrp_start;
275                     prevzptr = NULL;
276                     while (zptr) {
277                         if (strcasecmp(zname, zptr->z_origin) == 0 &&
278                             type == T_SOA && class == qclass) {
279                                 newgroup = 0;
280                                 break;
281                         }
282                         prevzptr = zptr;
283                         zptr = zptr->z_next;
284                     }
285                     if (!newgroup) {
286                         for (tmprrecp = zptr->z_rr;
287                              tmprrecp->r_grpnext;
288                              tmprrecp = tmprrecp->r_grpnext)
289                                     ;
290                         tmprrecp->r_grpnext = rrecp;
291                         rrecp->r_grpnext = NULL;
292                         done = 1;
293                         cp += dlen;
294                         break;
295                     } else {
296                         if ((n = dn_expand(answer, eom, cp, primary,
297                                            sizeof primary)) < 0)
298                             return (n);
299                         cp += n;
300                         /* 
301                          * We don't have to bounds check here because the
302                          * next use of 'cp' is in dn_expand().
303                          */
304                         cp1 = (char *)soardata;
305                         strcpy(cp1, primary);
306                         cp1 += strlen(cp1) + 1;
307                         if ((n = dn_expand(answer, eom, cp, mailaddr,
308                                            sizeof mailaddr)) < 0)
309                             return (n);
310                         cp += n;
311                         strcpy(cp1, mailaddr);
312                         cp1 += strlen(cp1) + 1;
313                         if (cp + 5*INT32SZ > eom)
314                                 return (-1);
315                         memcpy(cp1, cp, 5*INT32SZ);
316                         cp += 5*INT32SZ;
317                         cp1 += 5*INT32SZ;
318                         rdatasize = (u_char *)cp1 - soardata;
319                         zptr = calloc(1, sizeof(struct zonegrp));
320                         if (zptr == NULL)
321                             return (-1);
322                         if (zgrp_start == NULL)
323                             zgrp_start = zptr;
324                         else
325                             prevzptr->z_next = zptr;
326                         zptr->z_rr = rrecp;
327                         rrecp->r_grpnext = NULL;
328                         strcpy(zptr->z_origin, zname);
329                         zptr->z_class = class;
330                         memcpy(zptr->z_soardata, soardata, rdatasize);
331                         /* fallthrough to process NS and A records */
332                     }
333                 } else if (qtype == T_NS) {
334                     if (rcode == NOERROR && ancount > 0) {
335                         strcpy(zname, dname);
336                         for (zptr = zgrp_start; zptr; zptr = zptr->z_next) {
337                             if (strcasecmp(zname, zptr->z_origin) == 0)
338                                 break;
339                         }
340                         if (zptr == NULL)
341                             /* should not happen */
342                             return (-1);
343                         if (nscount > 0) {
344                             /*
345                              * answer and authority sections contain
346                              * the same information, skip answer section
347                              */
348                             for (j = 0; j < ancount; j++) {
349                                 n = dn_skipname(cp, eom);
350                                 if (n < 0)
351                                         return (-1);
352                                 n += 2*INT16SZ + INT32SZ;
353                                 if (cp + n + INT16SZ > eom)
354                                         return (-1);
355                                 cp += n;
356                                 GETSHORT(dlen, cp);
357                                 cp += dlen;
358                             }
359                         } else
360                             nscount = ancount;
361                         /* fallthrough to process NS and A records */
362                     } else {
363                         fprintf(stderr, "cannot determine nameservers for %s:\
364 ans=%d, auth=%d, add=%d, rcode=%d\n",
365                                 dname, ancount, nscount, arcount, hp->rcode);
366                         return (-1);
367                     }
368                 } else if (qtype == T_A) {
369                     if (rcode == NOERROR && ancount > 0) {
370                         arcount = ancount;
371                         ancount = nscount = 0;
372                         /* fallthrough to process A records */
373                     } else {
374                         fprintf(stderr, "cannot determine address for %s:\
375 ans=%d, auth=%d, add=%d, rcode=%d\n",
376                                 dname, ancount, nscount, arcount, hp->rcode);
377                         return (-1);
378                     }
379                 }
380                 /* process NS records for the zone */
381                 j = 0;
382                 for (i = 0; i < nscount; i++) {
383                     if ((n = dn_expand(answer, eom, cp, name,
384                                         sizeof name)) < 0)
385                         return (n);
386                     cp += n;
387                     if (cp + 3 * INT16SZ + INT32SZ > eom)
388                             return (-1);
389                     GETSHORT(type, cp);
390                     GETSHORT(class, cp);
391                     GETLONG(ttl, cp);
392                     GETSHORT(dlen, cp);
393                     if (cp + dlen > eom)
394                         return (-1);
395                     if (strcasecmp(name, zname) == 0 &&
396                         type == T_NS && class == qclass) {
397                                 if ((n = dn_expand(answer, eom, cp,
398                                                    name, sizeof name)) < 0)
399                                         return (n);
400                             target = zptr->z_ns[j++].nsname;
401                             strcpy(target, name);
402                     }
403                     cp += dlen;
404                 }
405                 if (zptr->z_nscount == 0)
406                     zptr->z_nscount = j;
407                 /* get addresses for the nameservers */
408                 for (i = 0; i < arcount; i++) {
409                     if ((n = dn_expand(answer, eom, cp, name,
410                                         sizeof name)) < 0)
411                         return (n);
412                     cp += n;
413                     if (cp + 3 * INT16SZ + INT32SZ > eom)
414                         return (-1);
415                     GETSHORT(type, cp);
416                     GETSHORT(class, cp);
417                     GETLONG(ttl, cp);
418                     GETSHORT(dlen, cp);
419                     if (cp + dlen > eom)
420                             return (-1);
421                     if (type == T_A && dlen == INT32SZ && class == qclass) {
422                         for (j = 0; j < zptr->z_nscount; j++)
423                             if (strcasecmp(name, zptr->z_ns[j].nsname) == 0) {
424                                 memcpy(&zptr->z_ns[j].nsaddr1.s_addr, cp,
425                                        INT32SZ);
426                                 break;
427                             }
428                     }
429                     cp += dlen;
430                 }
431                 if (zptr->z_nscount == 0) {
432                     dname = zname;
433                     qtype = T_NS;
434                     continue;
435                 }
436                 done = 1;
437                 for (k = 0; k < zptr->z_nscount; k++)
438                     if (zptr->z_ns[k].nsaddr1.s_addr == 0) {
439                         done = 0;
440                         dname = zptr->z_ns[k].nsname;
441                         qtype = T_A;
442                     }
443
444             } /* while */
445         }
446
447         _res.options |= RES_DEBUG;
448         for (zptr = zgrp_start; zptr; zptr = zptr->z_next) {
449
450                 /* append zone section */
451                 rrecp = res_mkupdrec(ns_s_zn, zptr->z_origin,
452                                      zptr->z_class, ns_t_soa, 0);
453                 if (rrecp == NULL) {
454                         fprintf(stderr, "saverrec error\n");
455                         fflush(stderr);
456                         return (-1);
457                 }
458                 rrecp->r_grpnext = zptr->z_rr;
459                 zptr->z_rr = rrecp;
460
461                 n = res_mkupdate(zptr->z_rr, packet, sizeof packet);
462                 if (n < 0) {
463                         fprintf(stderr, "res_mkupdate error\n");
464                         fflush(stderr);
465                         return (-1);
466                 } else
467                         fprintf(stdout, "res_mkupdate: packet size = %d\n", n);
468
469                 /*
470                  * Override the list of NS records from res_init() with
471                  * the authoritative nameservers for the zone being updated.
472                  * Sort primary to be the first in the list of nameservers.
473                  */
474                 for (i = 0; i < zptr->z_nscount; i++) {
475                         if (strcasecmp(zptr->z_ns[i].nsname,
476                                        zptr->z_soardata) == 0) {
477                                 struct in_addr tmpaddr;
478
479                                 if (i != 0) {
480                                         strcpy(zptr->z_ns[i].nsname,
481                                                zptr->z_ns[0].nsname);
482                                         strcpy(zptr->z_ns[0].nsname,
483                                                zptr->z_soardata);
484                                         tmpaddr = zptr->z_ns[i].nsaddr1;
485                                         zptr->z_ns[i].nsaddr1 =
486                                                 zptr->z_ns[0].nsaddr1;
487                                         zptr->z_ns[0].nsaddr1 = tmpaddr;
488                                 }
489                                 break;
490                         }
491                 }
492                 for (i = 0; i < MAXNS; i++) {
493                         _res.nsaddr_list[i].sin_addr = zptr->z_ns[i].nsaddr1;
494                         _res.nsaddr_list[i].sin_family = AF_INET;
495                         _res.nsaddr_list[i].sin_port = htons(NAMESERVER_PORT);
496                 }
497                 _res.nscount = (zptr->z_nscount < MAXNS) ? 
498                                         zptr->z_nscount : MAXNS;
499                 n = res_send(packet, n, answer, sizeof(answer));
500                 if (n < 0) {
501                         fprintf(stderr, "res_send: send error, n=%d\n", n);
502                         break;
503                 } else
504                         numzones++;
505         }
506
507         /* free malloc'ed memory */
508         while(zgrp_start) {
509                 zptr = zgrp_start;
510                 zgrp_start = zgrp_start->z_next;
511                 res_freeupdrec(zptr->z_rr);  /* Zone section we allocated. */
512                 free((char *)zptr);
513         }
514
515         return (numzones);
516 }