-/* Now we have to ensure addresses exist for all the hosts. We have ensured
-above that the names in the host items are all unique. The addresses may have
-been returned in the additional data section of the DNS query. Because it is
-more expensive to scan the returned DNS records (because you have to expand the
-names) we do a single scan over them, and multiple scans of the chain of host
-items (which is typically only 3 or 4 long anyway.) Add extra host items for
-multi-homed hosts. */
-
-for (rr = dns_next_rr(&dnsa, &dnss, RESET_ADDITIONAL);
- rr != NULL;
- rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
- {
- dns_address *da;
- int status = hstatus_unknown;
- int why = hwhy_unknown;
- int randoffset;
-
- if (rr->type != T_A
- #if HAVE_IPV6
- && rr->type != T_AAAA
- #ifdef SUPPORT_A6
- && rr->type != T_A6
- #endif
- #endif
- ) continue;
-
- /* Find the first host that matches this record's name. If there isn't
- one, move on to the next RR. */
-
- for (h = host; h != last->next; h = h->next)
- { if (strcmpic(h->name, rr->name) == 0) break; }
- if (h == last->next) continue;
-
- /* For IPv4 addresses, add 500 to the random part of the sort key, to ensure
- they sort after IPv6 addresses. */
-
- randoffset = (rr->type == T_A)? 500 : 0;
-
- /* Get the list of textual addresses for this RR. There may be more than one
- if it is an A6 RR. Then loop to handle multiple addresses from an A6 record.
- If there are none, nothing will get done - the record is ignored. */
-
- for (da = dns_address_from_rr(&dnsa, rr); da != NULL; da = da->next)
- {
- /* Set status for an ignorable host. */
-
- #ifndef STAND_ALONE
- if (ignore_target_hosts != NULL &&
- verify_check_this_host(&ignore_target_hosts, NULL, h->name,
- da->address, NULL) == OK)
- {
- DEBUG(D_host_lookup)
- debug_printf("ignored host %s [%s]\n", h->name, da->address);
- status = hstatus_unusable;
- why = hwhy_ignored;
- }
- #endif
-
- /* If the address is already set for this host, it may be that
- we just have a duplicate DNS record. Alternatively, this may be
- a multi-homed host. Search all items with the same host name
- (they will all be together) and if this address is found, skip
- to the next RR. */
-
- if (h->address != NULL)
- {
- int new_sort_key;
- host_item *thishostlast;
- host_item *hh = h;
-
- do
- {
- if (hh->address != NULL && Ustrcmp(CS da->address, hh->address) == 0)
- goto DNS_NEXT_RR; /* Need goto to escape from inner loop */
- thishostlast = hh;
- hh = hh->next;
- }
- while (hh != last->next && strcmpic(hh->name, rr->name) == 0);
-
- /* We have a multi-homed host, since we have a new address for
- an existing name. Create a copy of the current item, and give it
- the new address. RRs can be in arbitrary order, but one is supposed
- to randomize the addresses of multi-homed hosts, so compute a new
- sorting key and do that. [Latest SMTP RFC says not to randomize multi-
- homed hosts, but to rely on the resolver. I'm not happy about that -
- caching in the resolver will not rotate as often as the name server
- does.] */
-
- new_sort_key = h->mx * 1000 + random_number(500) + randoffset;
- hh = store_get(sizeof(host_item));
-
- /* New address goes first: insert the new block after the first one
- (so as not to disturb the original pointer) but put the new address
- in the original block. */
-
- if (new_sort_key < h->sort_key)
- {
- *hh = *h; /* Note: copies the port */
- h->next = hh;
- h->address = da->address;
- h->sort_key = new_sort_key;
- h->status = status;
- h->why = why;
- }
-
- /* Otherwise scan down the addresses for this host to find the
- one to insert after. */
-
- else
- {
- while (h != thishostlast)
- {
- if (new_sort_key < h->next->sort_key) break;
- h = h->next;
- }
- *hh = *h; /* Note: copies the port */
- h->next = hh;
- hh->address = da->address;
- hh->sort_key = new_sort_key;
- hh->status = status;
- hh->why = why;
- }
-
- if (h == last) last = hh; /* Inserted after last */
- }
-
- /* The existing item doesn't have its address set yet, so just set it.
- Ensure that an IPv4 address gets its sort key incremented in case an IPv6
- address is found later. */
-
- else
- {
- h->address = da->address; /* Port should be set already */
- h->status = status;
- h->why = why;
- h->sort_key += randoffset;
- }
- } /* Loop for addresses extracted from one RR */
-
- /* Carry on to the next RR. It would be nice to be able to be able to stop
- when every host on the list has an address, but we can't be sure there won't
- be an additional address for a multi-homed host further down the list, so
- we have to continue to the end. */
-
- DNS_NEXT_RR: continue;
- }
-
-/* Set the default yield to failure */
-
-yield = HOST_FIND_FAILED;
-
-/* If we haven't found all the addresses in the additional section, we
-need to search for A or AAAA records explicitly. The names shouldn't point to
-CNAMES, but we use the general lookup function that handles them, just
-in case. If any lookup gives a soft error, change the default yield.
+/* Now we have to find IP addresses for all the hosts. We have ensured above
+that the names in all the host items are unique. Before release 4.61 we used to
+process records from the additional section in the DNS packet that returned the
+MX or SRV records. However, a DNS name server is free to drop any resource
+records from the additional section. In theory, this has always been a
+potential problem, but it is exacerbated by the advent of IPv6. If a host had
+several IPv4 addresses and some were not in the additional section, at least
+Exim would try the others. However, if a host had both IPv4 and IPv6 addresses
+and all the IPv4 (say) addresses were absent, Exim would try only for a IPv6
+connection, and never try an IPv4 address. When there was only IPv4
+connectivity, this was a disaster that did in practice occur.
+
+So, from release 4.61 onwards, we always search for A and AAAA records
+explicitly. The names shouldn't point to CNAMES, but we use the general lookup
+function that handles them, just in case. If any lookup gives a soft error,
+change the default yield.