connect_server (const char *server, unsigned short port,
                unsigned int flags, const char *srvtag, unsigned int timeout,
                assuan_fd_t *r_sock)
{
  gpg_error_t err;
  assuan_fd_t sock = ASSUAN_INVALID_FD;
  unsigned int srvcount = 0;
  int hostfound = 0;
  int anyhostaddr = 0;
  int srv, connected;
  gpg_error_t last_err = 0;
  struct srventry *serverlist = NULL;

  *r_sock = ASSUAN_INVALID_FD;

#if defined(HAVE_W32_SYSTEM) && !defined(HTTP_NO_WSASTARTUP)
  init_sockets ();
#endif /*Windows*/

  /* Onion addresses require special treatment.  */
  if (is_onion_address (server))
    {
#ifdef ASSUAN_SOCK_TOR

      if (opt_debug)
        log_debug ("http.c:connect_server:onion: name='%s' port=%hu\n",
                   server, port);
      sock = assuan_sock_connect_byname (server, port, 0, NULL,
                                         ASSUAN_SOCK_TOR);
      if (sock == ASSUAN_INVALID_FD)
        {
          err = gpg_err_make (default_errsource,
                              (errno == EHOSTUNREACH)? GPG_ERR_UNKNOWN_HOST
                              : gpg_err_code_from_syserror ());
          log_error ("can't connect to '%s': %s\n", server, gpg_strerror (err));
          return err;
        }

      notify_netactivity ();
      *r_sock = sock;
      return 0;

#else /*!ASSUAN_SOCK_TOR*/

      err = gpg_err_make (default_errsource, GPG_ERR_ENETUNREACH);
      return ASSUAN_INVALID_FD;

#endif /*!HASSUAN_SOCK_TOR*/
    }

  /* Do the SRV thing */
  if (srvtag)
    {
      err = get_dns_srv (server, srvtag, NULL, &serverlist, &srvcount);
      if (err)
        log_info ("getting '%s' SRV for '%s' failed: %s\n",
                  srvtag, server, gpg_strerror (err));
      /* Note that on error SRVCOUNT is zero.  */
      err = 0;
    }

  if (!serverlist)
    {
      /* Either we're not using SRV, or the SRV lookup failed.  Make
	 up a fake SRV record. */
      serverlist = xtrycalloc (1, sizeof *serverlist);
      if (!serverlist)
        return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());

      serverlist->port = port;
      strncpy (serverlist->target, server, DIMof (struct srventry, target));
      serverlist->target[DIMof (struct srventry, target)-1] = '\0';
      srvcount = 1;
    }

  connected = 0;
  for (srv=0; srv < srvcount && !connected; srv++)
    {
      dns_addrinfo_t aibuf, ai;

      if (opt_debug)
        log_debug ("http.c:connect_server: trying name='%s' port=%hu\n",
                   serverlist[srv].target, port);
      err = resolve_dns_name (serverlist[srv].target, port, 0, SOCK_STREAM,
                              &aibuf, NULL);
      if (err)
        {
          log_info ("resolving '%s' failed: %s\n",
                    serverlist[srv].target, gpg_strerror (err));
          last_err = err;
          continue; /* Not found - try next one. */
        }
      hostfound = 1;

      for (ai = aibuf; ai && !connected; ai = ai->next)
        {
          if (ai->family == AF_INET && (flags & HTTP_FLAG_IGNORE_IPv4))
            continue;
          if (ai->family == AF_INET6 && (flags & HTTP_FLAG_IGNORE_IPv6))
            continue;

          if (sock != ASSUAN_INVALID_FD)
            assuan_sock_close (sock);
          sock = my_sock_new_for_addr (ai->addr, ai->socktype, ai->protocol);
          if (sock == ASSUAN_INVALID_FD)
            {
              err = gpg_err_make (default_errsource,
                                  gpg_err_code_from_syserror ());
              log_error ("error creating socket: %s\n", gpg_strerror (err));
              free_dns_addrinfo (aibuf);
              xfree (serverlist);
              return err;
            }

          anyhostaddr = 1;
          err = connect_with_timeout (sock, (struct sockaddr *)ai->addr,
                                      ai->addrlen, timeout);
          if (err)
            {
              last_err = err;
            }
          else
            {
              connected = 1;
              notify_netactivity ();
            }
        }
      free_dns_addrinfo (aibuf);
    }

  xfree (serverlist);

  if (!connected)
    {
      if (!hostfound)
        log_error ("can't connect to '%s': %s\n",
                   server, "host not found");
      else if (!anyhostaddr)
        log_error ("can't connect to '%s': %s\n",
                   server, "no IP address for host");
      else
        {
#ifdef HAVE_W32_SYSTEM
        log_error ("can't connect to '%s': ec=%d\n",
                   server, (int)WSAGetLastError());
#else
        log_error ("can't connect to '%s': %s\n",
                   server, gpg_strerror (last_err));
#endif
        }
      err = last_err? last_err : gpg_err_make (default_errsource,
                                               GPG_ERR_UNKNOWN_HOST);
      if (sock != ASSUAN_INVALID_FD)
	assuan_sock_close (sock);
      return err;
    }

  *r_sock = sock;
  return 0;
}