X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fhost.c;h=f3652c3090e0907d3d6ab9c15ab0fb7913425b9f;hb=a0d6ba8acc09196843fdea4132bb0814b2e81132;hp=c2a44a4313c9790f6b3bc5654667f6473941c2ee;hpb=8e669ac162fe3b1040297f1d021de10778dce9d9;p=exim.git diff --git a/src/src/host.c b/src/src/host.c index c2a44a431..f3652c309 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/host.c,v 1.9 2005/02/17 11:58:26 ph10 Exp $ */ +/* $Cambridge: exim/src/src/host.c,v 1.17 2005/11/11 10:02:04 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -40,6 +40,9 @@ with these comments: code by Stuart Levy as seen in comp.sys.sgi.admin +August 2005: Apparently this is also needed for AIX systems; USE_INET_NTOA_FIX +should now be set for them as well. + Arguments: sa an in_addr structure Returns: pointer to static text string */ @@ -99,6 +102,13 @@ number of multihomed host IP addresses into the order, so as to get repeatability. This doesn't have to be efficient. But don't interchange IPv4 and IPv6 addresses! +NOTE: +This sorting is not necessary for the new test harness, because it +doesn't call the real DNS resolver, and its output is repeatable. However, +until the old test harness is discarded, we need to retain this capability. +The new harness is being developed towards the end of 2005. It will be some +time before it can do everything that the old one can do. + Arguments: host -> the first host item last -> the last host item @@ -132,6 +142,186 @@ while (!done) +/************************************************* +* Replace gethostbyname() when testing * +*************************************************/ + +/* This function is called instead of gethostbyname(), gethostbyname2(), or +getipnodebyname() when running in the test harness. It recognizes the name +"manyhome.test.ex" and generates a humungous number of IP addresses. It also +recognizes an unqualified "localhost" and forces it to the appropriate loopback +address. IP addresses are treated as literals. For other names, it uses the DNS +to find the host name. In the new test harness, this means it will access only +the fake DNS resolver. In the old harness it will call the real resolver and +access the test zone. + +Arguments: + name the host name or a textual IP address + af AF_INET or AF_INET6 + error_num where to put an error code: + HOST_NOT_FOUND/TRY_AGAIN/NO_RECOVERY/NO_DATA + +Returns: a hostent structure or NULL for an error +*/ + +static struct hostent * +host_fake_gethostbyname(uschar *name, int af, int *error_num) +{ +#if HAVE_IPV6 +int alen = (af == AF_INET)? sizeof(struct in_addr):sizeof(struct in6_addr); +#else +int alen = sizeof(struct in_addr); +#endif + +int ipa; +uschar *lname = name; +uschar *adds; +uschar **alist; +struct hostent *yield; +dns_answer dnsa; +dns_scan dnss; +dns_record *rr; + +DEBUG(D_host_lookup) + debug_printf("using host_fake_gethostbyname for %s (%s)\n", name, + (af == AF_INET)? "IPv4" : "IPv6"); + +/* Handle the name that needs a vast number of IP addresses */ + +if (Ustrcmp(name, "manyhome.test.ex") == 0 && af == AF_INET) + { + int i, j; + yield = store_get(sizeof(struct hostent)); + alist = store_get(2049 * sizeof(char *)); + adds = store_get(2048 * alen); + yield->h_name = CS name; + yield->h_aliases = NULL; + yield->h_addrtype = af; + yield->h_length = alen; + yield->h_addr_list = CSS alist; + for (i = 104; i <= 111; i++) + { + for (j = 0; j <= 255; j++) + { + *alist++ = adds; + *adds++ = 10; + *adds++ = 250; + *adds++ = i; + *adds++ = j; + } + } + *alist = NULL; + return yield; + } + +/* Handle unqualified "localhost" */ + +if (Ustrcmp(name, "localhost") == 0) + lname = (af == AF_INET)? US"127.0.0.1" : US"::1"; + +/* Handle a literal IP address */ + +ipa = string_is_ip_address(lname, NULL); +if (ipa != 0) + { + if ((ipa == 4 && af == AF_INET) || + (ipa == 6 && af == AF_INET6)) + { + int i, n; + int x[4]; + yield = store_get(sizeof(struct hostent)); + alist = store_get(2 * sizeof(char *)); + adds = store_get(alen); + yield->h_name = CS name; + yield->h_aliases = NULL; + yield->h_addrtype = af; + yield->h_length = alen; + yield->h_addr_list = CSS alist; + *alist++ = adds; + n = host_aton(lname, x); + for (i = 0; i < n; i++) + { + int y = x[i]; + *adds++ = (y >> 24) & 255; + *adds++ = (y >> 16) & 255; + *adds++ = (y >> 8) & 255; + *adds++ = y & 255; + } + *alist = NULL; + } + + /* Wrong kind of literal address */ + + else + { + *error_num = HOST_NOT_FOUND; + return NULL; + } + } + +/* Handle a host name */ + +else + { + int type = (af == AF_INET)? T_A:T_AAAA; + int rc = dns_lookup(&dnsa, lname, type, NULL); + int count = 0; + + switch(rc) + { + case DNS_SUCCEED: break; + case DNS_NOMATCH: *error_num = HOST_NOT_FOUND; return NULL; + case DNS_NODATA: *error_num = NO_DATA; return NULL; + case DNS_AGAIN: *error_num = TRY_AGAIN; return NULL; + default: + case DNS_FAIL: *error_num = NO_RECOVERY; return NULL; + } + + for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); + rr != NULL; + rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) + { + if (rr->type == type) count++; + } + + yield = store_get(sizeof(struct hostent)); + alist = store_get((count + 1) * sizeof(char **)); + adds = store_get(count *alen); + + yield->h_name = CS name; + yield->h_aliases = NULL; + yield->h_addrtype = af; + yield->h_length = alen; + yield->h_addr_list = CSS alist; + + for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); + rr != NULL; + rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) + { + int i, n; + int x[4]; + dns_address *da; + if (rr->type != type) continue; + da = dns_address_from_rr(&dnsa, rr); + *alist++ = adds; + n = host_aton(da->address, x); + for (i = 0; i < n; i++) + { + int y = x[i]; + *adds++ = (y >> 24) & 255; + *adds++ = (y >> 16) & 255; + *adds++ = (y >> 8) & 255; + *adds++ = y & 255; + } + } + *alist = NULL; + } + +return yield; +} + + + /************************************************* * Build chain of host items from list * *************************************************/ @@ -234,7 +424,7 @@ Returns: 0 if there is no port, else the port number. If there's a syntax */ int -host_extract_port(uschar *address) +host_address_extract_port(uschar *address) { int port = 0; uschar *endptr; @@ -278,6 +468,59 @@ return port; } +/************************************************* +* Get port from a host item's name * +*************************************************/ + +/* This function is called when finding the IP address for a host that is in a +list of hosts explicitly configured, such as in the manualroute router, or in a +fallback hosts list. We see if there is a port specification at the end of the +host name, and if so, remove it. A minimum length of 3 is required for the +original name; nothing shorter is recognized as having a port. + +We test for a name ending with a sequence of digits; if preceded by colon we +have a port if the character before the colon is ] and the name starts with [ +or if there are no other colons in the name (i.e. it's not an IPv6 address). + +Arguments: pointer to the host item +Returns: a port number or PORT_NONE +*/ + +int +host_item_get_port(host_item *h) +{ +uschar *p; +int port, x; +int len = Ustrlen(h->name); + +if (len < 3 || (p = h->name + len - 1, !isdigit(*p))) return PORT_NONE; + +/* Extract potential port number */ + +port = *p-- - '0'; +x = 10; + +while (p > h->name + 1 && isdigit(*p)) + { + port += (*p-- - '0') * x; + x *= 10; + } + +/* The smallest value of p at this point is h->name + 1. */ + +if (*p != ':') return PORT_NONE; + +if (p[-1] == ']' && h->name[0] == '[') + h->name = string_copyn(h->name + 1, p - h->name - 2); +else if (Ustrchr(h->name, ':') == p) + h->name = string_copyn(h->name, p - h->name); +else return PORT_NONE; + +DEBUG(D_route|D_host_lookup) debug_printf("host=%s port=%d\n", h->name, port); +return port; +} + + #ifndef STAND_ALONE /* Omit when standalone testing */ @@ -292,8 +535,9 @@ as follows: (a) No sender_host_name or sender_helo_name: "[ip address]" (b) Just sender_host_name: "host_name [ip address]" -(c) Just sender_helo_name: "(helo_name) [ip address]" -(d) The two are identical: "host_name [ip address]" +(c) Just sender_helo_name: "(helo_name) [ip address]" unless helo is IP + in which case: "[ip address}" +(d) The two are identical: "host_name [ip address]" includes helo = IP (e) The two are different: "host_name (helo_name) [ip address]" If log_incoming_port is set, the sending host's port number is added to the IP @@ -314,7 +558,9 @@ Returns: nothing void host_build_sender_fullhost(void) { +BOOL show_helo = TRUE; uschar *address; +int len; int old_pool = store_pool; if (sender_host_address == NULL) return; @@ -330,6 +576,43 @@ address = string_sprintf("[%s]:%d", sender_host_address, sender_host_port); if ((log_extra_selector & LX_incoming_port) == 0 || sender_host_port <= 0) *(Ustrrchr(address, ':')) = 0; +/* If there's no EHLO/HELO data, we can't show it. */ + +if (sender_helo_name == NULL) show_helo = FALSE; + +/* If HELO/EHLO was followed by an IP literal, it's messy because of two +features of IPv6. Firstly, there's the "IPv6:" prefix (Exim is liberal and +doesn't require this, for historical reasons). Secondly, IPv6 addresses may not +be given in canonical form, so we have to canonicize them before comparing. As +it happens, the code works for both IPv4 and IPv6. */ + +else if (sender_helo_name[0] == '[' && + sender_helo_name[(len=Ustrlen(sender_helo_name))-1] == ']') + { + int offset = 1; + uschar *helo_ip; + + if (strncmpic(sender_helo_name + 1, US"IPv6:", 5) == 0) offset += 5; + if (strncmpic(sender_helo_name + 1, US"IPv4:", 5) == 0) offset += 5; + + helo_ip = string_copyn(sender_helo_name + offset, len - offset - 1); + + if (string_is_ip_address(helo_ip, NULL) != 0) + { + int x[4], y[4]; + int sizex, sizey; + uschar ipx[48], ipy[48]; /* large enough for full IPv6 */ + + sizex = host_aton(helo_ip, x); + sizey = host_aton(sender_host_address, y); + + (void)host_nmtoa(sizex, x, -1, ipx, ':'); + (void)host_nmtoa(sizey, y, -1, ipy, ':'); + + if (strcmpic(ipx, ipy) == 0) show_helo = FALSE; + } + } + /* Host name is not verified */ if (sender_host_name == NULL) @@ -345,7 +628,7 @@ if (sender_host_name == NULL) sender_rcvhost = string_cat(NULL, &size, &ptr, address, adlen); - if (sender_ident != NULL || sender_helo_name != NULL || portptr != NULL) + if (sender_ident != NULL || show_helo || portptr != NULL) { int firstptr; sender_rcvhost = string_cat(sender_rcvhost, &size, &ptr, US" (", 2); @@ -355,7 +638,7 @@ if (sender_host_name == NULL) sender_rcvhost = string_append(sender_rcvhost, &size, &ptr, 2, US"port=", portptr + 1); - if (sender_helo_name != NULL) + if (show_helo) sender_rcvhost = string_append(sender_rcvhost, &size, &ptr, 2, (firstptr == ptr)? US"helo=" : US" helo=", sender_helo_name); @@ -374,24 +657,15 @@ if (sender_host_name == NULL) store_reset(sender_rcvhost + ptr + 1); } -/* Host name is known and verified. */ +/* Host name is known and verified. Unless we've already found that the HELO +data matches the IP address, compare it with the name. */ else { - int len; - if (sender_helo_name == NULL || - strcmpic(sender_host_name, sender_helo_name) == 0 || - (sender_helo_name[0] == '[' && - sender_helo_name[(len=Ustrlen(sender_helo_name))-1] == ']' && - strncmpic(sender_helo_name+1, sender_host_address, len - 2) == 0)) - { - sender_fullhost = string_sprintf("%s %s", sender_host_name, address); - sender_rcvhost = (sender_ident == NULL)? - string_sprintf("%s (%s)", sender_host_name, address) : - string_sprintf("%s (%s ident=%s)", sender_host_name, address, - sender_ident); - } - else + if (show_helo && strcmpic(sender_host_name, sender_helo_name) == 0) + show_helo = FALSE; + + if (show_helo) { sender_fullhost = string_sprintf("%s (%s) %s", sender_host_name, sender_helo_name, address); @@ -401,6 +675,14 @@ else string_sprintf("%s\n\t(%s helo=%s ident=%s)", sender_host_name, address, sender_helo_name, sender_ident); } + else + { + sender_fullhost = string_sprintf("%s %s", sender_host_name, address); + sender_rcvhost = (sender_ident == NULL)? + string_sprintf("%s (%s)", sender_host_name, address) : + string_sprintf("%s (%s ident=%s)", sender_host_name, address, + sender_ident); + } } store_pool = old_pool; @@ -492,7 +774,7 @@ ip_address_item *next; while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) { - int port = host_extract_port(s); /* Leaves just the IP address */ + int port = host_address_extract_port(s); /* Leaves just the IP address */ if (string_is_ip_address(s, NULL) == 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Malformed IP address \"%s\" in %s", s, name); @@ -809,7 +1091,7 @@ if (Ustrchr(address, ':') != NULL) /* Handle IPv4 address */ -sscanf(CS address, "%d.%d.%d.%d", x, x+1, x+2, x+3); +(void)sscanf(CS address, "%d.%d.%d.%d", x, x+1, x+2, x+3); bin[v4offset] = (x[0] << 24) + (x[1] << 16) + (x[2] << 8) + x[3]; return v4offset+1; } @@ -1727,16 +2009,27 @@ for (i = 1; i <= times; struct hostent *hostdata; #if HAVE_IPV6 + if (running_in_test_harness) + hostdata = host_fake_gethostbyname(host->name, af, &error_num); + else + { #if HAVE_GETIPNODEBYNAME hostdata = getipnodebyname(CS host->name, af, 0, &error_num); #else hostdata = gethostbyname2(CS host->name, af); error_num = h_errno; #endif - #else - hostdata = gethostbyname(CS host->name); - error_num = h_errno; - #endif + } + + #else /* not HAVE_IPV6 */ + if (running_in_test_harness) + hostdata = host_fake_gethostbyname(host->name, AF_INET, &error_num); + else + { + hostdata = gethostbyname(CS host->name); + error_num = h_errno; + } + #endif /* HAVE_IPV6 */ if (hostdata == NULL) { @@ -1967,7 +2260,6 @@ to do so. On an IPv4 system, go round the loop once only, looking only for A records. */ #if HAVE_IPV6 - #ifndef STAND_ALONE if (dns_ipv4_lookup != NULL && match_isinlist(host->name, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN, @@ -2231,8 +2523,10 @@ if ((whichrrs & HOST_FIND_BY_SRV) != 0) if (rc == DNS_FAIL || rc == DNS_AGAIN) { + #ifndef STAND_ALONE if (match_isinlist(host->name, &srv_fail_domains, 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) != OK) + #endif return HOST_FIND_AGAIN; DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " "(domain in srv_fail_domains)\n", (rc == DNS_FAIL)? "FAIL":"AGAIN"); @@ -2253,8 +2547,10 @@ if (rc != DNS_SUCCEED && (whichrrs & HOST_FIND_BY_MX) != 0) if (rc == DNS_NOMATCH) return HOST_FIND_FAILED; if (rc == DNS_FAIL || rc == DNS_AGAIN) { + #ifndef STAND_ALONE if (match_isinlist(host->name, &mx_fail_domains, 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) != OK) + #endif return HOST_FIND_AGAIN; DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " "(domain in mx_fail_domains)\n", (rc == DNS_FAIL)? "FAIL":"AGAIN"); @@ -2900,18 +3196,6 @@ return yield; #ifdef STAND_ALONE -BOOL alldigits(uschar *buffer) -{ -if (!isdigit(*buffer)) return FALSE; -if (*buffer == '0' && buffer[1] == 'x') - { - buffer++; - while (isxdigit(*(++buffer))); - } -else while (isdigit(*(++buffer))); -return (*buffer == 0); -} - int main(int argc, char **cargv) { host_item h; @@ -2967,22 +3251,22 @@ while (Ufgets(buffer, 256, stdin) != NULL) else if (Ustrcmp(buffer, "no_qualify_single") == 0) qualify_single = FALSE; else if (Ustrcmp(buffer, "search_parents") == 0) search_parents = TRUE; else if (Ustrcmp(buffer, "no_search_parents") == 0) search_parents = FALSE; + else if (Ustrcmp(buffer, "test_harness") == 0) + running_in_test_harness = !running_in_test_harness; + else if (Ustrcmp(buffer, "res_debug") == 0) + { + _res.options ^= RES_DEBUG; + } else if (Ustrncmp(buffer, "retrans", 7) == 0) { - sscanf(CS(buffer+8), "%d", &dns_retrans); + (void)sscanf(CS(buffer+8), "%d", &dns_retrans); _res.retrans = dns_retrans; } else if (Ustrncmp(buffer, "retry", 5) == 0) { - sscanf(CS(buffer+6), "%d", &dns_retry); + (void)sscanf(CS(buffer+6), "%d", &dns_retry); _res.retry = dns_retry; } - else if (alldigits(buffer)) - { - debug_selector = Ustrtol(buffer, NULL, 0); - _res.options &= ~RES_DEBUG; - DEBUG(D_resolver) _res.options |= RES_DEBUG; - } else { int flags = whichrrs;