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>