1 /* Resolver state initialization and resolv.conf parsing.
2    Copyright (C) 1995-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /*
20  * Copyright (c) 1985, 1989, 1993
21  *    The Regents of the University of California.  All rights reserved.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the above copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 4. Neither the name of the University nor the names of its contributors
32  *    may be used to endorse or promote products derived from this software
33  *    without specific prior written permission.
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
36  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
39  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45  * SUCH DAMAGE.
46  */
47 
48 /*
49  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
50  *
51  * Permission to use, copy, modify, and distribute this software for any
52  * purpose with or without fee is hereby granted, provided that the above
53  * copyright notice and this permission notice appear in all copies, and that
54  * the name of Digital Equipment Corporation not be used in advertising or
55  * publicity pertaining to distribution of the document or software without
56  * specific, written prior permission.
57  *
58  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
59  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
60  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
61  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
62  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
63  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
64  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
65  * SOFTWARE.
66  */
67 
68 /*
69  * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
70  *
71  * Permission to use, copy, modify, and distribute this software for any
72  * purpose with or without fee is hereby granted, provided that the above
73  * copyright notice and this permission notice appear in all copies.
74  *
75  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
76  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
77  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
78  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
79  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
80  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
81  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
82  * SOFTWARE.
83  */
84 
85 #include <ctype.h>
86 #include <netdb.h>
87 #include <resolv/resolv-internal.h>
88 #include <res_hconf.h>
89 #include <stdio.h>
90 #include <stdio_ext.h>
91 #include <stdlib.h>
92 #include <string.h>
93 #include <unistd.h>
94 #include <stdint.h>
95 #include <arpa/inet.h>
96 #include <arpa/nameser.h>
97 #include <net/if.h>
98 #include <netinet/in.h>
99 #include <sys/param.h>
100 #include <sys/socket.h>
101 #include <sys/time.h>
102 #include <sys/types.h>
103 #include <inet/net-internal.h>
104 #include <errno.h>
105 #include <resolv_conf.h>
106 #include <file_change_detection.h>
107 
108 static uint32_t net_mask (struct in_addr);
109 
110 int
res_ninit(res_state statp)111 res_ninit (res_state statp)
112 {
113   return __res_vinit (statp, 0);
114 }
libc_hidden_def(__res_ninit)115 libc_hidden_def (__res_ninit)
116 
117 /* Return true if CH separates the netmask in the "sortlist"
118    directive.  */
119 static inline bool
120 is_sort_mask (char ch)
121 {
122   return ch == '/' || ch == '&';
123 }
124 
125 /* Array of name server addresses.  */
126 #define DYNARRAY_STRUCT nameserver_list
127 #define DYNARRAY_ELEMENT const struct sockaddr *
128 #define DYNARRAY_ELEMENT_FREE(e) free ((struct sockaddr *) *(e))
129 #define DYNARRAY_INITIAL_SIZE 3
130 #define DYNARRAY_PREFIX nameserver_list_
131 #include <malloc/dynarray-skeleton.c>
132 
133 /* Array of strings for the search array.  The backing store is
134    managed separately.  */
135 #define DYNARRAY_STRUCT search_list
136 #define DYNARRAY_ELEMENT const char *
137 #define DYNARRAY_INITIAL_SIZE 6
138 #define DYNARRAY_PREFIX search_list_
139 #include <malloc/dynarray-skeleton.c>
140 
141 /* Array of name server addresses.  */
142 #define DYNARRAY_STRUCT sort_list
143 #define DYNARRAY_ELEMENT struct resolv_sortlist_entry
144 #define DYNARRAY_INITIAL_SIZE 0
145 #define DYNARRAY_PREFIX sort_list_
146 #include <malloc/dynarray-skeleton.c>
147 
148 /* resolv.conf parser state and results.  */
149 struct resolv_conf_parser
150 {
151   char *buffer;            /* Temporary buffer for reading lines.  */
152 
153   struct nameserver_list nameserver_list; /* Nameserver addresses.  */
154 
155   char *search_list_store; /* Backing storage for search list entries.  */
156   struct search_list search_list; /* Points into search_list_store.  */
157 
158   struct sort_list sort_list;   /* Address preference sorting list.  */
159 
160   /* Configuration template.  The non-array elements are filled in
161      directly.  The array elements are updated prior to the call to
162      __resolv_conf_attach.  */
163   struct resolv_conf template;
164 };
165 
166 /* Return true if *PREINIT contains actual preinitialization.  */
167 static bool
has_preinit_values(const struct __res_state * preinit)168 has_preinit_values (const struct __res_state *preinit)
169 {
170   return (preinit->retrans != 0 && preinit->retrans != RES_TIMEOUT)
171     || (preinit->retry != 0 && preinit->retry != RES_DFLRETRY)
172     || (preinit->options != 0
173         && (preinit->options & ~RES_INIT) != RES_DEFAULT);
174 }
175 
176 static void
resolv_conf_parser_init(struct resolv_conf_parser * parser,const struct __res_state * preinit)177 resolv_conf_parser_init (struct resolv_conf_parser *parser,
178                          const struct __res_state *preinit)
179 {
180   parser->buffer = NULL;
181   parser->search_list_store = NULL;
182   nameserver_list_init (&parser->nameserver_list);
183   search_list_init (&parser->search_list);
184   sort_list_init (&parser->sort_list);
185 
186   if (preinit != NULL)
187     {
188       parser->template.retrans = preinit->retrans;
189       parser->template.retry = preinit->retry;
190       parser->template.options = preinit->options | RES_INIT;
191     }
192   else
193     {
194       parser->template.retrans = RES_TIMEOUT;
195       parser->template.retry = RES_DFLRETRY;
196       parser->template.options = RES_DEFAULT | RES_INIT;
197     }
198   parser->template.ndots = 1;
199 }
200 
201 static void
resolv_conf_parser_free(struct resolv_conf_parser * parser)202 resolv_conf_parser_free (struct resolv_conf_parser *parser)
203 {
204   free (parser->buffer);
205   free (parser->search_list_store);
206   nameserver_list_free (&parser->nameserver_list);
207   search_list_free (&parser->search_list);
208   sort_list_free (&parser->sort_list);
209 }
210 
211 /* Allocate a struct sockaddr_in object on the heap, with the
212    specified address and port.  */
213 static struct sockaddr *
allocate_address_v4(struct in_addr a,uint16_t port)214 allocate_address_v4 (struct in_addr a, uint16_t port)
215 {
216   struct sockaddr_in *sa4 = malloc (sizeof (*sa4));
217   if (sa4 == NULL)
218     return NULL;
219   sa4->sin_family = AF_INET;
220   sa4->sin_addr = a;
221   sa4->sin_port = htons (port);
222   return (struct sockaddr *) sa4;
223 }
224 
225 /* Try to obtain the domain name from the host name and store it in
226    *RESULT.  Return false on memory allocation failure.  If the domain
227    name cannot be determined for any other reason, write NULL to
228    *RESULT and return true.  */
229 static bool
domain_from_hostname(char ** result)230 domain_from_hostname (char **result)
231 {
232   char buf[256];
233   /* gethostbyname may not terminate the buffer.  */
234   buf[sizeof (buf) - 1] = '\0';
235   if (__gethostname (buf, sizeof (buf) - 1) == 0)
236     {
237       char *dot = strchr (buf, '.');
238       if (dot != NULL)
239         {
240           *result = __strdup (dot + 1);
241           if (*result == NULL)
242             return false;
243           return true;
244         }
245     }
246   *result = NULL;
247   return true;
248 }
249 
250 static void res_setoptions (struct resolv_conf_parser *, const char *options);
251 
252 /* Internal helper function for __res_vinit, to aid with resource
253    deallocation and error handling.  Return true on success, false on
254    failure.  */
255 static bool
res_vinit_1(FILE * fp,struct resolv_conf_parser * parser)256 res_vinit_1 (FILE *fp, struct resolv_conf_parser *parser)
257 {
258   char *cp;
259   size_t buffer_size = 0;
260   bool haveenv = false;
261 
262   /* Allow user to override the local domain definition.  */
263   if ((cp = getenv ("LOCALDOMAIN")) != NULL)
264     {
265       /* The code below splits the string in place.  */
266       cp = __strdup (cp);
267       if (cp == NULL)
268         return false;
269       free (parser->search_list_store);
270       parser->search_list_store = cp;
271       haveenv = true;
272 
273       /* The string will be truncated as needed below.  */
274       search_list_add (&parser->search_list, cp);
275 
276       /* Set search list to be blank-separated strings from rest of
277          env value.  Permits users of LOCALDOMAIN to still have a
278          search list, and anyone to set the one that they want to use
279          as an individual (even more important now that the rfc1535
280          stuff restricts searches).  */
281       for (bool in_name = true; *cp != '\0'; cp++)
282         {
283           if (*cp == '\n')
284             {
285               *cp = '\0';
286               break;
287             }
288           else if (*cp == ' ' || *cp == '\t')
289             {
290               *cp = '\0';
291               in_name = false;
292             }
293           else if (!in_name)
294             {
295               search_list_add (&parser->search_list, cp);
296               in_name = true;
297             }
298         }
299     }
300 
301 #define MATCH(line, name)                       \
302   (!strncmp ((line), name, sizeof (name) - 1)     \
303    && ((line)[sizeof (name) - 1] == ' '           \
304        || (line)[sizeof (name) - 1] == '\t'))
305 
306   if (fp != NULL)
307     {
308       /* No threads use this stream.  */
309       __fsetlocking (fp, FSETLOCKING_BYCALLER);
310       /* Read the config file.  */
311       while (true)
312         {
313           {
314             ssize_t ret = __getline (&parser->buffer, &buffer_size, fp);
315             if (ret <= 0)
316               {
317                 if (_IO_ferror_unlocked (fp))
318                   return false;
319                 else
320                   break;
321               }
322           }
323 
324           /* Skip comments.  */
325           if (*parser->buffer == ';' || *parser->buffer == '#')
326             continue;
327           /* Read default domain name.  */
328           if (MATCH (parser->buffer, "domain"))
329             {
330               if (haveenv)
331                 /* LOCALDOMAIN overrides the configuration file.  */
332                 continue;
333               cp = parser->buffer + sizeof ("domain") - 1;
334               while (*cp == ' ' || *cp == '\t')
335                 cp++;
336               if ((*cp == '\0') || (*cp == '\n'))
337                 continue;
338 
339               cp = __strdup (cp);
340               if (cp == NULL)
341                 return false;
342               free (parser->search_list_store);
343               parser->search_list_store = cp;
344               search_list_clear (&parser->search_list);
345               search_list_add (&parser->search_list, cp);
346               /* Replace trailing whitespace.  */
347               if ((cp = strpbrk (cp, " \t\n")) != NULL)
348                 *cp = '\0';
349               continue;
350             }
351           /* Set search list.  */
352           if (MATCH (parser->buffer, "search"))
353             {
354               if (haveenv)
355                 /* LOCALDOMAIN overrides the configuration file.  */
356                 continue;
357               cp = parser->buffer + sizeof ("search") - 1;
358               while (*cp == ' ' || *cp == '\t')
359                 cp++;
360               if ((*cp == '\0') || (*cp == '\n'))
361                 continue;
362 
363               {
364                 char *p = strchr (cp, '\n');
365                 if (p != NULL)
366                   *p = '\0';
367               }
368               cp = __strdup (cp);
369               if (cp == NULL)
370                 return false;
371               free (parser->search_list_store);
372               parser->search_list_store = cp;
373 
374               /* The string is truncated below.  */
375               search_list_clear (&parser->search_list);
376               search_list_add (&parser->search_list, cp);
377 
378               /* Set search list to be blank-separated strings on rest
379                  of line.  */
380               for (bool in_name = true; *cp != '\0'; cp++)
381                 {
382                   if (*cp == ' ' || *cp == '\t')
383                     {
384                       *cp = '\0';
385                       in_name = false;
386                     }
387                   else if (!in_name)
388                     {
389                       search_list_add (&parser->search_list, cp);
390                       in_name = true;
391                     }
392                 }
393               continue;
394             }
395           /* Read nameservers to query.  */
396           if (MATCH (parser->buffer, "nameserver"))
397             {
398               struct in_addr a;
399 
400               cp = parser->buffer + sizeof ("nameserver") - 1;
401               while (*cp == ' ' || *cp == '\t')
402                 cp++;
403 
404               /* Ignore trailing contents on the name server line.  */
405               {
406                 char *el;
407                 if ((el = strpbrk (cp, " \t\n")) != NULL)
408                   *el = '\0';
409               }
410 
411               struct sockaddr *sa;
412               if ((*cp != '\0') && (*cp != '\n') && __inet_aton_exact (cp, &a))
413                 {
414                   sa = allocate_address_v4 (a, NAMESERVER_PORT);
415                   if (sa == NULL)
416                     return false;
417                 }
418               else
419                 {
420                   struct in6_addr a6;
421                   char *el;
422                   if ((el = strchr (cp, SCOPE_DELIMITER)) != NULL)
423                     *el = '\0';
424                   if ((*cp != '\0') && (__inet_pton (AF_INET6, cp, &a6) > 0))
425                     {
426                       struct sockaddr_in6 *sa6;
427 
428                       sa6 = malloc (sizeof (*sa6));
429                       if (sa6 == NULL)
430                         return false;
431 
432                       sa6->sin6_family = AF_INET6;
433                       sa6->sin6_port = htons (NAMESERVER_PORT);
434                       sa6->sin6_flowinfo = 0;
435                       sa6->sin6_addr = a6;
436 
437                       sa6->sin6_scope_id = 0;
438                       if (__glibc_likely (el != NULL))
439                         /* Ignore errors, for backwards
440                            compatibility.  */
441                         __inet6_scopeid_pton
442                           (&a6, el + 1, &sa6->sin6_scope_id);
443                       sa = (struct sockaddr *) sa6;
444                     }
445                   else
446                     /* IPv6 address parse failure.  */
447                     sa = NULL;
448                 }
449               if (sa != NULL)
450                 {
451                   const struct sockaddr **p = nameserver_list_emplace
452                     (&parser->nameserver_list);
453                   if (p != NULL)
454                     *p = sa;
455                   else
456                     {
457                       free (sa);
458                       return false;
459                     }
460                 }
461               continue;
462             }
463           if (MATCH (parser->buffer, "sortlist"))
464             {
465               struct in_addr a;
466 
467               cp = parser->buffer + sizeof ("sortlist") - 1;
468               while (true)
469                 {
470                   while (*cp == ' ' || *cp == '\t')
471                     cp++;
472                   if (*cp == '\0' || *cp == '\n' || *cp == ';')
473                     break;
474                   char *net = cp;
475                   while (*cp && !is_sort_mask (*cp) && *cp != ';'
476                          && isascii (*cp) && !isspace (*cp))
477                     cp++;
478                   char separator = *cp;
479                   *cp = 0;
480                   struct resolv_sortlist_entry e;
481                   if (__inet_aton_exact (net, &a))
482                     {
483                       e.addr = a;
484                       if (is_sort_mask (separator))
485                         {
486                           *cp++ = separator;
487                           net = cp;
488                           while (*cp && *cp != ';'
489                                  && isascii (*cp) && !isspace (*cp))
490                             cp++;
491                           separator = *cp;
492                           *cp = 0;
493                           if (__inet_aton_exact (net, &a))
494                             e.mask = a.s_addr;
495                           else
496                             e.mask = net_mask (e.addr);
497                         }
498                       else
499                         e.mask = net_mask (e.addr);
500                       sort_list_add (&parser->sort_list, e);
501                     }
502                   *cp = separator;
503                 }
504               continue;
505             }
506           if (MATCH (parser->buffer, "options"))
507             {
508               res_setoptions (parser, parser->buffer + sizeof ("options") - 1);
509               continue;
510             }
511         }
512     }
513   if (__glibc_unlikely (nameserver_list_size (&parser->nameserver_list) == 0))
514     {
515       const struct sockaddr **p
516         = nameserver_list_emplace (&parser->nameserver_list);
517       if (p == NULL)
518         return false;
519       *p = allocate_address_v4 (__inet_makeaddr (IN_LOOPBACKNET, 1),
520                                 NAMESERVER_PORT);
521       if (*p == NULL)
522         return false;
523     }
524 
525   if (search_list_size (&parser->search_list) == 0)
526     {
527       char *domain;
528       if (!domain_from_hostname (&domain))
529         return false;
530       if (domain != NULL)
531         {
532           free (parser->search_list_store);
533           parser->search_list_store = domain;
534           search_list_add (&parser->search_list, domain);
535         }
536     }
537 
538   if ((cp = getenv ("RES_OPTIONS")) != NULL)
539     res_setoptions (parser, cp);
540 
541   if (nameserver_list_has_failed (&parser->nameserver_list)
542       || search_list_has_failed (&parser->search_list)
543       || sort_list_has_failed (&parser->sort_list))
544     {
545       __set_errno (ENOMEM);
546       return false;
547     }
548 
549   return true;
550 }
551 
552 struct resolv_conf *
__resolv_conf_load(struct __res_state * preinit,struct file_change_detection * change)553 __resolv_conf_load (struct __res_state *preinit,
554                     struct file_change_detection *change)
555 {
556   /* Ensure that /etc/hosts.conf has been loaded (once).  */
557   _res_hconf_init ();
558 
559   FILE *fp = fopen (_PATH_RESCONF, "rce");
560   if (fp == NULL)
561     switch (errno)
562       {
563       case EACCES:
564       case EISDIR:
565       case ELOOP:
566       case ENOENT:
567       case ENOTDIR:
568       case EPERM:
569         /* Ignore these errors.  They are persistent errors caused
570            by file system contents.  */
571         break;
572       default:
573         /* Other errors refer to resource allocation problems and
574            need to be handled by the application.  */
575         return NULL;
576       }
577 
578   struct resolv_conf_parser parser;
579   resolv_conf_parser_init (&parser, preinit);
580 
581   struct resolv_conf *conf = NULL;
582   bool ok = res_vinit_1 (fp, &parser);
583   if (ok && change != NULL)
584     /* Update the file change information if the configuration was
585        loaded successfully.  */
586     ok = __file_change_detection_for_fp (change, fp);
587 
588   if (ok)
589     {
590       parser.template.nameserver_list
591         = nameserver_list_begin (&parser.nameserver_list);
592       parser.template.nameserver_list_size
593         = nameserver_list_size (&parser.nameserver_list);
594       parser.template.search_list = search_list_begin (&parser.search_list);
595       parser.template.search_list_size
596         = search_list_size (&parser.search_list);
597       parser.template.sort_list = sort_list_begin (&parser.sort_list);
598       parser.template.sort_list_size = sort_list_size (&parser.sort_list);
599       conf = __resolv_conf_allocate (&parser.template);
600     }
601   resolv_conf_parser_free (&parser);
602 
603   if (fp != NULL)
604     {
605       int saved_errno = errno;
606       fclose (fp);
607       __set_errno (saved_errno);
608     }
609 
610   return conf;
611 }
612 
613 /* Set up default settings.  If the /etc/resolv.conf configuration
614    file exist, the values there will have precedence.  Otherwise, the
615    server address is set to INADDR_LOOPBACK and the default domain
616    name comes from gethostname.  The RES_OPTIONS and LOCALDOMAIN
617    environment variables can be used to override some settings.
618    Return 0 if completes successfully, -1 on error.  */
619 int
__res_vinit(res_state statp,int preinit)620 __res_vinit (res_state statp, int preinit)
621 {
622   struct resolv_conf *conf;
623   if (preinit && has_preinit_values (statp))
624     /* For the preinit case, we cannot use the cached configuration
625        because some settings could be different.  */
626     conf = __resolv_conf_load (statp, NULL);
627   else
628     conf = __resolv_conf_get_current ();
629   if (conf == NULL)
630     return -1;
631 
632   bool ok = __resolv_conf_attach (statp, conf);
633   __resolv_conf_put (conf);
634   if (ok)
635     {
636       if (preinit)
637         statp->id = res_randomid ();
638       return 0;
639     }
640   else
641     return -1;
642 }
643 
644 static void
res_setoptions(struct resolv_conf_parser * parser,const char * options)645 res_setoptions (struct resolv_conf_parser *parser, const char *options)
646 {
647   const char *cp = options;
648 
649   while (*cp)
650     {
651       /* Skip leading and inner runs of spaces.  */
652       while (*cp == ' ' || *cp == '\t')
653         cp++;
654       /* Search for and process individual options.  */
655       if (!strncmp (cp, "ndots:", sizeof ("ndots:") - 1))
656         {
657           int i = atoi (cp + sizeof ("ndots:") - 1);
658           if (i <= RES_MAXNDOTS)
659             parser->template.ndots = i;
660           else
661             parser->template.ndots = RES_MAXNDOTS;
662         }
663       else if (!strncmp (cp, "timeout:", sizeof ("timeout:") - 1))
664         {
665           int i = atoi (cp + sizeof ("timeout:") - 1);
666           if (i <= RES_MAXRETRANS)
667             parser->template.retrans = i;
668           else
669             parser->template.retrans = RES_MAXRETRANS;
670         }
671       else if (!strncmp (cp, "attempts:", sizeof ("attempts:") - 1))
672         {
673           int i = atoi (cp + sizeof ("attempts:") - 1);
674           if (i <= RES_MAXRETRY)
675             parser->template.retry = i;
676           else
677             parser->template.retry = RES_MAXRETRY;
678         }
679       else
680         {
681           static const struct
682           {
683             char str[22];
684             uint8_t len;
685             uint8_t clear;
686             unsigned long int flag;
687           } options[] = {
688 #define STRnLEN(str) str, sizeof (str) - 1
689             { STRnLEN ("rotate"), 0, RES_ROTATE },
690             { STRnLEN ("edns0"), 0, RES_USE_EDNS0 },
691             { STRnLEN ("single-request-reopen"), 0, RES_SNGLKUPREOP },
692             { STRnLEN ("single-request"), 0, RES_SNGLKUP },
693             { STRnLEN ("no_tld_query"), 0, RES_NOTLDQUERY },
694             { STRnLEN ("no-tld-query"), 0, RES_NOTLDQUERY },
695             { STRnLEN ("no-reload"), 0, RES_NORELOAD },
696             { STRnLEN ("use-vc"), 0, RES_USEVC },
697             { STRnLEN ("trust-ad"), 0, RES_TRUSTAD },
698             { STRnLEN ("no-aaaa"), 0, RES_NOAAAA },
699           };
700 #define noptions (sizeof (options) / sizeof (options[0]))
701           for (int i = 0; i < noptions; ++i)
702             if (strncmp (cp, options[i].str, options[i].len) == 0)
703               {
704                 if (options[i].clear)
705                   parser->template.options &= options[i].flag;
706                 else
707                   parser->template.options |= options[i].flag;
708                 break;
709               }
710         }
711       /* Skip to next run of spaces.  */
712       while (*cp && *cp != ' ' && *cp != '\t')
713         cp++;
714     }
715 }
716 
717 static uint32_t
net_mask(struct in_addr in)718 net_mask (struct in_addr in)
719 {
720   uint32_t i = ntohl (in.s_addr);
721 
722   if (IN_CLASSA (i))
723     return htonl (IN_CLASSA_NET);
724   else if (IN_CLASSB (i))
725     return htonl (IN_CLASSB_NET);
726   return htonl (IN_CLASSC_NET);
727 }
728