Пример #1
0
Файл: utf8.c Проект: Exim/exim
uschar *
string_address_utf8_to_alabel(const uschar * utf8, uschar ** err)
{
uschar * l, * d;

if (!*utf8) return string_copy(utf8);

DEBUG(D_expand) debug_printf("addr from utf8 <%s>", utf8);

for (const uschar * s = utf8; *s; s++)
  if (*s == '@')
    {
    l = string_copyn(utf8, s - utf8);
    if (  !(l = string_localpart_utf8_to_alabel(l, err))
       || !(d = string_domain_utf8_to_alabel(++s, err))
       )
      return NULL;
    l = string_sprintf("%s@%s", l, d);
    DEBUG(D_expand) debug_printf(" -> <%s>\n", l);
    return l;
    }

l =  string_localpart_utf8_to_alabel(utf8, err);
DEBUG(D_expand) debug_printf(" -> <%s>\n", l);
return l;
}
Пример #2
0
int
dns_lookup(dns_answer *dnsa, const uschar *name, int type,
  const uschar **fully_qualified_name)
{
int i;
const uschar *orig_name = name;
BOOL secure_so_far = TRUE;

/* Loop to follow CNAME chains so far, but no further... */

for (i = 0; i < 10; i++)
  {
  uschar data[256];
  dns_record *rr, cname_rr, type_rr;
  dns_scan dnss;
  int datalen, rc;

  /* DNS lookup failures get passed straight back. */

  if ((rc = dns_basic_lookup(dnsa, name, type)) != DNS_SUCCEED) return rc;

  /* We should have either records of the required type, or a CNAME record,
  or both. We need to know whether both exist for getting the fully qualified
  name, but avoid scanning more than necessary. Note that we must copy the
  contents of any rr blocks returned by dns_next_rr() as they use the same
  area in the dnsa block. */

  cname_rr.data = type_rr.data = NULL;
  for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
       rr;
       rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
    {
    if (rr->type == type)
      {
      if (type_rr.data == NULL) type_rr = *rr;
      if (cname_rr.data != NULL) break;
      }
    else if (rr->type == T_CNAME) cname_rr = *rr;
    }

  /* For the first time round this loop, if a CNAME was found, take the fully
  qualified name from it; otherwise from the first data record, if present. */

  if (i == 0 && fully_qualified_name != NULL)
    {
    uschar * rr_name = cname_rr.data ? cname_rr.name
      : type_rr.data ? type_rr.name : NULL;
    if (  rr_name
       && Ustrcmp(rr_name, *fully_qualified_name) != 0
       && rr_name[0] != '*'
#ifdef EXPERIMENTAL_INTERNATIONAL
       && (  !string_is_utf8(*fully_qualified_name)
	  || Ustrcmp(rr_name,
	       string_domain_utf8_to_alabel(*fully_qualified_name, NULL)) != 0
	  )
#endif
       )
        *fully_qualified_name = string_copy_dnsdomain(rr_name);
    }

  /* If any data records of the correct type were found, we are done. */

  if (type_rr.data != NULL)
    {
    if (!secure_so_far)	/* mark insecure if any element of CNAME chain was */
      dns_set_insecure(dnsa);
    return DNS_SUCCEED;
    }

  /* If there are no data records, we need to re-scan the DNS using the
  domain given in the CNAME record, which should exist (otherwise we should
  have had a failure from dns_lookup). However code against the possibility of
  its not existing. */

  if (cname_rr.data == NULL) return DNS_FAIL;
  datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
    cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, sizeof(data));
  if (datalen < 0) return DNS_FAIL;
  name = data;

  if (!dns_is_secure(dnsa))
    secure_so_far = FALSE;

  DEBUG(D_dns) debug_printf("CNAME found: change to %s\n", name);
  }       /* Loop back to do another lookup */

/*Control reaches here after 10 times round the CNAME loop. Something isn't
right... */

