Date: 3 Nov 2005
Author: Daniel Stenberg
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("%sn", 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 returnedn", numips); for(i=0; i<numips; i++) { printf("IP index %dn", 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 forstn"); 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) failedn"); 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; }