From 5de85b922aaa5bfa6eb53db6000adf01ebdb0736 Mon Sep 17 00:00:00 2001 From: James M Leddy Date: Tue, 23 Aug 2011 17:02:03 -0400 Subject: [PATCH] ITS#7027 Implement priority/weight for DNS SRV records From RFC 2782: A client MUST attempt to contact the target host with the lowest-numbered priority it can reach. This patch sorts the DNS SRV records by their priority, and additionally gives records with a larger weight a higher probability of appearing earlier. This way, the DNS SRV records are tried in the order of their priority. --- libraries/libldap/dnssrv.c | 92 +++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 17 deletions(-) diff --git a/libraries/libldap/dnssrv.c b/libraries/libldap/dnssrv.c index 6c6e5b120a..6d1bfa8e3c 100644 --- a/libraries/libldap/dnssrv.c +++ b/libraries/libldap/dnssrv.c @@ -174,6 +174,46 @@ int ldap_domain2dn( return LDAP_SUCCESS; } +#ifdef HAVE_RES_QUERY +#define DNSBUFSIZ (64*1024) +typedef struct srv_record { + u_short priority; + u_short weight; + u_short port; + char hostname[DNSBUFSIZ]; +} srv_record; + + +static int srv_cmp(const void *aa, const void *bb){ + srv_record *a=(srv_record *)aa; + srv_record *b=(srv_record *)bb; + u_long total; + + if(a->priority < b->priority) { + return -1; + } + if(a->priority > b->priority) { + return 1; + } + if(a->priority == b->priority){ + /* targets with same priority are in psudeo random order */ + if (a->weight == 0 && b->weight == 0) { + if (rand() % 2) { + return -1; + } else { + return 1; + } + } + total = a->weight + b->weight; + if (rand() % total < a->weight) { + return -1; + } else { + return 1; + } + } +} +#endif /* HAVE_RES_QUERY */ + /* * Lookup and return LDAP servers for domain (using the DNS * SRV record _ldap._tcp.domain). @@ -183,15 +223,16 @@ int ldap_domain2hostlist( char **list ) { #ifdef HAVE_RES_QUERY -#define DNSBUFSIZ (64*1024) char *request; char *hostlist = NULL; + srv_record *hostent_head=NULL; + int i; int rc, len, cur = 0; unsigned char reply[DNSBUFSIZ]; + int hostent_count=0; assert( domain != NULL ); assert( list != NULL ); - if( *domain == '\0' ) { return LDAP_PARAM_ERROR; } @@ -223,8 +264,7 @@ int ldap_domain2hostlist( unsigned char *p; char host[DNSBUFSIZ]; int status; - u_short port; - /* int priority, weight; */ + u_short port, priority, weight; /* Parse out query */ p = reply; @@ -263,36 +303,51 @@ int ldap_domain2hostlist( size = (p[0] << 8) | p[1]; p += 2; if (type == T_SRV) { - int buflen; status = dn_expand(reply, reply + len, p + 6, host, sizeof(host)); if (status < 0) { goto out; } - /* ignore priority and weight for now */ - /* priority = (p[0] << 8) | p[1]; */ - /* weight = (p[2] << 8) | p[3]; */ + + /* Get priority weight and port */ + priority = (p[0] << 8) | p[1]; + weight = (p[2] << 8) | p[3]; port = (p[4] << 8) | p[5]; if ( port == 0 || host[ 0 ] == '\0' ) { goto add_size; } - buflen = strlen(host) + STRLENOF(":65355 "); - hostlist = (char *) LDAP_REALLOC(hostlist, cur + buflen + 1); - if (hostlist == NULL) { - rc = LDAP_NO_MEMORY; + hostent_head = (srv_record *) LDAP_REALLOC(hostent_head, (hostent_count+1)*(sizeof(srv_record))); + if(hostent_head==NULL){ + rc=LDAP_NO_MEMORY; goto out; } - if (cur > 0) { - /* not first time around */ - hostlist[cur++] = ' '; - } - cur += sprintf(&hostlist[cur], "%s:%hu", host, port); + hostent_head[hostent_count].priority=priority; + hostent_head[hostent_count].weight=priority; + hostent_head[hostent_count].port=port; + strncpy(hostent_head[hostent_count].hostname, host,255); + hostent_count=hostent_count+1; } add_size:; p += size; } } + qsort(hostent_head, hostent_count, sizeof(srv_record), srv_cmp); + + for(i=0; i0){ + hostlist[cur++]=' '; + } + cur += sprintf(&hostlist[cur], "%s:%hd", hostent_head[i].hostname, hostent_head[i].port); + } + if (hostlist == NULL) { /* No LDAP servers found in DNS. */ rc = LDAP_UNAVAILABLE; @@ -308,6 +363,9 @@ add_size:; if (request != NULL) { LDAP_FREE(request); } + if (hostent_head != NULL) { + LDAP_FREE(hostent_head); + } if (rc != LDAP_SUCCESS && hostlist != NULL) { LDAP_FREE(hostlist); } -- 2.39.5