Annotation of embedaddon/dnsmasq/src/domain-match.c, revision 1.1

1.1     ! misho       1: /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
        !             2: 
        !             3:    This program is free software; you can redistribute it and/or modify
        !             4:    it under the terms of the GNU General Public License as published by
        !             5:    the Free Software Foundation; version 2 dated June, 1991, or
        !             6:    (at your option) version 3 dated 29 June, 2007.
        !             7:  
        !             8:    This program is distributed in the hope that it will be useful,
        !             9:    but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            10:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            11:    GNU General Public License for more details.
        !            12:      
        !            13:    You should have received a copy of the GNU General Public License
        !            14:    along with this program.  If not, see <http://www.gnu.org/licenses/>.
        !            15: */
        !            16: 
        !            17: #include "dnsmasq.h"
        !            18: 
        !            19: static int order(char *qdomain, size_t qlen, struct server *serv);
        !            20: static int order_qsort(const void *a, const void *b);
        !            21: static int order_servers(struct server *s, struct server *s2);
        !            22: 
        !            23: /* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */
        !            24: #define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
        !            25: 
        !            26: void build_server_array(void)
        !            27: {
        !            28:   struct server *serv;
        !            29:   int count = 0;
        !            30:   
        !            31:   for (serv = daemon->servers; serv; serv = serv->next)
        !            32: #ifdef HAVE_LOOP
        !            33:     if (!(serv->flags & SERV_LOOP))
        !            34: #endif
        !            35:       {
        !            36:        count++;
        !            37:        if (serv->flags & SERV_WILDCARD)
        !            38:          daemon->server_has_wildcard = 1;
        !            39:       }
        !            40:   
        !            41:   for (serv = daemon->local_domains; serv; serv = serv->next)
        !            42:     {
        !            43:       count++;
        !            44:       if (serv->flags & SERV_WILDCARD)
        !            45:        daemon->server_has_wildcard = 1;
        !            46:     }
        !            47:   
        !            48:   daemon->serverarraysz = count;
        !            49: 
        !            50:   if (count > daemon->serverarrayhwm)
        !            51:     {
        !            52:       struct server **new;
        !            53: 
        !            54:       count += 10; /* A few extra without re-allocating. */
        !            55: 
        !            56:       if ((new = whine_malloc(count * sizeof(struct server *))))
        !            57:        {
        !            58:          if (daemon->serverarray)
        !            59:            free(daemon->serverarray);
        !            60:          
        !            61:          daemon->serverarray = new;
        !            62:          daemon->serverarrayhwm = count;
        !            63:        }
        !            64:     }
        !            65: 
        !            66:   count = 0;
        !            67:   
        !            68:   for (serv = daemon->servers; serv; serv = serv->next)
        !            69: #ifdef HAVE_LOOP
        !            70:     if (!(serv->flags & SERV_LOOP))
        !            71: #endif
        !            72:       {
        !            73:        daemon->serverarray[count] = serv;
        !            74:        serv->serial = count;
        !            75:        serv->last_server = -1;
        !            76:        count++;
        !            77:       }
        !            78:   
        !            79:   for (serv = daemon->local_domains; serv; serv = serv->next, count++)
        !            80:     daemon->serverarray[count] = serv;
        !            81:   
        !            82:   qsort(daemon->serverarray, daemon->serverarraysz, sizeof(struct server *), order_qsort);
        !            83:   
        !            84:   /* servers need the location in the array to find all the whole
        !            85:      set of equivalent servers from a pointer to a single one. */
        !            86:   for (count = 0; count < daemon->serverarraysz; count++)
        !            87:     if (!(daemon->serverarray[count]->flags & SERV_IS_LOCAL))
        !            88:       daemon->serverarray[count]->arrayposn = count;
        !            89: }
        !            90: 
        !            91: /* we're looking for the server whose domain is the longest exact match
        !            92:    to the RH end of qdomain, or a local address if the flags match.
        !            93:    Add '.' to the LHS of the query string so
        !            94:    server=/.example.com/ works.
        !            95: 
        !            96:    A flag of F_SERVER returns an upstream server only.
        !            97:    A flag of F_DNSSECOK returns a DNSSEC capable server only and
        !            98:    also disables NODOTS servers from consideration.
        !            99:    A flag of F_DOMAINSRV returns a domain-specific server only.
        !           100:    A flag of F_CONFIG returns anything that generates a local
        !           101:    reply of IPv4 or IPV6.
        !           102:    return 0 if nothing found, 1 otherwise.
        !           103: */
        !           104: int lookup_domain(char *domain, int flags, int *lowout, int *highout)
        !           105: {
        !           106:   int rc, crop_query, nodots;
        !           107:   ssize_t qlen;
        !           108:   int try, high, low = 0;
        !           109:   int nlow = 0, nhigh = 0;
        !           110:   char *cp, *qdomain = domain;
        !           111: 
        !           112:   /* may be no configured servers. */
        !           113:   if (daemon->serverarraysz == 0)
        !           114:     return 0;
        !           115:   
        !           116:   /* find query length and presence of '.' */
        !           117:   for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
        !           118:     if (*cp == '.')
        !           119:       nodots = 0;
        !           120: 
        !           121:   /* Handle empty name, and searches for DNSSEC queries without
        !           122:      diverting to NODOTS servers. */
        !           123:   if (qlen == 0 || flags & F_DNSSECOK)
        !           124:     nodots = 0;
        !           125: 
        !           126:   /* Search shorter and shorter RHS substrings for a match */
        !           127:   while (qlen >= 0)
        !           128:     {
        !           129:       /* Note that when we chop off a label, all the possible matches
        !           130:         MUST be at a larger index than the nearest failing match with one more
        !           131:         character, since the array is sorted longest to smallest. Hence 
        !           132:         we don't reset low to zero here, we can go further below and crop the 
        !           133:         search string to the size of the largest remaining server
        !           134:         when this match fails. */
        !           135:       high = daemon->serverarraysz;
        !           136:       crop_query = 1;
        !           137:       
        !           138:       /* binary search */
        !           139:       while (1) 
        !           140:        {
        !           141:          try = (low + high)/2;
        !           142: 
        !           143:          if ((rc = order(qdomain, qlen, daemon->serverarray[try])) == 0)
        !           144:            break;
        !           145:          
        !           146:          if (rc < 0)
        !           147:            {
        !           148:              if (high == try)
        !           149:                {
        !           150:                  /* qdomain is longer or same length as longest domain, and try == 0 
        !           151:                     crop the query to the longest domain. */
        !           152:                  crop_query = qlen - daemon->serverarray[try]->domain_len;
        !           153:                  break;
        !           154:                }
        !           155:              high = try;
        !           156:            }
        !           157:          else
        !           158:            {
        !           159:              if (low == try)
        !           160:                {
        !           161:                  /* try now points to the last domain that sorts before the query, so 
        !           162:                     we know that a substring of the query shorter than it is required to match, so
        !           163:                     find the largest domain that's shorter than try. Note that just going to
        !           164:                     try+1 is not optimal, consider searching bbb in (aaa,ccc,bb). try will point
        !           165:                     to aaa, since ccc sorts after bbb, but the first domain that has a chance to 
        !           166:                     match is bb. So find the length of the first domain later than try which is
        !           167:                     is shorter than it. 
        !           168:                     There's a nasty edge case when qdomain sorts before _any_ of the 
        !           169:                     server domains, where try _doesn't point_ to the last domain that sorts
        !           170:                     before the query, since no such domain exists. In that case, the loop 
        !           171:                     exits via the rc < 0 && high == try path above and this code is
        !           172:                     not executed. */
        !           173:                  ssize_t len, old = daemon->serverarray[try]->domain_len;
        !           174:                  while (++try != daemon->serverarraysz)
        !           175:                    {
        !           176:                      if (old != (len = daemon->serverarray[try]->domain_len))
        !           177:                        {
        !           178:                          crop_query = qlen - len;
        !           179:                          break;
        !           180:                        }
        !           181:                    }
        !           182:                  break;
        !           183:                }
        !           184:              low = try;
        !           185:            }
        !           186:        };
        !           187:       
        !           188:       if (rc == 0)
        !           189:        {
        !           190:          int found = 1;
        !           191: 
        !           192:          if (daemon->server_has_wildcard)
        !           193:            {
        !           194:              /* if we have example.com and *example.com we need to check against *example.com, 
        !           195:                 but the binary search may have found either. Use the fact that example.com is sorted before *example.com
        !           196:                 We favour example.com in the case that both match (ie www.example.com) */
        !           197:              while (try != 0 && order(qdomain, qlen, daemon->serverarray[try-1]) == 0)
        !           198:                try--;
        !           199:              
        !           200:              if (!(qdomain == domain || *qdomain == 0 || *(qdomain-1) == '.'))
        !           201:                {
        !           202:                  while (try < daemon->serverarraysz-1 && order(qdomain, qlen, daemon->serverarray[try+1]) == 0)
        !           203:                    try++;
        !           204:                  
        !           205:                  if (!(daemon->serverarray[try]->flags & SERV_WILDCARD))
        !           206:                     found = 0;
        !           207:                }
        !           208:            }
        !           209:          
        !           210:          if (found && filter_servers(try, flags, &nlow, &nhigh))
        !           211:            /* We have a match, but it may only be (say) an IPv6 address, and
        !           212:               if the query wasn't for an AAAA record, it's no good, and we need
        !           213:               to continue generalising */
        !           214:            {
        !           215:              /* We've matched a setting which says to use servers without a domain.
        !           216:                 Continue the search with empty query. We set the F_SERVER flag
        !           217:                 so that --address=/#/... doesn't match. */
        !           218:              if (daemon->serverarray[nlow]->flags & SERV_USE_RESOLV)
        !           219:                {
        !           220:                  crop_query = qlen;
        !           221:                  flags |= F_SERVER;
        !           222:                }
        !           223:              else
        !           224:                break;
        !           225:            }
        !           226:        }
        !           227:       
        !           228:       /* crop_query must be at least one always. */
        !           229:       if (crop_query == 0)
        !           230:        crop_query = 1;
        !           231: 
        !           232:       /* strip chars off the query based on the largest possible remaining match,
        !           233:         then continue to the start of the next label unless we have a wildcard
        !           234:         domain somewhere, in which case we have to go one at a time. */
        !           235:       qlen -= crop_query;
        !           236:       qdomain += crop_query;
        !           237:       if (!daemon->server_has_wildcard)
        !           238:        while (qlen > 0 &&  (*(qdomain-1) != '.'))
        !           239:          qlen--, qdomain++;
        !           240:     }
        !           241: 
        !           242:   /* domain has no dots, and we have at least one server configured to handle such,
        !           243:      These servers always sort to the very end of the array. 
        !           244:      A configured server eg server=/lan/ will take precdence. */
        !           245:   if (nodots &&
        !           246:       (daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) &&
        !           247:       (nlow == nhigh || daemon->serverarray[nlow]->domain_len == 0))
        !           248:     filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
        !           249:   
        !           250:   if (lowout)
        !           251:     *lowout = nlow;
        !           252:   
        !           253:   if (highout)
        !           254:     *highout = nhigh;
        !           255: 
        !           256:   /* qlen == -1 when we failed to match even an empty query, if there are no default servers. */
        !           257:   if (nlow == nhigh || qlen == -1)
        !           258:     return 0;
        !           259:   
        !           260:   return 1;
        !           261: }
        !           262: 
        !           263: /* Return first server in group of equivalent servers; this is the "master" record. */
        !           264: int server_samegroup(struct server *a, struct server *b)
        !           265: {
        !           266:   return order_servers(a, b) == 0;
        !           267: }
        !           268: 
        !           269: int filter_servers(int seed, int flags, int *lowout, int *highout)
        !           270: {
        !           271:   int nlow = seed, nhigh = seed;
        !           272:   int i;
        !           273:   
        !           274:   /* expand nlow and nhigh to cover all the records with the same domain 
        !           275:      nlow is the first, nhigh - 1 is the last. nlow=nhigh means no servers,
        !           276:      which can happen below. */
        !           277:   while (nlow > 0 && order_servers(daemon->serverarray[nlow-1], daemon->serverarray[nlow]) == 0)
        !           278:     nlow--;
        !           279:   
        !           280:   while (nhigh < daemon->serverarraysz-1 && order_servers(daemon->serverarray[nhigh], daemon->serverarray[nhigh+1]) == 0)
        !           281:     nhigh++;
        !           282:   
        !           283:   nhigh++;
        !           284:   
        !           285: #define SERV_LOCAL_ADDRESS (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)
        !           286:   
        !           287:   if (flags & F_CONFIG)
        !           288:     {
        !           289:       /* We're just lookin for any matches that return an RR. */
        !           290:       for (i = nlow; i < nhigh; i++)
        !           291:        if (daemon->serverarray[i]->flags & SERV_LOCAL_ADDRESS)
        !           292:          break;
        !           293:       
        !           294:       /* failed, return failure. */
        !           295:       if (i == nhigh)
        !           296:        nhigh = nlow;
        !           297:     }
        !           298:   else
        !           299:     {
        !           300:       /* Now the servers are on order between low and high, in the order
        !           301:         IPv6 addr, IPv4 addr, return zero for both, resolvconf servers, send upstream, no-data return.
        !           302:         
        !           303:         See which of those match our query in that priority order and narrow (low, high) */
        !           304:       
        !           305:       for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
        !           306:       
        !           307:       if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV6))
        !           308:        nhigh = i;
        !           309:       else
        !           310:        {
        !           311:          nlow = i;
        !           312:          
        !           313:          for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
        !           314:          
        !           315:          if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV4))
        !           316:            nhigh = i;
        !           317:          else
        !           318:            {
        !           319:              nlow = i;
        !           320:              
        !           321:              for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
        !           322:              
        !           323:              if (!(flags & F_SERVER) && i != nlow && (flags & (F_IPV4 | F_IPV6)))
        !           324:                nhigh = i;
        !           325:              else
        !           326:                {
        !           327:                  nlow = i;
        !           328:                  
        !           329:                  /* Short to resolv.conf servers */
        !           330:                  for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
        !           331:                  
        !           332:                  if (i != nlow)
        !           333:                    nhigh = i;
        !           334:                  else
        !           335:                    {
        !           336:                      /* now look for a server */
        !           337:                      for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
        !           338:                      
        !           339:                      if (i != nlow)
        !           340:                        {
        !           341:                          /* If we want a server that can do DNSSEC, and this one can't, 
        !           342:                             return nothing, similarly if were looking only for a server
        !           343:                             for a particular domain. */
        !           344:                          if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
        !           345:                            nlow = nhigh;
        !           346:                          else if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
        !           347:                            nlow = nhigh;
        !           348:                          else
        !           349:                            nhigh = i;
        !           350:                        }
        !           351:                      else
        !           352:                        {
        !           353:                          /* --local=/domain/, only return if we don't need a server. */
        !           354:                          if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER))
        !           355:                            nhigh = i;
        !           356:                        }
        !           357:                    }
        !           358:                }
        !           359:            }
        !           360:        }
        !           361:     }
        !           362: 
        !           363:   *lowout = nlow;
        !           364:   *highout = nhigh;
        !           365:   
        !           366:   return (nlow != nhigh);
        !           367: }
        !           368: 
        !           369: int is_local_answer(time_t now, int first, char *name)
        !           370: {
        !           371:   int flags = 0;
        !           372:   int rc = 0;
        !           373:   
        !           374:   if ((flags = daemon->serverarray[first]->flags) & SERV_LITERAL_ADDRESS)
        !           375:     {
        !           376:       if (flags & SERV_4ADDR)
        !           377:        rc = F_IPV4;
        !           378:       else if (flags & SERV_6ADDR)
        !           379:        rc = F_IPV6;
        !           380:       else if (flags & SERV_ALL_ZEROS)
        !           381:        rc = F_IPV4 | F_IPV6;
        !           382:       else
        !           383:        {
        !           384:          /* argument first is the first struct server which matches the query type;
        !           385:             now roll back to the server which is just the same domain, to check if that 
        !           386:             provides an answer of a different type. */
        !           387: 
        !           388:          for (;first > 0 && order_servers(daemon->serverarray[first-1], daemon->serverarray[first]) == 0; first--);
        !           389:          
        !           390:          if ((daemon->serverarray[first]->flags & SERV_LOCAL_ADDRESS) ||
        !           391:              check_for_local_domain(name, now))
        !           392:            rc = F_NOERR;
        !           393:          else
        !           394:            rc = F_NXDOMAIN;
        !           395:        }
        !           396:     }
        !           397: 
        !           398:   return rc;
        !           399: }
        !           400: 
        !           401: size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede)
        !           402: {
        !           403:   int trunc = 0, anscount = 0;
        !           404:   unsigned char *p;
        !           405:   int start;
        !           406:   union all_addr addr;
        !           407:   
        !           408:   if (flags & (F_NXDOMAIN | F_NOERR))
        !           409:     log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0);
        !           410:          
        !           411:   setup_reply(header, flags, ede);
        !           412:          
        !           413:   if (!(p = skip_questions(header, size)))
        !           414:     return 0;
        !           415:          
        !           416:   if (flags & gotname & F_IPV4)
        !           417:     for (start = first; start != last; start++)
        !           418:       {
        !           419:        struct serv_addr4 *srv = (struct serv_addr4 *)daemon->serverarray[start];
        !           420: 
        !           421:        if (srv->flags & SERV_ALL_ZEROS)
        !           422:          memset(&addr, 0, sizeof(addr));
        !           423:        else
        !           424:          addr.addr4 = srv->addr;
        !           425:        
        !           426:        if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr))
        !           427:          anscount++;
        !           428:        log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL, 0);
        !           429:       }
        !           430:   
        !           431:   if (flags & gotname & F_IPV6)
        !           432:     for (start = first; start != last; start++)
        !           433:       {
        !           434:        struct serv_addr6 *srv = (struct serv_addr6 *)daemon->serverarray[start];
        !           435: 
        !           436:        if (srv->flags & SERV_ALL_ZEROS)
        !           437:          memset(&addr, 0, sizeof(addr));
        !           438:        else
        !           439:          addr.addr6 = srv->addr;
        !           440:        
        !           441:        if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr))
        !           442:          anscount++;
        !           443:        log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL, 0);
        !           444:       }
        !           445: 
        !           446:   if (trunc)
        !           447:     header->hb3 |= HB3_TC;
        !           448:   header->ancount = htons(anscount);
        !           449:   
        !           450:   return p - (unsigned char *)header;
        !           451: }
        !           452: 
        !           453: #ifdef HAVE_DNSSEC
        !           454: int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
        !           455: {
        !           456:   int first, last, index;
        !           457: 
        !           458:   /* Find server to send DNSSEC query to. This will normally be the 
        !           459:      same as for the original query, but may be another if
        !           460:      servers for domains are involved. */                    
        !           461:   if (!lookup_domain(keyname, F_DNSSECOK, &first, &last))
        !           462:     return -1;
        !           463: 
        !           464:   for (index = first; index != last; index++)
        !           465:     if (daemon->serverarray[index] == server)
        !           466:       break;
        !           467:              
        !           468:   /* No match to server used for original query.
        !           469:      Use newly looked up set. */
        !           470:   if (index == last)
        !           471:     index =  daemon->serverarray[first]->last_server == -1 ?
        !           472:       first : daemon->serverarray[first]->last_server;
        !           473: 
        !           474:   if (firstp)
        !           475:     *firstp = first;
        !           476: 
        !           477:   if (lastp)
        !           478:     *lastp = last;
        !           479:    
        !           480:   return index;
        !           481: }
        !           482: #endif
        !           483: 
        !           484: /* order by size, then by dictionary order */
        !           485: static int order(char *qdomain, size_t qlen, struct server *serv)
        !           486: {
        !           487:   size_t dlen = 0;
        !           488:     
        !           489:   /* servers for dotless names always sort last 
        !           490:      searched for name is never dotless. */
        !           491:   if (serv->flags & SERV_FOR_NODOTS)
        !           492:     return -1;
        !           493: 
        !           494:   dlen = serv->domain_len;
        !           495:   
        !           496:   if (qlen < dlen)
        !           497:     return 1;
        !           498:   
        !           499:   if (qlen > dlen)
        !           500:     return -1;
        !           501: 
        !           502:   return hostname_order(qdomain, serv->domain);
        !           503: }
        !           504: 
        !           505: static int order_servers(struct server *s1, struct server *s2)
        !           506: {
        !           507:   int rc;
        !           508: 
        !           509:   /* need full comparison of dotless servers in 
        !           510:      order_qsort() and filter_servers() */
        !           511: 
        !           512:   if (s1->flags & SERV_FOR_NODOTS)
        !           513:      return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1;
        !           514:    
        !           515:   if ((rc = order(s1->domain, s1->domain_len, s2)) != 0)
        !           516:     return rc;
        !           517: 
        !           518:   /* For identical domains, sort wildcard ones first */
        !           519:   if (s1->flags & SERV_WILDCARD)
        !           520:     return (s2->flags & SERV_WILDCARD) ? 0 : 1;
        !           521: 
        !           522:   return (s2->flags & SERV_WILDCARD) ? -1 : 0;
        !           523: }
        !           524:   
        !           525: static int order_qsort(const void *a, const void *b)
        !           526: {
        !           527:   int rc;
        !           528:   
        !           529:   struct server *s1 = *((struct server **)a);
        !           530:   struct server *s2 = *((struct server **)b);
        !           531:   
        !           532:   rc = order_servers(s1, s2);
        !           533: 
        !           534:   /* Sort all literal NODATA and local IPV4 or IPV6 responses together,
        !           535:      in a very specific order. We flip the SERV_LITERAL_ADDRESS bit
        !           536:      so the order is IPv6 literal, IPv4 literal, all-zero literal, 
        !           537:      unqualified servers, upstream server, NXDOMAIN literal. */
        !           538:   if (rc == 0)
        !           539:     rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
        !           540:       ((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
        !           541: 
        !           542:   /* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
        !           543:   if (rc == 0)
        !           544:     if (!(s1->flags & SERV_LITERAL_ADDRESS))
        !           545:       rc = s1->serial - s2->serial;
        !           546: 
        !           547:   return rc;
        !           548: }
        !           549: 
        !           550: 
        !           551: /* When loading large numbers of server=.... lines during startup,
        !           552:    there's no possibility that there will be server records that can be reused, but
        !           553:    searching a long list for each server added grows as O(n^2) and slows things down.
        !           554:    This flag is set only if is known there may be free server records that can be reused.
        !           555:    There's a call to mark_servers(0) in read_opts() to reset the flag before
        !           556:    main config read. */
        !           557: 
        !           558: static int maybe_free_servers = 0;
        !           559: 
        !           560: /* Must be called before  add_update_server() to set daemon->servers_tail */
        !           561: void mark_servers(int flag)
        !           562: {
        !           563:   struct server *serv, *next, **up;
        !           564: 
        !           565:   maybe_free_servers = !!flag;
        !           566:   
        !           567:   daemon->servers_tail = NULL;
        !           568:   
        !           569:   /* mark everything with argument flag */
        !           570:   for (serv = daemon->servers; serv; serv = serv->next)
        !           571:     {
        !           572:       if (serv->flags & flag)
        !           573:        serv->flags |= SERV_MARK;
        !           574:       else
        !           575:        serv->flags &= ~SERV_MARK;
        !           576: 
        !           577:       daemon->servers_tail = serv;
        !           578:     }
        !           579:   
        !           580:   /* --address etc is different: since they are expected to be 
        !           581:      1) numerous and 2) not reloaded often. We just delete 
        !           582:      and recreate. */
        !           583:   if (flag)
        !           584:     for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = next)
        !           585:       {
        !           586:        next = serv->next;
        !           587: 
        !           588:        if (serv->flags & flag)
        !           589:          {
        !           590:            *up = next;
        !           591:            free(serv->domain);
        !           592:            free(serv);
        !           593:          }
        !           594:        else 
        !           595:          up = &serv->next;
        !           596:       }
        !           597: }
        !           598: 
        !           599: void cleanup_servers(void)
        !           600: {
        !           601:   struct server *serv, *tmp, **up;
        !           602: 
        !           603:   /* unlink and free anything still marked. */
        !           604:   for (serv = daemon->servers, up = &daemon->servers, daemon->servers_tail = NULL; serv; serv = tmp) 
        !           605:     {
        !           606:       tmp = serv->next;
        !           607:       if (serv->flags & SERV_MARK)
        !           608:        {
        !           609:          server_gone(serv);
        !           610:          *up = serv->next;
        !           611:         free(serv->domain);
        !           612:         free(serv);
        !           613:        }
        !           614:       else 
        !           615:        {
        !           616:          up = &serv->next;
        !           617:          daemon->servers_tail = serv;
        !           618:        }
        !           619:     }
        !           620: }
        !           621: 
        !           622: int add_update_server(int flags,
        !           623:                      union mysockaddr *addr,
        !           624:                      union mysockaddr *source_addr,
        !           625:                      const char *interface,
        !           626:                      const char *domain,
        !           627:                      union all_addr *local_addr)
        !           628: {
        !           629:   struct server *serv = NULL;
        !           630:   char *alloc_domain;
        !           631:   
        !           632:   if (!domain)
        !           633:     domain = "";
        !           634: 
        !           635:   /* .domain == domain, for historical reasons. */
        !           636:   if (*domain == '.')
        !           637:     while (*domain == '.') domain++;
        !           638:   else if (*domain == '*')
        !           639:     {
        !           640:       domain++;
        !           641:       if (*domain != 0)
        !           642:        flags |= SERV_WILDCARD;
        !           643:     }
        !           644:   
        !           645:   if (*domain == 0)
        !           646:     alloc_domain = whine_malloc(1);
        !           647:   else
        !           648:     alloc_domain = canonicalise((char *)domain, NULL);
        !           649: 
        !           650:   if (!alloc_domain)
        !           651:     return 0;
        !           652: 
        !           653:   if (flags & SERV_IS_LOCAL)
        !           654:     {
        !           655:       size_t size;
        !           656:       
        !           657:       if (flags & SERV_6ADDR)
        !           658:        size = sizeof(struct serv_addr6);
        !           659:       else if (flags & SERV_4ADDR)
        !           660:        size = sizeof(struct serv_addr4);
        !           661:       else
        !           662:        size = sizeof(struct serv_local);
        !           663:       
        !           664:       if (!(serv = whine_malloc(size)))
        !           665:        {
        !           666:          free(alloc_domain);
        !           667:          return 0;
        !           668:        }
        !           669:       
        !           670:       serv->next = daemon->local_domains;
        !           671:       daemon->local_domains = serv;
        !           672:       
        !           673:       if (flags & SERV_4ADDR)
        !           674:        ((struct serv_addr4*)serv)->addr = local_addr->addr4;
        !           675:       
        !           676:       if (flags & SERV_6ADDR)
        !           677:        ((struct serv_addr6*)serv)->addr = local_addr->addr6;
        !           678:     }
        !           679:   else
        !           680:     { 
        !           681:       /* Upstream servers. See if there is a suitable candidate, if so unmark
        !           682:         and move to the end of the list, for order. The entry found may already
        !           683:         be at the end. */
        !           684:       struct server **up, *tmp;
        !           685: 
        !           686:       serv = NULL;
        !           687:       
        !           688:       if (maybe_free_servers)
        !           689:        for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
        !           690:          {
        !           691:            tmp = serv->next;
        !           692:            if ((serv->flags & SERV_MARK) &&
        !           693:                hostname_isequal(alloc_domain, serv->domain))
        !           694:              {
        !           695:                /* Need to move down? */
        !           696:                if (serv->next)
        !           697:                  {
        !           698:                    *up = serv->next;
        !           699:                    daemon->servers_tail->next = serv;
        !           700:                    daemon->servers_tail = serv;
        !           701:                    serv->next = NULL;
        !           702:                  }
        !           703:                break;
        !           704:              }
        !           705:            else
        !           706:              up = &serv->next;
        !           707:          }
        !           708:       
        !           709:       if (serv)
        !           710:        {
        !           711:          free(alloc_domain);
        !           712:          alloc_domain = serv->domain;
        !           713:        }
        !           714:       else
        !           715:        {
        !           716:          if (!(serv = whine_malloc(sizeof(struct server))))
        !           717:            {
        !           718:              free(alloc_domain);
        !           719:              return 0;
        !           720:            }
        !           721:          
        !           722:          memset(serv, 0, sizeof(struct server));
        !           723:          
        !           724:          /* Add to the end of the chain, for order */
        !           725:          if (daemon->servers_tail)
        !           726:            daemon->servers_tail->next = serv;
        !           727:          else
        !           728:            daemon->servers = serv;
        !           729:          daemon->servers_tail = serv;
        !           730:        }
        !           731:       
        !           732: #ifdef HAVE_LOOP
        !           733:       serv->uid = rand32();
        !           734: #endif      
        !           735:          
        !           736:       if (interface)
        !           737:        safe_strncpy(serv->interface, interface, sizeof(serv->interface));
        !           738:       if (addr)
        !           739:        serv->addr = *addr;
        !           740:       if (source_addr)
        !           741:        serv->source_addr = *source_addr;
        !           742:     }
        !           743:     
        !           744:   serv->flags = flags;
        !           745:   serv->domain = alloc_domain;
        !           746:   serv->domain_len = strlen(alloc_domain);
        !           747:   
        !           748:   return 1;
        !           749: }
        !           750: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>