Exemple #1
0
/* Debug function to print the entire hosttable.  */
gpg_error_t
ks_hkp_print_hosttable (ctrl_t ctrl)
{
  gpg_error_t err;
  int idx, idx2;
  hostinfo_t hi;
  membuf_t mb;
  time_t curtime;
  char *p, *died;
  const char *diedstr;

  err = ks_print_help (ctrl, "hosttable (idx, ipv6, ipv4, dead, name, time):");
  if (err)
    return err;

  /* FIXME: We need a lock for the hosttable.  */
  curtime = gnupg_get_time ();
  for (idx=0; idx < hosttable_size; idx++)
    if ((hi=hosttable[idx]))
      {
        if (hi->dead && hi->died_at)
          {
            died = elapsed_time_string (hi->died_at, curtime);
            diedstr = died? died : "error";
          }
        else
          diedstr = died = NULL;

        if (!hi->iporname_valid)
          {
            char *canon = NULL;

            xfree (hi->iporname);
            hi->iporname = NULL;

            /* Do a lookup just for the display purpose.  */
            if (hi->onion || hi->pool)
              ;
            else if (is_ip_address (hi->name))
              {
                dns_addrinfo_t aibuf, ai;

                /* Turn the numerical IP address string into an AI and
                 * then do a DNS PTR lookup.  */
                if (!resolve_dns_name (hi->name, 0, 0,
                                       SOCK_STREAM,
                                       &aibuf, &canon))
                  {
                    if (canon && is_ip_address (canon))
                      {
                        xfree (canon);
                        canon = NULL;
                      }
                    for (ai = aibuf; !canon && ai; ai = ai->next)
                      {
                        resolve_dns_addr (ai->addr, ai->addrlen,
                                          DNS_WITHBRACKET, &canon);
                        if (canon && is_ip_address (canon))
                          {
                            /* We already have the numeric IP - no need to
                             * display it a second time.  */
                            xfree (canon);
                            canon = NULL;
                          }
                      }
                  }
                free_dns_addrinfo (aibuf);
              }
            else
              {
                dns_addrinfo_t aibuf, ai;

                /* Get the IP address as a string from a name.  Note
                 * that resolve_dns_addr allocates CANON on success
                 * and thus terminates the loop. */
                if (!resolve_dns_name (hi->name, 0,
                                       hi->v6? AF_INET6 : AF_INET,
                                       SOCK_STREAM,
                                       &aibuf, NULL))
                  {
                    for (ai = aibuf; !canon && ai; ai = ai->next)
                      {
                        resolve_dns_addr (ai->addr, ai->addrlen,
                                          DNS_NUMERICHOST|DNS_WITHBRACKET,
                                          &canon);
                      }
                  }
                free_dns_addrinfo (aibuf);
              }

            hi->iporname = canon;
            hi->iporname_valid = 1;
          }

        err = ks_printf_help (ctrl, "%3d %s %s %s %s%s%s%s%s%s%s\n",
                              idx,
                              hi->onion? "O" : hi->v6? "6":" ",
                              hi->v4? "4":" ",
                              hi->dead? "d":" ",
                              hi->name,
                              hi->iporname? " (":"",
                              hi->iporname? hi->iporname : "",
                              hi->iporname? ")":"",
                              diedstr? "  (":"",
                              diedstr? diedstr:"",
                              diedstr? ")":""   );
        xfree (died);
        if (err)
          return err;

        if (hi->cname)
          err = ks_printf_help (ctrl, "  .       %s", hi->cname);
        if (err)
          return err;

        if (hi->pool)
          {
            init_membuf (&mb, 256);
            put_membuf_printf (&mb, "  .   -->");
            for (idx2 = 0; idx2 < hi->pool_len && hi->pool[idx2] != -1; idx2++)
              {
                put_membuf_printf (&mb, " %d", hi->pool[idx2]);
                if (hi->poolidx == hi->pool[idx2])
                  put_membuf_printf (&mb, "*");
              }
            put_membuf( &mb, "", 1);
            p = get_membuf (&mb, NULL);
            if (!p)
              return gpg_error_from_syserror ();
            err = ks_print_help (ctrl, p);
            xfree (p);
            if (err)
              return err;
          }
      }
  return 0;
}
Exemple #2
0
/* Add the host AI under the NAME into the HOSTTABLE.  If PORT is not
   zero, it specifies which port to use to talk to the host for
   PROTOCOL.  If NAME specifies a pool (as indicated by IS_POOL),
   update the given reference table accordingly.  */
