DNS round robin resolving tests/questions
Daniel Stenberg
daniel@haxx.se
Sun Nov 6 18:12:00 GMT 2005
Hi glibc fellows!
I'm resolving host names with glibc 2.3.5 on Linux x86 (Debian unstable),
using getaddrinfo() and gethostbyname(). The host names are "round robin"
types in DNS, they resolve to a set of different IP addresses.
I'm experiencing differences in how the functions return the addresses, and
I'm posting here to ask for guidance. Am I doing it wrong? Should I do
something special to make getaddrinfo() return the addresses in a more
"random" fashion?
Attached to this mail are two files:
roundrobin.c - a stand alone test program for testing round robin DNS
resolves (also at http://daniel.haxx.se/roundrobin.c)
bad11-2000 - the output I got from running the test program with the
arguments "bad11.haxx.s 2000". It makes the test program resolve that host
name 2000 times and then check how the response addressess are distributed.
(also at http://daniel.haxx.se/bad11-2000)
The bad11-2000 output clearly shows that gethostbyname() returns a more random
list of addresses on my machine. Unfortunately, most of my getaddrinfo()-
capable Linux hosts are all running Debian unstable so I can basically only
test this on the same glibc. And I cannot detect any noticable differences
between the hosts (they all use different /etc/resolv.conf files and different
resolving servers for example).
Can anyone detect an obvious flaw in the test method or give a resonable
explanation why this result could be considered OK?
Is there anything we mere mortals can do to improve the getaddrinfo() output
without modifying glibc? I do realize I can "scramble" the resulting list
myself, but I would find that awkward.
I'm not an expert on this, please point out my mistakes!
--
-=- Daniel Stenberg -=- http://daniel.haxx.se -=-
ech`echo xiun|tr nu oc|sed 'sx\([sx]\)\([xoi]\)xo un\2\1 is xg'`ol
-------------- next part --------------
/*****************************************************************************
*
* Date: 3 Nov 2005
* Author: Daniel Stenberg <daniel@haxx.se>
* License: freely available to do whatever you want with.
*
* roundrobin.c - test the round robin DNS features of the resolver functions
*
* Note: this test script is written to be compiled and run on Linux. It is
* not as portable as it could be, but that is just to make it a simpler test
* case.
*
* "gcc roundrobin.c"
*
* This source snippet resolves a name with multiple IP addresses and prints
* them out in the order the addresses were returned by the resolving
* function. It first uses getaddrinfo() (called GAI) and then gethostbyname()
* (called GHBN).
*
* On my three test machines they both show the same sympthoms:
*
* The GAI list is a lot less "random" than the GHBN one. The GAI list almost
* always returns the same first entry on repeated invokes (while the
* subsequent entries comes somewhat more random). The GHBN list is returned
* in a much more random fashion.
*
* The test machines are all running Debian Unstable glibc 2.3.5
*
* What this program does:
*
* It runs N resolves of a given host names. It stores the order it gets the
* returned addresses. When all N resolves are done, it checks how the
* returned addresses were distributed. The procedure is first done with GAI
* and then with GHBN. The output is presented in list index order. That
* means: 'index 0' is the first address in the returned list and 'index 1' is
* the second address and so on. We have found out that in the GAI case you
* very often get 100% of the same address in index 0.
*
* We have three hosts names that resolves to multiple IP addresses:
*
* bad2.haxx.se
* bad10.haxx.se
* bad11.haxx.se
*
* As you will see, none of them resolves any sensible data for other purposes
* than resolve tests or similar.
*
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
static const char *printable_address(const struct addrinfo *ip,
char *buf, size_t bufsize)
{
const void *ip4 = &((const struct sockaddr_in*)ip->ai_addr)->sin_addr;
int af = ip->ai_family;
const void *ip6 = NULL;
return inet_ntop(af, af == AF_INET ? ip4 : ip6, buf, bufsize);
}
/* maximum number of test rounds we keep data for */
#define MAX_TESTS 2000
/* maximum number of ips returned for a name */
#define MAX_IPS 10
long counters[MAX_IPS][MAX_TESTS];
int lap;
static void dump_addrinfo(const char *str,
const struct addrinfo *ai,
int *numips)
{
int addr=0;
for ( ; ai; ai = ai->ai_next, addr++) {
char buf[INET6_ADDRSTRLEN];
#if 0
/* output the address */
printf("%s fam %2d, CNAME %s, ", str,
ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>");
#endif
if (printable_address(ai, buf, sizeof(buf))) {
#if 0
/* show the "printable" format version of the address */
printf("%s\n", buf);
#endif
counters[addr][lap] = inet_addr(buf);
}
}
*numips = addr;
}
static struct addrinfo *he2ai(struct hostent *he)
{
struct addrinfo *ai;
struct addrinfo *prevai = NULL;
struct addrinfo *firstai = NULL;
struct sockaddr_in *addr;
int i;
struct in_addr *curr;
for(i=0; (curr = (struct in_addr *)he->h_addr_list[i]); i++) {
ai = calloc(1, sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
if(!ai)
break;
if(!firstai)
firstai = ai;
if(prevai)
prevai->ai_next = ai;
ai->ai_family = AF_INET;
ai->ai_socktype = SOCK_STREAM;
ai->ai_addrlen = sizeof(struct sockaddr_in);
ai->ai_addr = (struct sockaddr *) ((char*)ai + sizeof(struct addrinfo));
addr = (struct sockaddr_in *)ai->ai_addr; /* storage area for this info */
memcpy((char *)&(addr->sin_addr), curr, sizeof(struct in_addr));
addr->sin_family = he->h_addrtype;
addr->sin_port = htons((unsigned short)80);
prevai = ai;
}
return firstai;
}
int sumip[MAX_IPS];
int sum[MAX_IPS];
static report(int numips, int numtests)
{
int i, j;
printf("--- %d different IPs were returned\n", numips);
for(i=0; i<numips; i++) {
printf("IP index %d\n", i);
memset(sum, 0, sizeof(sum));
for(lap=0; lap< numtests; lap++) {
#if 0
/* show each address for each test lap */
printf(" %x", counters[i][lap]);
#endif
for(j=0; j<numips; j++) {
if(sumip[j] == counters[i][lap])
sum[j]++;
}
}
#if 0
/* end of line of the previous output */
printf("\n ");
#endif
for(j=0; j<numips; j++) {
if(sum[j])
printf(" %x %d times (%d%%)\n", sumip[j], sum[j], sum[j]*100/numtests);
}
printf("\n");
}
}
#define HOSTNAME argv[1]
int main(int argc, char **argv)
{
struct addrinfo hints, *res;
int error;
struct hostent *h;
int numtests = 5;
int numips;
int i, j;
if(argc<2) {
printf("roundrobin [host] [number of resolves]\n");
return 2;
}
if(argc>2) {
numtests = atoi(argv[2]);
if(numtests > MAX_TESTS) {
printf("too many, increase MAX_TESTS forst\n");
return 1;
}
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
/* hints.ai_flags = AI_CANONNAME makes no apparent difference */
printf("---Check %d runs of getaddrinfo(%s)\n", numtests, HOSTNAME);
for(lap=0; lap< numtests; lap++) {
error = getaddrinfo(HOSTNAME, "80", &hints, &res);
if (error) {
printf("getaddrinfo(3) failed\n");
return 1;
}
else {
dump_addrinfo("getaddrinfo", res, &numips);
}
freeaddrinfo(res);
}
for(i=0; i<numips; i++)
/* get each IP address binary blob */
sumip[i]= counters[i][0];
report(numips, numtests);
printf("---Check %d runs of gethostbyname(%s)\n", numtests, HOSTNAME);
for(lap=0; lap< numtests; lap++) {
h = gethostbyname(HOSTNAME);
if(h) {
res = he2ai(h);
dump_addrinfo("gethostbyname", res, &numips);
}
}
report(numips, numtests);
return 0;
}
-------------- next part --------------
---Check 2000 runs of getaddrinfo(bad11.haxx.se)
--- 4 different IPs were returned
IP index 0
4040404 2000 times (100%)
IP index 1
2020202 2000 times (100%)
IP index 2
1010101 977 times (48%)
3030303 1023 times (51%)
IP index 3
1010101 1023 times (51%)
3030303 977 times (48%)
---Check 2000 runs of gethostbyname(bad11.haxx.se)
--- 4 different IPs were returned
IP index 0
4040404 486 times (24%)
2020202 476 times (23%)
1010101 518 times (25%)
3030303 520 times (26%)
IP index 1
4040404 520 times (26%)
2020202 518 times (25%)
1010101 486 times (24%)
3030303 476 times (23%)
IP index 2
4040404 476 times (23%)
2020202 486 times (24%)
1010101 520 times (26%)
3030303 518 times (25%)
IP index 3
4040404 518 times (25%)
2020202 520 times (26%)
1010101 476 times (23%)
3030303 486 times (24%)
More information about the Libc-alpha
mailing list