Ejemplo n.º 1
0
static void
do_connectable (const char *arg, gboolean synchronous)
{
  char **parts;
  GSocketConnectable *connectable;
  GSocketAddressEnumerator *enumerator;

  if (strchr (arg, '/'))
    {
      /* service/protocol/domain */
      parts = g_strsplit (arg, "/", 3);
      if (!parts || !parts[2])
	usage ();

      connectable = g_network_service_new (parts[0], parts[1], parts[2]);
    }
  else
    {
      guint16 port;

      parts = g_strsplit (arg, ":", 2);
      if (parts && parts[1])
	{
	  arg = parts[0];
	  port = strtoul (parts[1], NULL, 10);
	}
      else
	port = 0;

      if (g_hostname_is_ip_address (arg))
	{
	  GInetAddress *addr = g_inet_address_new_from_string (arg);
	  GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port);

	  g_object_unref (addr);
	  connectable = G_SOCKET_CONNECTABLE (sockaddr);
	}
      else
        connectable = g_network_address_new (arg, port);
    }

  enumerator = g_socket_connectable_enumerate (connectable);
  g_object_unref (connectable);

  if (synchronous)
    do_sync_connectable (enumerator);
  else
    do_async_connectable (enumerator);
}
Ejemplo n.º 2
0
/* Tries to resolve priv->name as an IP address, possibly including an
 * IPv6 scope id.
 */
static void
maybe_resolve_ip (SoupAddress *addr)
{
	SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
	const char *pct, *ip;
	char *tmp = NULL;
	GSocketConnectable *gaddr;
	GSocketAddressEnumerator *sa_enum;
	GSocketAddress *saddr;

	if (priv->sockaddr || !priv->name)
		return;

	pct = strchr (priv->name, '%');
	if (pct)
		ip = tmp = g_strndup (priv->name, pct - priv->name);
	else
		ip = priv->name;

	if (!g_hostname_is_ip_address (ip)) {
		g_free (tmp);
		return;
	}
	g_free (tmp);

	gaddr = g_network_address_new (priv->name, priv->port);
	if (!gaddr)
		return;

	sa_enum = g_socket_connectable_enumerate (gaddr);
	saddr = g_socket_address_enumerator_next (sa_enum, NULL, NULL);
	if (saddr) {
		priv->n_addrs = 1;
		priv->sockaddr = g_new (struct sockaddr_storage, 1);
		if (!g_socket_address_to_native (saddr, priv->sockaddr,
						 sizeof (struct sockaddr_storage),
						 NULL)) {
			/* can't happen: We know the address format is supported
			 * and the buffer is large enough
			 */
			g_warn_if_reached ();
		}
		g_object_unref (saddr);
	}

	g_object_unref (sa_enum);
	g_object_unref (gaddr);
}
Ejemplo n.º 3
0
/**
 * Reads the entries save in given file and store them in the whitelist.
 */
