Hi all,

If you take multi-app.c example:
http://curl.haxx.se/libcurl/c/multi-app.html
and use it to connect to a server that is using DNS load balancing (has
several IPs for one hostname), such as google.com, it seems all connections
will be established with the same IP address.

A slightly modified multi-app.c code is attached that shows this behaviour:
./a.out |sort |uniq -c
      1 ares enabled
      1 C-ares: 1.10.1-20141210
      1 Curl version: 7.39.0
    100 Transfer done to IP: 173.194.40.67

If the program is run for a second time, then (usually) another IP is being
used. Several DNS queries are also sent and received but still all the easy
handles use the same IP. This happens with all of the DNS related settings
mentioned in
http://git.io/libcurl-dns
and even if assigning separate share handles (CURLOPT_SHARE) for each easy
handle.

The only way I could get the DNS load balancing to work was by resolving
all the IPs first and then assigning them manually using CURLOPT_RESOLVE.
This is not that nice so is there perhaps some way to disable DNS caching
between curl handles that I did not notice? DNS load balancing is quite
common so this should work quite easily.

Cheers,
Seppo
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2011, Daniel Stenberg, <dan...@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at http://curl.haxx.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ***************************************************************************/
/* This is an example application source code using the multi interface
 * and testing access to host that is using DNS load balancing.
 *
 * Compile:
 * g++ -lcurl -lcares multi-app.c
 *
 * Usage:
 * ./a.out | sort | uniq -c
 */

#include <stdio.h>
#include <string.h>

/* somewhat unix-specific */
#include <sys/time.h>
#include <unistd.h>

/* curl stuff */
#include <curl/curl.h>
#include <ares_version.h>
#include <ares.h>

#define HANDLECOUNT 100   /* Number of simultaneous transfers */

int main(void) {
  CURL   *handles[HANDLECOUNT];
  CURLSH *sharehandles[HANDLECOUNT];
  CURLM  *multi_handle;

  int still_running; /* keep number of running handles */
  int i;

  curl_global_init(CURL_GLOBAL_ALL);

  // Print version info
  curl_version_info_data *d = curl_version_info(CURLVERSION_NOW);
  printf("Curl version: %s\n", d->version);
  if (d->features & CURL_VERSION_ASYNCHDNS) {
    printf("ares enabled\n");
  } else {
    printf("ares NOT enabled\n");
  }
  printf("C-ares: %s\n", ares_version(NULL));

  CURLMsg *msg;   /* for picking up messages with the transfer status */
  int msgs_left;  /* how many messages are left */

  /* init a multi stack */
  multi_handle = curl_multi_init();

  /* Allocate one CURL handle per transfer */
  for (i = 0; i < HANDLECOUNT; i++) {
    handles[i] = curl_easy_init();
  }

#define USE_SEPARATE_SHARE_HANDLES
#ifdef USE_SEPARATE_SHARE_HANDLES
  for (i = 0; i < HANDLECOUNT; i++) {
    sharehandles[i] = curl_share_init();
    curl_share_setopt(sharehandles[i], CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
    curl_easy_setopt(handles[i], CURLOPT_SHARE, sharehandles[0]);
  }
#endif

  for (i = 0; i < HANDLECOUNT; i++) {
    curl_easy_setopt(handles[i], CURLOPT_UPLOAD, 0);
    curl_easy_setopt(handles[i], CURLOPT_NOBODY, 1);
    curl_easy_setopt(handles[i], CURLOPT_NOSIGNAL, 1L);
    //curl_easy_setopt(handles[i], CURLOPT_DNS_CACHE_TIMEOUT, 0);
    //curl_easy_setopt(handles[i], CURLOPT_DNS_USE_GLOBAL_CACHE, 0);
    //curl_easy_setopt(handles[i], CURLOPT_FORBID_REUSE, 1L);
    //curl_easy_setopt(handles[i], CURLOPT_VERBOSE, 1L);
    curl_easy_setopt(handles[i], CURLOPT_URL,
                     "http://google.com/";);
  }

  /* add the individual transfers */
  for (i = 0; i < HANDLECOUNT; i++) {
    if (i == 1) {
      // With libcurl 7.37.0 this was needed for DNS balancing not to work
      //curl_multi_perform(multi_handle, &still_running);
      //sleep(1);
    }
    curl_multi_add_handle(multi_handle, handles[i]);
  }

  curl_multi_perform(multi_handle, &still_running);

  do {
    struct timeval timeout;
    int rc; /* select() return code */

    fd_set fdread;
    fd_set fdwrite;
    fd_set fdexcep;
    int maxfd = -1;

    long curl_timeo = -1;

    FD_ZERO(&fdread);
    FD_ZERO(&fdwrite);
    FD_ZERO(&fdexcep);

    /* set a suitable timeout to play around with */
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;

    curl_multi_timeout(multi_handle, &curl_timeo);
    if (curl_timeo >= 0) {
      timeout.tv_sec = curl_timeo / 1000;
      if (timeout.tv_sec > 1)
        timeout.tv_sec = 1;
      else
        timeout.tv_usec = (curl_timeo % 1000) * 1000;
    }

    /* get file descriptors from the transfers */
    curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);

    /* In a real-world program you OF COURSE check the return code of the
       function calls.  On success, the value of maxfd is guaranteed to be
       greater or equal than -1.  We call select(maxfd + 1, ...), specially in
       case of (maxfd == -1), we call select(0, ...), which is basically equal
       to sleep. */
    rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);

    switch (rc) {
    case -1:
      /* select error */
      break;
    case 0: /* timeout */
    default: /* action */
      curl_multi_perform(multi_handle, &still_running);
      break;
    }
  } while (still_running);

  /* See how the transfers went */
  while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
    if (msg->msg == CURLMSG_DONE) {
      int idx, found = 0;

      /* Find out which handle this message is about */
      for (idx = 0; idx < HANDLECOUNT; idx++) {
        found = (msg->easy_handle == handles[idx]);
        if (found)
          break;
      }

      if (found) {
        char *server_ip;
        curl_easy_getinfo(handles[idx], CURLINFO_PRIMARY_IP, &server_ip);
        printf("Transfer done to IP: %s\n", server_ip);
      }
    }
  }

  curl_multi_cleanup(multi_handle);

  /* Free the CURL handles */
  for (i = 0; i < HANDLECOUNT; i++) {
    curl_easy_cleanup(handles[i]);
  }

#ifdef USE_SEPARATE_SHARE_HANDLES
  /* Free share handles */
  for (i = 0; i < HANDLECOUNT; i++) {
    curl_share_cleanup(sharehandles[i]);
  }
#endif

  return 0;
}
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html

Reply via email to