static void
add_host (const char *name, int is_pool,
          const dns_addrinfo_t ai,
          enum ks_protocol protocol, unsigned short port)
{
  gpg_error_t tmperr;
  char *tmphost;
  int idx, tmpidx;
  hostinfo_t host;
  int i;

  idx = find_hostinfo (name);
  host = hosttable[idx];

  if (is_pool)
    {
      /* For a pool immediately convert the address to a string.  */
      tmperr = resolve_dns_addr (ai->addr, ai->addrlen,
                                 (DNS_NUMERICHOST | DNS_WITHBRACKET), &tmphost);
    }
  else if (!is_ip_address (name))
    {
      /* This is a hostname.  Use the name as given without going
       * through resolve_dns_addr.  */
      tmphost = xtrystrdup (name);
      if (!tmphost)
        tmperr = gpg_error_from_syserror ();
      else
        tmperr = 0;
    }
  else
    {
      /* Do a PTR lookup on AI.  If a name was not found the function
       * returns the numeric address (with brackets).  */
      tmperr = resolve_dns_addr (ai->addr, ai->addrlen,
                                 DNS_WITHBRACKET, &tmphost);
    }

  if (tmperr)
    {
      log_info ("resolve_dns_addr failed while checking '%s': %s\n",
                name, gpg_strerror (tmperr));
    }
  else if (host->pool_len + 1 >= MAX_POOL_SIZE)
    {
      log_error ("resolve_dns_addr for '%s': '%s'"
                 " [index table full - ignored]\n", name, tmphost);
    }
  else
    {
      if (!is_pool && is_ip_address (name))
        /* Update the original entry.  */
        tmpidx = idx;
      else
        tmpidx = find_hostinfo (tmphost);
      log_info ("resolve_dns_addr for '%s': '%s'%s\n",
                name, tmphost,
                tmpidx == -1? "" : " [already known]");

      if (tmpidx == -1) /* Create a new entry.  */
        tmpidx = create_new_hostinfo (tmphost);

      if (tmpidx == -1)
        {
          log_error ("map_host for '%s' problem: %s - '%s' [ignored]\n",
                     name, strerror (errno), tmphost);
        }
      else  /* Set or update the entry. */
        {
          if (port)
            hosttable[tmpidx]->port[protocol] = port;

          if (ai->family == AF_INET6)
            {
              hosttable[tmpidx]->v6 = 1;
            }
          else if (ai->family == AF_INET)
            {
              hosttable[tmpidx]->v4 = 1;
            }
          else
            BUG ();

          /* If we updated the main entry, we're done.  */
          if (idx == tmpidx)
            goto leave;

          /* If we updated an existing entry, we're done.  */
          for (i = 0; i < host->pool_len; i++)
            if (host->pool[i] == tmpidx)
              goto leave;

          /* Otherwise, we need to add it to the pool.  Check if there
             is space.  */
          if (host->pool_len + 1 > host->pool_size)
            {
              int *new_pool;
              size_t new_size;

              if (host->pool_size == 0)
                new_size = 4;
              else
                new_size = host->pool_size * 2;

              new_pool = xtryrealloc (host->pool,
                                      new_size * sizeof *new_pool);

              if (new_pool == NULL)
                goto leave;

              host->pool = new_pool;
              host->pool_size = new_size;
            }

          /* Finally, add it.  */
          log_assert (host->pool_len < host->pool_size);
          host->pool[host->pool_len++] = tmpidx;
        }
    }
 leave:
  xfree (tmphost);
}
Exemple #3
0
/* Map the host name NAME to the actual to be used host name.  This
 * allows us to manage round robin DNS names.  We use our own strategy
 * to choose one of the hosts.  For example we skip those hosts which
 * failed for some time and we stick to one host for a time
 * independent of DNS retry times.  If FORCE_RESELECT is true a new
 * host is always selected.  If SRVTAG is NULL no service record
 * lookup will be done, if it is set that service name is used.  The
 * selected host is stored as a malloced string at R_HOST; on error
 * NULL is stored.  If we know the port used by the selected host from
 * a service record, a string representation is written to R_PORTSTR,
 * otherwise it is left untouched.  If R_HTTPFLAGS is not NULL it will
 * receive flags which are to be passed to http_open.  If R_HTTPHOST
 * is not NULL a malloced name of the host is stored there; this might
 * be different from R_HOST in case it has been selected from a
 * pool.  */