static void load_entries(HSTSProvider *provider, const char *file)
{
    char **lines, **parts, *host, *line;
    int i, len, partlen;
    gboolean include_sub_domains;
    SoupDate *date;
    HSTSEntry *entry;

    lines = util_get_lines(file);
    if (!(len = g_strv_length(lines))) {
        return;
    }

    for (i = len - 1; i >= 0; i--) {
        line = lines[i];
        /* skip empty or commented lines */
        if (!*line || *line == '#') {
            continue;
        }

        parts   = g_strsplit(line, "\t", 3);
        partlen = g_strv_length(parts);
        if (partlen == 3) {
            host = parts[0];
            if (g_hostname_is_ip_address(host)) {
                continue;
            }
            date = soup_date_new_from_string(parts[1]);
            if (!date) {
                continue;
            }
            include_sub_domains = (*parts[2] == 'y') ? true : false;

            /* built the new entry to add */
            entry = g_slice_new(HSTSEntry);
            entry->expires_at          = soup_date_new_from_string(parts[1]);
            entry->include_sub_domains = include_sub_domains;

            add_host_entry(provider, host, entry);
        } else {
            g_warning("could not parse hsts line '%s'", line);
        }
        g_strfreev(parts);
    }
    g_strfreev(lines);
}
Ejemplo n.º 4
0
char*
ephy_embed_utils_normalize_address (const char *address)
{
	char *effective_address = NULL;
	SoupURI *uri;

	g_return_val_if_fail (address, NULL);

	if (ephy_embed_utils_address_is_existing_absolute_filename (address))
		return g_strconcat ("file://", address, NULL);

	uri = soup_uri_new (address);

	/* FIXME: if we are here we passed through the "should we
	 * auto-search this?" regex in EphyWebView, so we should be a
	 * valid-ish URL. Auto-prepend http:// to anything that is not
	 * one according to soup, because it probably will be
	 * something like "google.com". Special case localhost(:port)
	 * and IP(:port), because SoupURI, correctly, thinks it is a
	 * URI with scheme being localhost/IP and, optionally, path
	 * being the port. Ideally we should check if we have a
	 * handler for the scheme, and since we'll fail for localhost
	 * and IP, we'd fallback to loading it as a domain. */
	if (!uri ||
	    (uri && !g_strcmp0 (uri->scheme, "localhost")) ||
	    (uri && g_hostname_is_ip_address (uri->scheme)))
		effective_address = g_strconcat ("http://", address, NULL);
	else {
		/* Convert about: schemes to ephy-about: in order to
		 * force WebKit to delegate its handling to our
		 * EphyRequestAbout. In general about: schemes are
		 * handled internally by WebKit and mean "empty
		 * document".
		 */
		if (g_str_has_prefix (address, "about:") && !g_str_equal (address, "about:blank")) {
			effective_address = g_strconcat (EPHY_ABOUT_SCHEME, address + strlen ("about"), NULL);
		} else
			effective_address = g_strdup (address);
	}

	if (uri)
		soup_uri_free (uri);

	return effective_address;
}
Ejemplo n.º 5
0
/**
 * Checks if given host is a known https host according to RFC 6797 8.2f
 */
