File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / src / domain-match.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Sep 27 11:02:07 2023 UTC (9 months, 1 week ago) by misho
Branches: dnsmasq, MAIN
CVS tags: v8_2p1, HEAD
Version 8.2p1

    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>