static gpg_error_t
map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect,
          enum ks_protocol protocol, char **r_host, char *r_portstr,
          unsigned int *r_httpflags, char **r_httphost)
{
  gpg_error_t err = 0;
  hostinfo_t hi;
  int idx;
  dns_addrinfo_t aibuf, ai;
  int is_pool;
  int new_hosts = 0;
  char *cname;

  *r_host = NULL;
  if (r_httpflags)
    *r_httpflags = 0;
  if (r_httphost)
    *r_httphost = NULL;

  /* No hostname means localhost.  */
  if (!name || !*name)
    {
      *r_host = xtrystrdup ("localhost");
      return *r_host? 0 : gpg_error_from_syserror ();
    }

  /* See whether the host is in our table.  */
  idx = find_hostinfo (name);
  if (idx == -1)
    {
      idx = create_new_hostinfo (name);
      if (idx == -1)
        return gpg_error_from_syserror ();
      hi = hosttable[idx];
      hi->onion = is_onion_address (name);
    }
  else
    hi = hosttable[idx];

  is_pool = hi->pool != NULL;

  if (srvtag && !is_ip_address (name)
      && ! hi->onion
      && ! (hi->did_srv_lookup & 1 << protocol))
    {
      struct srventry *srvs;
      unsigned int srvscount;

      /* Check for SRV records.  */
      err = get_dns_srv (name, srvtag, NULL, &srvs, &srvscount);
      if (err)
        {
          if (gpg_err_code (err) == GPG_ERR_ECONNREFUSED)
            tor_not_running_p (ctrl);
          return err;
        }

      if (srvscount > 0)
        {
          int i;
          if (! is_pool)
            is_pool = srvscount > 1;

          for (i = 0; i < srvscount; i++)
            {
              err = resolve_dns_name (srvs[i].target, 0,
                                      AF_UNSPEC, SOCK_STREAM,
                                      &ai, &cname);
              if (err)
                continue;
              dirmngr_tick (ctrl);
              add_host (name, is_pool, ai, protocol, srvs[i].port);
              new_hosts = 1;
            }

          xfree (srvs);
        }

      hi->did_srv_lookup |= 1 << protocol;
    }

  if (! hi->did_a_lookup
      && ! hi->onion)
    {
      /* Find all A records for this entry and put them into the pool
         list - if any.  */
      err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname);
      if (err)
        {
          log_error ("resolving '%s' failed: %s\n", name, gpg_strerror (err));
          err = 0;
        }
      else
        {
          /* First figure out whether this is a pool.  For a pool we
             use a different strategy than for a plain server: We use
             the canonical name of the pool as the virtual host along
             with the IP addresses.  If it is not a pool, we use the
             specified name. */
          if (! is_pool)
            is_pool = arecords_is_pool (aibuf);
          if (is_pool && cname)
            {
              hi->cname = cname;
              cname = NULL;
            }

          for (ai = aibuf; ai; ai = ai->next)
            {
              if (ai->family != AF_INET && ai->family != AF_INET6)
                continue;
              if (opt.disable_ipv4 && ai->family == AF_INET)
                continue;
              if (opt.disable_ipv6 && ai->family == AF_INET6)
                continue;
              dirmngr_tick (ctrl);

              add_host (name, is_pool, ai, 0, 0);
              new_hosts = 1;
            }

          hi->did_a_lookup = 1;
        }
      xfree (cname);
      free_dns_addrinfo (aibuf);
    }
  if (new_hosts)
    hostinfo_sort_pool (hi);

  if (hi->pool)
    {
      /* Deal with the pool name before selecting a host. */
      if (r_httphost)
        {
          *r_httphost = xtrystrdup (hi->cname? hi->cname : hi->name);
          if (!*r_httphost)
            return gpg_error_from_syserror ();
        }

      /* If the currently selected host is now marked dead, force a
         re-selection .  */
      if (force_reselect)
        hi->poolidx = -1;
      else if (hi->poolidx >= 0 && hi->poolidx < hosttable_size
               && hosttable[hi->poolidx] && hosttable[hi->poolidx]->dead)
        hi->poolidx = -1;

      /* Select a host if needed.  */
      if (hi->poolidx == -1)
        {
          hi->poolidx = select_random_host (hi);
          if (hi->poolidx == -1)
            {
              log_error ("no alive host found in pool '%s'\n", name);
              if (r_httphost)
                {
                  xfree (*r_httphost);
                  *r_httphost = NULL;
                }
              return gpg_error (GPG_ERR_NO_KEYSERVER);
            }
        }

      assert (hi->poolidx >= 0 && hi->poolidx < hosttable_size);
      hi = hosttable[hi->poolidx];
      assert (hi);
    }
  else if (r_httphost && is_ip_address (hi->name))
    {
      /* This is a numerical IP address and not a pool.  We want to
       * find the canonical name so that it can be used in the HTTP
       * Host header.  Fixme: We should store that name in the
       * hosttable. */
      char *host;

      err = resolve_dns_name (hi->name, 0, 0, SOCK_STREAM, &aibuf, NULL);
      if (!err)
        {
          for (ai = aibuf; ai; ai = ai->next)
            {
              if ((!opt.disable_ipv6 && ai->family == AF_INET6)
                  || (!opt.disable_ipv4 && ai->family == AF_INET))
                {
                  err = resolve_dns_addr (ai->addr, ai->addrlen, 0, &host);
                  if (!err)
                    {
                      /* Okay, we return the first found name.  */
                      *r_httphost = host;
                      break;
                    }
                }
            }
        }
      free_dns_addrinfo (aibuf);
    }

  if (hi->dead)
    {
      log_error ("host '%s' marked as dead\n", hi->name);
      if (r_httphost)
        {
          xfree (*r_httphost);
          *r_httphost = NULL;
        }
      return gpg_error (GPG_ERR_NO_KEYSERVER);
    }

  if (r_httpflags)
    {
      /* If the hosttable does not indicate that a certain host
         supports IPv<N>, we explicit set the corresponding http
         flags.  The reason for this is that a host might be listed in
         a pool as not v6 only but actually support v6 when later
         the name is resolved by our http layer.  */
      if (!hi->v4)
        *r_httpflags |= HTTP_FLAG_IGNORE_IPv4;
      if (!hi->v6)
        *r_httpflags |= HTTP_FLAG_IGNORE_IPv6;

      /* Note that we do not set the HTTP_FLAG_FORCE_TOR for onion
         addresses because the http module detects this itself.  This
         also allows us to use an onion address without Tor mode being
         enabled.  */
    }

  *r_host = xtrystrdup (hi->name);
  if (!*r_host)
    {
      err = gpg_error_from_syserror ();
      if (r_httphost)
        {
          xfree (*r_httphost);
          *r_httphost = NULL;
        }
      return err;
    }
  if (hi->port[protocol])
    snprintf (r_portstr, 6 /* five digits and the sentinel */,
              "%hu", hi->port[protocol]);
  return 0;
}
Exemple #4
0
/* Add the host AI under the NAME into the HOSTTABLE.  If PORT is not
   zero, it specifies which port to use to talk to the host.  If NAME
   specifies a pool (as indicated by IS_POOL), update the given
   reference table accordingly.  */