static inline gboolean should_secure_host(HSTSProvider *provider,
    const char *host)
{
    HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE(provider);
    HSTSEntry *entry;
    char *canonical, *p;
    gboolean result = false, is_subdomain = false;

    /* ip is not allowed for hsts */
    if (g_hostname_is_ip_address(host)) {
        return false;
    }

    canonical = g_hostname_to_ascii(host);
    /* don't match empty host */
    if (*canonical) {
        p = canonical;
        /* Try to find the whole congruent matching host in hash table - if
         * not found strip of the first label and try to find a superdomain
         * match. Specified is a from right to left comparison 8.3, but in the
         * end this should be lead to the same result. */
        while (p != NULL) {
            entry = g_hash_table_lookup(priv->whitelist, p);
            if (entry != NULL) {
                /* remove expired entries RFC 6797 8.1.1 */
                if (soup_date_is_past(entry->expires_at)) {
                    remove_host_entry(provider, p);
                } else if(!is_subdomain || entry->include_sub_domains) {
                    result = true;
                    break;
                }
            }

            is_subdomain = true;
            /* test without the first domain part */
            if ((p = strchr(p, '.'))) {
                p++;
            }
        }
    }
    g_free(canonical);

    return result;
}
Ejemplo n.º 6
0
static void process_hsts_header(SoupMessage *msg, gpointer data)
{
    HSTSProvider *provider = (HSTSProvider*)data;
    SoupURI *uri           = soup_message_get_uri(msg);
    SoupMessageHeaders *hdrs;
    const char *header;

    if (!g_hostname_is_ip_address(uri->host)
        && (soup_message_get_flags(msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED)
    ){
        g_object_get(G_OBJECT(msg), SOUP_MESSAGE_RESPONSE_HEADERS, &hdrs, NULL);

        /* TODO according to RFC 6797 8.1 we must only use the first header */
        header = soup_message_headers_get_one(hdrs, HSTS_HEADER_NAME);
        if (header) {
            parse_hsts_header(provider, uri->host, header);
        }
    }
}
Ejemplo n.º 7
0
static void
start_async_lookups (char **argv, int argc)
{
  int i;

  for (i = 0; i < argc; i++)
    {
      if (strchr (argv[i], '/'))
	{
	  /* service/protocol/domain */
	  char **parts = g_strsplit (argv[i], "/", 3);

	  if (!parts || !parts[2])
	    usage ();

	  g_resolver_lookup_service_async (resolver,
					   parts[0], parts[1], parts[2],
					   cancellable,
					   lookup_service_callback, argv[i]);
	}
      else if (g_hostname_is_ip_address (argv[i]))
	{
          GInetAddress *addr = g_inet_address_new_from_string (argv[i]);

	  g_resolver_lookup_by_address_async (resolver, addr, cancellable,
                                              lookup_by_addr_callback, argv[i]);
	  g_object_unref (addr);
	}
      else
	{
	  g_resolver_lookup_by_name_async (resolver, argv[i], cancellable,
                                           lookup_by_name_callback,
                                           argv[i]);
	}

      /* Stress-test the reloading code */
      g_signal_emit_by_name (resolver, "reload");
    }
}
Ejemplo n.º 8
0
static void
lookup_one_sync (const char *arg)
{
  GError *error = NULL;

  if (strchr (arg, '/'))
    {
      GList *targets;
      /* service/protocol/domain */
      char **parts = g_strsplit (arg, "/", 3);

      if (!parts || !parts[2])
	usage ();

      targets = g_resolver_lookup_service (resolver,
                                           parts[0], parts[1], parts[2],
                                           cancellable, &error);
      print_resolved_service (arg, targets, error);
    }
  else if (g_hostname_is_ip_address (arg))
    {
      GInetAddress *addr = g_inet_address_new_from_string (arg);
      char *name;

      name = g_resolver_lookup_by_address (resolver, addr, cancellable, &error);
      print_resolved_name (arg, name, error);
      g_object_unref (addr);
    }
  else
    {
      GList *addresses;

      addresses = g_resolver_lookup_by_name (resolver, arg, cancellable, &error);
      print_resolved_addresses (arg, addresses, error);
    }
}
Ejemplo n.º 9
0
char *resolve_hostip (const char *hostname)
{
	char **pp;
	struct in_addr addr;
	struct hostent *hostp;
	char ip[INET_ADDRSTRLEN];

	if (g_hostname_is_ip_address (hostname))
	{
		return g_strdup (hostname);
	}
	
	hostp = gethostbyname (hostname);
	if (hostp == NULL)
	{
		return NULL;
	}

	pp = hostp->h_addr_list; 
	addr.s_addr = *((unsigned int *)*pp);
	inet_ntop (AF_INET, &addr, ip, sizeof (ip));
	
	return g_strdup (ip);
}
Ejemplo n.º 10
0
static void sevencup_attempt_connection(SevenCupConnection *scon)
{
	gboolean is_proxy = FALSE;
	SevenCupAccount *sa = scon->sa;
	PurpleProxyInfo *proxy_info = NULL;

	if (sa && sa->account && !(scon->method & STEAM_METHOD_SSL))
	{
		proxy_info = purple_proxy_get_setup(sa->account);
		if (purple_proxy_info_get_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL)
			proxy_info = purple_global_proxy_get_info();
		if (purple_proxy_info_get_type(proxy_info) == PURPLE_PROXY_HTTP)
		{
			is_proxy = TRUE;
		}	
	}

#if 0
	/* Connection to attempt retries.  This code doesn't work perfectly, but
	 * remains here for future reference if needed */
	if (time(NULL) - scon->request_time > 5) {
		/* We've continuously tried to remake this connection for a 
		 * bit now.  It isn't happening, sadly.  Time to die. */
		purple_debug_error("7cups", "could not connect after retries\n");
		sevencup_fatal_connection_cb(scon);
		return;
	}

	purple_debug_info("7cups", "making connection attempt\n");

	/* TODO: If we're retrying the connection, consider clearing the cached
	 * DNS value.  This will require some juggling with the hostname param */
	/* TODO/FIXME: This retries almost instantenously, which in some cases
	 * runs at blinding speed.  Slow it down. */
	/* TODO/FIXME: this doesn't retry properly on non-ssl connections */
#endif
	
	sa->conns = g_slist_prepend(sa->conns, scon);

	/*
	 * Do a separate DNS lookup for the given host name and cache it
	 * for next time.
	 *
	 * TODO: It would be better if we did this before we call
	 *       purple_proxy_connect(), so we could re-use the result.
	 *       Or even better: Use persistent HTTP connections for servers
	 *       that we access continually.
	 *
	 * TODO: This cache of the hostname<-->IP address does not respect
	 *       the TTL returned by the DNS server.  We should expire things
	 *       from the cache after some amount of time.
	 */
	if (!is_proxy && !(scon->method & STEAM_METHOD_SSL) && !g_hostname_is_ip_address(scon->hostname))
	{
		/* Don't do this for proxy connections, since proxies do the DNS lookup */
		gchar *host_ip;

		host_ip = g_hash_table_lookup(sa->hostname_ip_cache, scon->hostname);
		if (host_ip != NULL) {
			g_free(scon->hostname);
			scon->hostname = g_strdup(host_ip);
		} else if (sa->account && !sa->account->disconnecting) {
			GSList *host_lookup_list = NULL;
			PurpleDnsQueryData *query;

			host_lookup_list = g_slist_prepend(
					host_lookup_list, g_strdup(scon->hostname));
			host_lookup_list = g_slist_prepend(
					host_lookup_list, sa);

			query = purple_dnsquery_a(
#if PURPLE_VERSION_CHECK(3, 0, 0)
					scon->sa->account,
#endif
					scon->hostname, 80,
					sevencup_host_lookup_cb, host_lookup_list);
			sa->dns_queries = g_slist_prepend(sa->dns_queries, query);
			host_lookup_list = g_slist_append(host_lookup_list, query);
		}
	}

	if (scon->method & STEAM_METHOD_SSL) {
		scon->ssl_conn = purple_ssl_connect(sa->account, scon->hostname,
				443, sevencup_post_or_get_ssl_connect_cb,
				sevencup_ssl_connection_error, scon);
	} else {
		scon->connect_data = purple_proxy_connect(NULL, sa->account,
				scon->hostname, 80, sevencup_post_or_get_connect_cb, scon);
	}
	
	scon->timeout_watcher = purple_timeout_add_seconds(120, sevencup_connection_timedout, scon);

	return;
}
Ejemplo n.º 11
0
static gint
set_connect_msg (guint8      *msg,
		 const gchar *hostname,
		 guint16      port,
		 const char  *username,
		 GError     **error)
{
  GInetAddress *addr;
  guint len = 0;
  gsize addr_len;
  gboolean is_ip;
  const gchar *ip;

  msg[len++] = SOCKS4_VERSION;
  msg[len++] = SOCKS4_CMD_CONNECT;

    {
      guint16 hp = g_htons (port);
      memcpy (msg + len, &hp, 2);
      len += 2;
    }

  is_ip = g_hostname_is_ip_address (hostname);

  if (is_ip)
    ip = hostname;
  else
    ip = "0.0.0.1";
    
  addr = g_inet_address_new_from_string (ip);
  addr_len = g_inet_address_get_native_size (addr);

  if (addr_len != 4)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
		  _("SOCKSv4 does not support IPv6 address '%s'"),
		  ip);
      g_object_unref (addr);
      return -1;
    }

  memcpy (msg + len, g_inet_address_to_bytes (addr), addr_len);
  len += addr_len;

  g_object_unref (addr);

  if (username)
    {
      gsize user_len = strlen (username);

      if (user_len > SOCKS4_MAX_LEN)
	{
	  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
			       _("Username is too long for SOCKSv4 protocol"));
	  return -1;
	}

      memcpy (msg + len, username, user_len);
      len += user_len;
    }

  msg[len++] = '\0';

  if (!is_ip)
    {
      gsize host_len = strlen (hostname);

      if (host_len > SOCKS4_MAX_LEN)
	{
	  g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
		       _("Hostname '%s' is too long for SOCKSv4 protocol"),
		       hostname);
	  return -1;
	}

      memcpy (msg + len, hostname, host_len);
      len += host_len;
      msg[len++] = '\0';
    }

  return len;
}
Ejemplo n.º 12
0
static const char *
soup_tld_get_base_domain_internal (const char *hostname, guint additional_domains, GError **error)
{
	char *prev_domain, *cur_domain, *tld, *next_dot;
	gint add_domains;

	soup_tld_ensure_rules_hash_table ();

	if (g_hostname_is_ip_address (hostname)) {
		g_set_error_literal (error, SOUP_TLD_ERROR,
				     SOUP_TLD_ERROR_IS_IP_ADDRESS,
				     _("Hostname is an IP address"));
		return NULL;
	}

	cur_domain = (char *) hostname;
	tld = cur_domain;
	prev_domain = NULL;
	/* Process matching rules from longest to shortest. Logic
	 * based on Mozilla's implementation of nsEffectiveTLDService.
	 */
	while (TRUE) {
		char *orig_domain;
		gboolean domain_found;
		int *flags;

		/* Valid hostnames neither start with a dot nor have more than one
		 * dot together.
		 */
		if (*cur_domain == '.') {
			g_set_error_literal (error, SOUP_TLD_ERROR,
					     SOUP_TLD_ERROR_INVALID_HOSTNAME,
					     _("Invalid hostname"));
			return NULL;
		}

		next_dot = strchr (cur_domain, '.');
		domain_found = g_hash_table_lookup_extended (rules, cur_domain, (gpointer *) &orig_domain, (gpointer *) &flags);
		/* We compare the keys just to be sure that we haven't hit a collision */
		if (domain_found && !strncmp (orig_domain, cur_domain, strlen (orig_domain))) {
			if (*flags & SOUP_TLD_RULE_MATCH_ALL) {
				/* If we match a *. rule and there were no previous exceptions
				 * nor previous domains then treat it as an exact match.
				 */
				tld = prev_domain ? prev_domain : cur_domain;
				break;
			} else if (*flags == SOUP_TLD_RULE_NORMAL) {
				tld = cur_domain;
				break;
			} else if (*flags & SOUP_TLD_RULE_EXCEPTION) {
				tld = next_dot + 1;
				break;
			}
		}

		/* If we hit the top and haven't matched yet, then it
		 * has no public suffix.
		 */
		if (!next_dot) {
			g_set_error_literal (error, SOUP_TLD_ERROR,
					     SOUP_TLD_ERROR_NO_BASE_DOMAIN,
					     _("Hostname has no base domain"));
			return NULL;
		}

		prev_domain = cur_domain;
		cur_domain = next_dot + 1;
	}

	/* Include the additional number of domains requested. */
	add_domains = additional_domains;
	while (tld != hostname) {
		if (*(--tld) == '.' && (!(add_domains--))) {
			++add_domains;
			++tld;
			break;
		}
	}

	/* If additional_domains > 0 then we haven't found enough additional domains. */
	if (add_domains) {
		g_set_error_literal (error, SOUP_TLD_ERROR,
				     SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS,
				     _("Not enough domains"));
		return NULL;
	}

	return tld;
}
Ejemplo n.º 13
0
void okc_post_or_get(OkCupidAccount *oca, OkCupidMethod method,
		const gchar *host, const gchar *url, const gchar *postdata,
		OkCupidProxyCallbackFunc callback_func, gpointer user_data,
		gboolean keepalive)
{
	GString *request;
	gchar *cookies;
	OkCupidConnection *okconn;
	gchar *real_url;
	gboolean is_proxy = FALSE;
	const gchar* const *languages;
	gchar *language_names;
	PurpleProxyInfo *proxy_info = NULL;
	gchar *proxy_auth;
	gchar *proxy_auth_base64;

	/* TODO: Fix keepalive and use it as much as possible */
	keepalive = FALSE;
	
	if (purple_account_get_bool(oca->account, "force_https", TRUE) && !(method & OKC_METHOD_SSL) &&
			(!host || g_str_equal(host, "www.okcupid.com") || g_str_equal(host, "api.okcupid.com")))
	{
		method |= OKC_METHOD_SSL;
	}

	if (method & OKC_METHOD_SSL)
		host = "www.okcupid.com";
	if (host == NULL && oca && oca->account)
		host = purple_account_get_string(oca->account, "host", "api.okcupid.com");
	if (host == NULL)
		host = "api.okcupid.com";

	if (oca && oca->account && !(method & OKC_METHOD_SSL))
	{
		proxy_info = purple_proxy_get_setup(oca->account);
		if (purple_proxy_info_get_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL)
			proxy_info = purple_global_proxy_get_info();
		if (purple_proxy_info_get_type(proxy_info) == PURPLE_PROXY_HTTP)
		{
			is_proxy = TRUE;
		}	
	}
	if (is_proxy == TRUE)
	{
		real_url = g_strdup_printf("http://%s%s", host, url);
	} else {
		real_url = g_strdup(url);
	}
	
	cookies = okc_cookies_to_string(oca);
	
	if (method & OKC_METHOD_POST && !postdata)
		postdata = "";
	
	/* Build the request */
	request = g_string_new(NULL);
	g_string_append_printf(request, "%s %s HTTP/1.0\r\n",
			(method & OKC_METHOD_POST) ? "POST" : "GET",
			real_url);
	
	if (is_proxy == FALSE)
		g_string_append_printf(request, "Host: %s\r\n", host);
	g_string_append_printf(request, "Connection: %s\r\n",
			(keepalive ? "Keep-Alive" : "close"));
	g_string_append_printf(request, "User-Agent: %s (libpurple %s)\r\n", purple_core_get_ui(), purple_core_get_version());
	if (method & OKC_METHOD_POST) {
		g_string_append_printf(request,
				"Content-Type: application/x-www-form-urlencoded\r\n");
		g_string_append_printf(request,
				"Content-length: %zu\r\n", strlen(postdata));
	}
	g_string_append_printf(request, "Accept: */*\r\n");
	g_string_append_printf(request, "Cookie: %s\r\n", cookies);
	g_string_append_printf(request, "Accept-Encoding: gzip\r\n");
	g_string_append_printf(request, "X-OkCupid-Api-Version: 1\r\n");
	
	if (is_proxy == TRUE)
	{
		if (purple_proxy_info_get_username(proxy_info) &&
			purple_proxy_info_get_password(proxy_info))
		{
			proxy_auth = g_strdup_printf("%s:%s", purple_proxy_info_get_username(proxy_info), purple_proxy_info_get_password(proxy_info));
			proxy_auth_base64 = purple_base64_encode((guchar *)proxy_auth, strlen(proxy_auth));
			g_string_append_printf(request, "Proxy-Authorization: Basic %s\r\n", proxy_auth_base64);
			g_free(proxy_auth_base64);
			g_free(proxy_auth);
		}
	}
	/* Tell the server what language we accept, so that we get error messages in our language (rather than our IP's) */
	languages = g_get_language_names();
	language_names = g_strjoinv(", ", (gchar **)languages);
	purple_util_chrreplace(language_names, '_', '-');
	g_string_append_printf(request, "Accept-Language: %s\r\n", language_names);
	g_free(language_names);

	purple_debug_misc("okcupid", "requesting url %s\n", url);

	g_string_append_printf(request, "\r\n");
	if (method & OKC_METHOD_POST)
		g_string_append_printf(request, "%s", postdata);

	/* If it needs to go over a SSL connection, we probably shouldn't print
	 * it in the debug log.  Without this condition a user's password is
	 * printed in the debug log */
	if (method == OKC_METHOD_POST)
		purple_debug_misc("okcupid", "sending request data:\n%s\n",
			postdata);

	g_free(cookies);
	g_free(real_url);
	/*
	 * Do a separate DNS lookup for the given host name and cache it
	 * for next time.
	 *
	 * TODO: It would be better if we did this before we call
	 *       purple_proxy_connect(), so we could re-use the result.
	 *       Or even better: Use persistent HTTP connections for servers
	 *       that we access continually.
	 *
	 * TODO: This cache of the hostname<-->IP address does not respect
	 *       the TTL returned by the DNS server.  We should expire things
	 *       from the cache after some amount of time.
	 */
	if (!is_proxy && !g_hostname_is_ip_address(host) && !(method & OKC_METHOD_SSL))
	{
		/* Don't do this for proxy connections, since proxies do the DNS lookup */
		gchar *host_ip;

		host_ip = g_hash_table_lookup(oca->hostname_ip_cache, host);
		if (host_ip != NULL) {
			purple_debug_info("okcupid",
					"swapping original host %s with cached value of %s\n",
					host, host_ip);
			host = host_ip;
		} else if (oca->account && !oca->account->disconnecting) {
			GSList *host_lookup_list = NULL;
			PurpleDnsQueryData *query;

			host_lookup_list = g_slist_prepend(
					host_lookup_list, g_strdup(host));
			host_lookup_list = g_slist_prepend(
					host_lookup_list, oca);

			query = purple_dnsquery_a(host, 80,
					okc_host_lookup_cb, host_lookup_list);
			oca->dns_queries = g_slist_prepend(oca->dns_queries, query);
			host_lookup_list = g_slist_append(host_lookup_list, query);
		}
	}

	okconn = g_new0(OkCupidConnection, 1);
	okconn->oca = oca;
	okconn->method = method;
	okconn->hostname = g_strdup(host);
	okconn->request = request;
	okconn->callback = callback_func;
	okconn->user_data = user_data;
	okconn->fd = -1;
	okconn->connection_keepalive = keepalive;
	okconn->request_time = time(NULL);

	g_queue_push_head(oca->waiting_conns, okconn);
	okc_next_connection(oca);
}