Commit 50d1b337 authored by Anders Bakken's avatar Anders Bakken Committed by Jay Satiro
Browse files

CURLOPT_RESOLVE: Add support for multiple IP addresses per entry

This enables users to preresolve but still take advantage of happy
eyeballs and trying multiple addresses if some are not connecting.

Ref: https://github.com/curl/curl/pull/2260
parent e77f0e5a
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
Long: resolve
Arg: <host:port:address>
Arg: <host:port:address[,address]...>
Help: Resolve the host+port to this address
Added: 7.21.3
---
@@ -16,4 +16,6 @@ is set to make curl use another IP version.

Support for providing the IP address within [brackets] was added in 7.57.0.

Support for providing multiple IP addresses per entry was added in 7.59.0.

This option can be used many times to add many host names to resolve.
+8 −4
Original line number Diff line number Diff line
@@ -37,10 +37,12 @@ list of \fBstruct curl_slist\fP structs properly filled in. Use
to clean up an entire list.

Each single name resolve string should be written using the format
HOST:PORT:ADDRESS where HOST is the name libcurl will try to resolve, PORT is
the port number of the service where libcurl wants to connect to the HOST and
ADDRESS is the numerical IP address. If libcurl is built to support IPv6,
ADDRESS can of course be either IPv4 or IPv6 style addressing.
HOST:PORT:ADDRESS[,ADDRESS]... where HOST is the name libcurl will try
to resolve, PORT is the port number of the service where libcurl wants
to connect to the HOST and ADDRESS is one or more numerical IP
addresses. If you specify multiple ip addresses they need to be
separated by comma. If libcurl is built to support IPv6, each of the
ADDRESS entries can of course be either IPv4 or IPv6 style addressing.

This option effectively pre-populates the DNS cache with entries for the
host+port pair so redirects and everything that operations against the
@@ -57,6 +59,8 @@ by including a string in the linked list that uses the format
and port number must exactly match what was already added previously.

Support for providing the ADDRESS within [brackets] was added in 7.57.0.

Support for providing multiple IP addresses per entry was added in 7.59.0.
.SH DEFAULT
NULL
.SH PROTOCOLS
+8 −8
Original line number Diff line number Diff line
@@ -619,7 +619,7 @@ void Curl_persistconninfo(struct connectdata *conn)