static void
add_host (const char *name, int is_pool,
          const dns_addrinfo_t ai, unsigned short port,
          int *reftbl, size_t reftblsize, int *refidx)
{
  gpg_error_t tmperr;
  char *tmphost;
  int idx, tmpidx;
  int is_numeric = 0;
  int i;

  idx = find_hostinfo (name);

  if (!is_pool && !is_ip_address (name))
    {
      /* This is a hostname but not a pool.  Use the name
         as given without going through resolve_dns_addr.  */
      tmphost = xtrystrdup (name);
      if (!tmphost)
        tmperr = gpg_error_from_syserror ();
      else
        tmperr = 0;
    }
  else
    {
      tmperr = resolve_dns_addr (ai->addr, ai->addrlen,
                                 DNS_WITHBRACKET, &tmphost);
      if (tmphost && is_ip_address (tmphost))
        is_numeric = 1;
    }

  if (tmperr)
    {
      log_info ("resolve_dns_addr failed while checking '%s': %s\n",
                name, gpg_strerror (tmperr));
    }
  else if ((*refidx) + 1 >= reftblsize)
    {
      log_error ("resolve_dns_addr for '%s': '%s'"
                 " [index table full - ignored]\n", name, tmphost);
    }
  else
    {
      if (!is_pool && is_ip_address (name))
        /* Update the original entry.  */
        tmpidx = idx;
      else
        tmpidx = find_hostinfo (tmphost);
      log_info ("resolve_dns_addr for '%s': '%s'%s\n",
                name, tmphost,
                tmpidx == -1? "" : " [already known]");

      if (tmpidx == -1) /* Create a new entry.  */
        tmpidx = create_new_hostinfo (tmphost);

      if (tmpidx == -1)
        {
          log_error ("map_host for '%s' problem: %s - '%s'"
                     " [ignored]\n",
                     name, strerror (errno), tmphost);
        }
      else  /* Set or update the entry. */
        {
          char *ipaddr = NULL;

          if (port)
            hosttable[tmpidx]->port = port;

          if (!is_numeric)
            {
              xfree (tmphost);
              tmperr = resolve_dns_addr (ai->addr, ai->addrlen,
                                         (DNS_NUMERICHOST
                                          | DNS_WITHBRACKET),
                                         &tmphost);
              if (tmperr)
                log_info ("resolve_dns_addr failed: %s\n",
                          gpg_strerror (tmperr));
              else
                {
                  ipaddr = tmphost;
                  tmphost = NULL;
                }
            }

          if (ai->family == AF_INET6)
            {
              hosttable[tmpidx]->v6 = 1;
              xfree (hosttable[tmpidx]->v6addr);
              hosttable[tmpidx]->v6addr = ipaddr;
            }
          else if (ai->family == AF_INET)
            {
              hosttable[tmpidx]->v4 = 1;
              xfree (hosttable[tmpidx]->v4addr);
              hosttable[tmpidx]->v4addr = ipaddr;
            }
          else
            BUG ();

          for (i=0; i < *refidx; i++)
            if (reftbl[i] == tmpidx)
              break;
          if (!(i < *refidx) && tmpidx != idx)
            reftbl[(*refidx)++] = tmpidx;
        }
    }
  xfree (tmphost);
}