log_write(0, LOG_MAIN, "CNAME loop for %s encountered", orig_name);
return DNS_FAIL;
}
Пример #3
0
int
dns_basic_lookup(dns_answer *dnsa, const uschar *name, int type)
{
#ifndef STAND_ALONE
int rc = -1;
const uschar *save_domain;
#endif
res_state resp = os_get_dns_resolver_res();

tree_node *previous;
uschar node_name[290];

/* DNS lookup failures of any kind are cached in a tree. This is mainly so that
a timeout on one domain doesn't happen time and time again for messages that
have many addresses in the same domain. We rely on the resolver and name server
caching for successful lookups. */

sprintf(CS node_name, "%.255s-%s-%lx", name, dns_text_type(type),
  resp->options);
previous = tree_search(tree_dns_fails, node_name);
if (previous != NULL)
  {
  DEBUG(D_dns) debug_printf("DNS lookup of %.255s-%s: using cached value %s\n",
    name, dns_text_type(type),
      (previous->data.val == DNS_NOMATCH)? "DNS_NOMATCH" :
      (previous->data.val == DNS_NODATA)? "DNS_NODATA" :
      (previous->data.val == DNS_AGAIN)? "DNS_AGAIN" :
      (previous->data.val == DNS_FAIL)? "DNS_FAIL" : "??");
  return previous->data.val;
  }

#ifdef EXPERIMENTAL_INTERNATIONAL
/* Convert all names to a-label form before doing lookup */
  {
  uschar * alabel;
  uschar * errstr = NULL;
  DEBUG(D_dns) if (string_is_utf8(name))
    debug_printf("convert utf8 '%s' to alabel for for lookup\n", name);
  if ((alabel = string_domain_utf8_to_alabel(name, &errstr)), errstr)
    {
    DEBUG(D_dns)
      debug_printf("DNS name '%s' utf8 conversion to alabel failed: %s\n", name,
        errstr);
    host_find_failed_syntax = TRUE;
    return DNS_NOMATCH;
    }
  name = alabel;
  }
#endif

/* If configured, check the hygene of the name passed to lookup. Otherwise,
although DNS lookups may give REFUSED at the lower level, some resolvers
turn this into TRY_AGAIN, which is silly. Give a NOMATCH return, since such
domains cannot be in the DNS. The check is now done by a regular expression;
give it space for substring storage to save it having to get its own if the
regex has substrings that are used - the default uses a conditional.

This test is omitted for PTR records. These occur only in calls from the dnsdb
lookup, which constructs the names itself, so they should be OK. Besides,
bitstring labels don't conform to normal name syntax. (But the aren't used any
more.)

For SRV records, we omit the initial _smtp._tcp. components at the start. */

#ifndef STAND_ALONE   /* Omit this for stand-alone tests */

if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT)
  {
  const uschar *checkname = name;
  int ovector[3*(EXPAND_MAXN+1)];

  dns_pattern_init();

  /* For an SRV lookup, skip over the first two components (the service and
  protocol names, which both start with an underscore). */

  if (type == T_SRV || type == T_TLSA)
    {
    while (*checkname++ != '.');
    while (*checkname++ != '.');
    }

  if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname),
      0, PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)) < 0)
    {
    DEBUG(D_dns)
      debug_printf("DNS name syntax check failed: %s (%s)\n", name,
        dns_text_type(type));
    host_find_failed_syntax = TRUE;
    return DNS_NOMATCH;
    }
  }

#endif /* STAND_ALONE */

/* Call the resolver; for an overlong response, res_search() will return the
number of bytes the message would need, so we need to check for this case. The
effect is to truncate overlong data.

On some systems, res_search() will recognize "A-for-A" queries and return
the IP address instead of returning -1 with h_error=HOST_NOT_FOUND. Some
nameservers are also believed to do this. It is, of course, contrary to the
specification of the DNS, so we lock it out. */

if ((type == T_A || type == T_AAAA) && string_is_ip_address(name, NULL) != 0)
  return DNS_NOMATCH;

/* If we are running in the test harness, instead of calling the normal resolver
(res_search), we call fakens_search(), which recognizes certain special
domains, and interfaces to a fake nameserver for certain special zones. */

dnsa->answerlen = running_in_test_harness
  ? fakens_search(name, type, dnsa->answer, MAXPACKET)
  : res_search(CCS name, C_IN, type, dnsa->answer, MAXPACKET);

if (dnsa->answerlen > MAXPACKET)
  {
  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet (size %d), truncating to %d.\n",
    name, dns_text_type(type), dnsa->answerlen, MAXPACKET);
  dnsa->answerlen = MAXPACKET;
  }

if (dnsa->answerlen < 0) switch (h_errno)
  {
  case HOST_NOT_FOUND:
  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave HOST_NOT_FOUND\n"
    "returning DNS_NOMATCH\n", name, dns_text_type(type));
  return dns_return(name, type, DNS_NOMATCH);

  case TRY_AGAIN:
  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n",
    name, dns_text_type(type));

  /* Cut this out for various test programs */
#ifndef STAND_ALONE
  save_domain = deliver_domain;
  deliver_domain = string_copy(name);  /* set $domain */
  rc = match_isinlist(name, (const uschar **)&dns_again_means_nonexist, 0, NULL, NULL,
    MCL_DOMAIN, TRUE, NULL);
  deliver_domain = save_domain;
  if (rc != OK)
    {
    DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n");
    return dns_return(name, type, DNS_AGAIN);
    }
  DEBUG(D_dns) debug_printf("%s is in dns_again_means_nonexist: returning "
    "DNS_NOMATCH\n", name);
  return dns_return(name, type, DNS_NOMATCH);

#else   /* For stand-alone tests */
  return dns_return(name, type, DNS_AGAIN);
#endif

  case NO_RECOVERY:
  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n"
    "returning DNS_FAIL\n", name, dns_text_type(type));
  return dns_return(name, type, DNS_FAIL);

  case NO_DATA:
  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_DATA\n"
    "returning DNS_NODATA\n", name, dns_text_type(type));
  return dns_return(name, type, DNS_NODATA);

  default:
  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave unknown DNS error %d\n"
    "returning DNS_FAIL\n", name, dns_text_type(type), h_errno);
  return dns_return(name, type, DNS_FAIL);
  }

DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) succeeded\n",
  name, dns_text_type(type));

return DNS_SUCCEED;
}