/* retrieves ip address and port from a sockaddr structure.
   note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
static bool getaddressinfo(struct sockaddr *sa, char *addr,
bool Curl_getaddressinfo(struct sockaddr *sa, char *addr,
                         long *port)
{
  unsigned short us_port;
@@ -700,7 +700,7 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
      return;
    }

    if(!getaddressinfo((struct sockaddr*)&ssrem,
    if(!Curl_getaddressinfo((struct sockaddr*)&ssrem,
                            conn->primary_ip, &conn->primary_port)) {
      failf(data, "ssrem inet_ntop() failed with errno %d: %s",
            errno, Curl_strerror(conn, errno));
@@ -708,7 +708,7 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
    }
    memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN);

    if(!getaddressinfo((struct sockaddr*)&ssloc,
    if(!Curl_getaddressinfo((struct sockaddr*)&ssloc,
                            conn->local_ip, &conn->local_port)) {
      failf(data, "ssloc inet_ntop() failed with errno %d: %s",
            errno, Curl_strerror(conn, errno));
@@ -1005,7 +1005,7 @@ static CURLcode singleipconnect(struct connectdata *conn,
    return CURLE_OK;

  /* store remote address and port used in this connection attempt */
  if(!getaddressinfo((struct sockaddr*)&addr.sa_addr,
  if(!Curl_getaddressinfo((struct sockaddr*)&addr.sa_addr,
                          ipaddress, &port)) {
    /* malformed address or bug in inet_ntop, try next address */
    failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
+5 −0
Original line number Diff line number Diff line
@@ -77,6 +77,11 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd);
void Curl_persistconninfo(struct connectdata *conn);
int Curl_closesocket(struct connectdata *conn, curl_socket_t sock);

/*
 * Get presentation format IP address and port from a sockaddr.
 */
bool Curl_getaddressinfo(struct sockaddr *sa, char *addr, long *port);

/*
 * The Curl_sockaddr_ex structure is basically libcurl's external API
 * curl_sockaddr structure with enough space available to directly hold any
+89 −27
Original line number Diff line number Diff line
@@ -781,7 +781,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
{
  struct curl_slist *hostp;
  char hostname[256];
  int port;
  int port = 0;

  for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
    if(!hostp->data)
@@ -819,32 +819,95 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
    }
    else {
      struct Curl_dns_entry *dns;
      Curl_addrinfo *addr;
      Curl_addrinfo *head = NULL, *tail = NULL;
      char *entry_id;
      size_t entry_len;
      char buffer[256];
      char *address = &buffer[0];
      char address[64];
      char *addresses;
      char *addr_begin;
      char *addr_end;
      char *port_ptr;
      char *end_ptr;
      char *host_end;
      unsigned long tmp_port;
      bool error = true;

      host_end = strchr(hostp->data, ':');
      if(!host_end ||
         ((host_end - hostp->data) >= (ptrdiff_t)sizeof(hostname)))
        goto err;

      memcpy(hostname, hostp->data, host_end - hostp->data);
      hostname[host_end - hostp->data] = '\0';

      port_ptr = host_end + 1;
      tmp_port = strtoul(port_ptr, &end_ptr, 10);
      if(end_ptr == port_ptr || tmp_port > USHRT_MAX || *end_ptr != ':')
        goto err;

      port = (int)tmp_port;
      addresses = end_ptr + 1;

      while(*end_ptr) {
        size_t alen;
        Curl_addrinfo *ai;

        addr_begin = end_ptr + 1;
        addr_end = strchr(addr_begin, ',');
        if(!addr_end)
          addr_end = addr_begin + strlen(addr_begin);
        end_ptr = addr_end;

      if(3 != sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
                     address)) {
        infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
              hostp->data);
        continue;
        /* allow IP(v6) address within [brackets] */
        if(*addr_begin == '[') {
          if(addr_end == addr_begin || *(addr_end - 1) != ']')
            goto err;
          ++addr_begin;
          --addr_end;
        }

      /* allow IP(v6) address within [brackets] */
      if(address[0] == '[') {
        size_t alen = strlen(address);
        if(address[alen-1] != ']')
          /* it needs to also end with ] to be valid */
        alen = addr_end - addr_begin;
        if(!alen)
          continue;

        if(alen >= sizeof(address))
          goto err;

        memcpy(address, addr_begin, alen);
        address[alen] = '\0';

#ifndef ENABLE_IPV6
        if(strchr(address, ':')) {
          infof(data, "Ignoring resolve address '%s', missing IPv6 support.\n",
                address);
          continue;
        address[alen-1] = 0; /* zero terminate there */
        address++; /* pass the open bracket */
        }
#endif

      addr = Curl_str2addr(address, port);
      if(!addr) {
        infof(data, "Address in '%s' found illegal!\n", hostp->data);
        ai = Curl_str2addr(address, port);
        if(!ai) {
          infof(data, "Resolve address '%s' found illegal!\n", address);
          goto err;
        }

        if(tail) {
          tail->ai_next = ai;
          tail = tail->ai_next;
        }
        else {
          head = tail = ai;
        }
      }

      if(!head)
        goto err;

      error = false;
   err:
      if(error) {
        infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
              hostp->data);
        Curl_freeaddrinfo(head);
        continue;
      }

@@ -852,10 +915,9 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
      entry_id = create_hostcache_id(hostname, port);
      /* If we can't create the entry id, fail */
      if(!entry_id) {
        Curl_freeaddrinfo(addr);
        Curl_freeaddrinfo(head);
        return CURLE_OUT_OF_MEMORY;
      }

      entry_len = strlen(entry_id);

      if(data->share)
@@ -869,7 +931,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)

      if(!dns) {
        /* if not in the cache already, put this host in the cache */
        dns = Curl_cache_addr(data, addr, hostname, port);
        dns = Curl_cache_addr(data, head, hostname, port);
        if(dns) {
          dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
          /* release the returned reference; the cache itself will keep the
@@ -880,19 +942,19 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
      else {
        /* this is a duplicate, free it again */
        infof(data, "RESOLVE %s:%d is already cached, %s not stored!\n",
              hostname, port, address);
        Curl_freeaddrinfo(addr);
              hostname, port, addresses);
        Curl_freeaddrinfo(head);
      }

      if(data->share)
        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);

      if(!dns) {
        Curl_freeaddrinfo(addr);
        Curl_freeaddrinfo(head);
        return CURLE_OUT_OF_MEMORY;
      }
      infof(data, "Added %s:%d:%s to DNS cache\n",
            hostname, port, address);
            hostname, port, addresses);
    }
  }
  data->change.resolve = NULL; /* dealt with now */
Loading