1243883Sbapt/*- 2257309Sbapt * Copyright (c) 2012-2013 Baptiste Daroussin <bapt@FreeBSD.org> 3243883Sbapt * All rights reserved. 4243883Sbapt * 5243883Sbapt * Redistribution and use in source and binary forms, with or without 6243883Sbapt * modification, are permitted provided that the following conditions 7243883Sbapt * are met: 8243883Sbapt * 1. Redistributions of source code must retain the above copyright 9243883Sbapt * notice, this list of conditions and the following disclaimer, 10243883Sbapt * without modification, immediately at the beginning of the file. 11243883Sbapt * 2. Redistributions in binary form must reproduce the above copyright 12243883Sbapt * notice, this list of conditions and the following disclaimer in the 13243883Sbapt * documentation and/or other materials provided with the distribution. 14243883Sbapt * 15243883Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16243883Sbapt * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17243883Sbapt * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18243883Sbapt * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19243883Sbapt * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20243883Sbapt * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21243883Sbapt * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22243883Sbapt * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23243883Sbapt * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24243883Sbapt * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25243883Sbapt */ 26243883Sbapt 27243883Sbapt#include <sys/cdefs.h> 28243883Sbapt__FBSDID("$FreeBSD$"); 29243883Sbapt 30243883Sbapt#include <stdlib.h> 31243883Sbapt#include <string.h> 32243883Sbapt#include <netinet/in.h> 33243883Sbapt#include <resolv.h> 34243883Sbapt 35243883Sbapt#include "dns_utils.h" 36243883Sbapt 37243883Sbapttypedef union { 38243883Sbapt HEADER hdr; 39243883Sbapt unsigned char buf[1024]; 40243883Sbapt} dns_query; 41243883Sbapt 42257309Sbaptstatic int 43257309Sbaptsrv_priority_cmp(const void *a, const void *b) 44257309Sbapt{ 45257309Sbapt const struct dns_srvinfo *da, *db; 46257309Sbapt unsigned int r, l; 47257309Sbapt 48257309Sbapt da = *(struct dns_srvinfo * const *)a; 49257309Sbapt db = *(struct dns_srvinfo * const *)b; 50257309Sbapt 51257309Sbapt l = da->priority; 52257309Sbapt r = db->priority; 53257309Sbapt 54257309Sbapt return ((l > r) - (l < r)); 55257309Sbapt} 56257309Sbapt 57257309Sbaptstatic int 58257309Sbaptsrv_final_cmp(const void *a, const void *b) 59257309Sbapt{ 60257309Sbapt const struct dns_srvinfo *da, *db; 61257309Sbapt unsigned int r, l, wr, wl; 62257309Sbapt int res; 63257309Sbapt 64257309Sbapt da = *(struct dns_srvinfo * const *)a; 65257309Sbapt db = *(struct dns_srvinfo * const *)b; 66257309Sbapt 67257309Sbapt l = da->priority; 68257309Sbapt r = db->priority; 69257309Sbapt 70257309Sbapt res = ((l > r) - (l < r)); 71257309Sbapt 72257309Sbapt if (res == 0) { 73257309Sbapt wl = da->finalweight; 74257309Sbapt wr = db->finalweight; 75257309Sbapt res = ((wr > wl) - (wr < wl)); 76257309Sbapt } 77257309Sbapt 78257309Sbapt return (res); 79257309Sbapt} 80257309Sbapt 81257309Sbaptstatic void 82257309Sbaptcompute_weight(struct dns_srvinfo **d, int first, int last) 83257309Sbapt{ 84257309Sbapt int i, j, totalweight; 85257309Sbapt int *chosen; 86257309Sbapt 87257309Sbapt chosen = malloc(sizeof(int) * (last - first + 1)); 88257309Sbapt totalweight = 0; 89257309Sbapt 90257309Sbapt for (i = 0; i <= last; i++) 91257309Sbapt totalweight += d[i]->weight; 92257309Sbapt 93257309Sbapt if (totalweight == 0) 94257309Sbapt return; 95257309Sbapt 96257309Sbapt for (i = 0; i <= last; i++) { 97257309Sbapt for (;;) { 98257309Sbapt chosen[i] = random() % (d[i]->weight * 100 / totalweight); 99257309Sbapt for (j = 0; j < i; j++) { 100257309Sbapt if (chosen[i] == chosen[j]) 101257309Sbapt break; 102257309Sbapt } 103257309Sbapt if (j == i) { 104257309Sbapt d[i]->finalweight = chosen[i]; 105257309Sbapt break; 106257309Sbapt } 107257309Sbapt } 108257309Sbapt } 109257309Sbapt 110257309Sbapt free(chosen); 111257309Sbapt} 112257309Sbapt 113243883Sbaptstruct dns_srvinfo * 114243883Sbaptdns_getsrvinfo(const char *zone) 115243883Sbapt{ 116243883Sbapt struct dns_srvinfo **res, *first; 117243883Sbapt unsigned char *end, *p; 118243883Sbapt char host[MAXHOSTNAMELEN]; 119243883Sbapt dns_query q; 120257309Sbapt int len, qdcount, ancount, n, i, f, l; 121243883Sbapt unsigned int type, class, ttl, priority, weight, port; 122243883Sbapt 123243883Sbapt if ((len = res_query(zone, C_IN, T_SRV, q.buf, sizeof(q.buf))) == -1 || 124243883Sbapt len < (int)sizeof(HEADER)) 125243883Sbapt return (NULL); 126243883Sbapt 127243883Sbapt qdcount = ntohs(q.hdr.qdcount); 128243883Sbapt ancount = ntohs(q.hdr.ancount); 129243883Sbapt 130243883Sbapt end = q.buf + len; 131243883Sbapt p = q.buf + sizeof(HEADER); 132243883Sbapt 133243883Sbapt while(qdcount > 0 && p < end) { 134243883Sbapt qdcount--; 135243883Sbapt if((len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN)) < 0) 136243883Sbapt return (NULL); 137243883Sbapt p += len + NS_QFIXEDSZ; 138243883Sbapt } 139243883Sbapt 140251560Sbapt res = calloc(ancount, sizeof(struct dns_srvinfo *)); 141243883Sbapt if (res == NULL) 142243883Sbapt return (NULL); 143243883Sbapt 144243883Sbapt n = 0; 145243883Sbapt while (ancount > 0 && p < end) { 146243883Sbapt ancount--; 147243883Sbapt len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN); 148243883Sbapt if (len < 0) { 149243883Sbapt for (i = 0; i < n; i++) 150243883Sbapt free(res[i]); 151243883Sbapt free(res); 152243883Sbapt return NULL; 153243883Sbapt } 154243883Sbapt 155243883Sbapt p += len; 156243883Sbapt 157243883Sbapt NS_GET16(type, p); 158243883Sbapt NS_GET16(class, p); 159243883Sbapt NS_GET32(ttl, p); 160243883Sbapt NS_GET16(len, p); 161243883Sbapt 162243883Sbapt if (type != T_SRV) { 163243883Sbapt p += len; 164243883Sbapt continue; 165243883Sbapt } 166243883Sbapt 167243883Sbapt NS_GET16(priority, p); 168243883Sbapt NS_GET16(weight, p); 169243883Sbapt NS_GET16(port, p); 170243883Sbapt 171243883Sbapt len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN); 172243883Sbapt if (len < 0) { 173243883Sbapt for (i = 0; i < n; i++) 174243883Sbapt free(res[i]); 175243883Sbapt free(res); 176243883Sbapt return (NULL); 177243883Sbapt } 178243883Sbapt 179243883Sbapt res[n] = malloc(sizeof(struct dns_srvinfo)); 180243883Sbapt if (res[n] == NULL) { 181243883Sbapt for (i = 0; i < n; i++) 182243883Sbapt free(res[i]); 183243883Sbapt free(res); 184243883Sbapt return (NULL); 185243883Sbapt } 186243883Sbapt res[n]->type = type; 187243883Sbapt res[n]->class = class; 188243883Sbapt res[n]->ttl = ttl; 189243883Sbapt res[n]->priority = priority; 190243883Sbapt res[n]->weight = weight; 191243883Sbapt res[n]->port = port; 192243883Sbapt res[n]->next = NULL; 193243883Sbapt strlcpy(res[n]->host, host, MAXHOSTNAMELEN); 194243883Sbapt 195243883Sbapt p += len; 196243883Sbapt n++; 197243883Sbapt } 198243883Sbapt 199257309Sbapt qsort(res, n, sizeof(res[0]), srv_priority_cmp); 200257309Sbapt 201257309Sbapt priority = f = l = 0; 202257309Sbapt for (i = 0; i < n; i++) { 203257309Sbapt if (res[i]->priority != priority) { 204257309Sbapt if (f != l) 205257309Sbapt compute_weight(res, f, l); 206257309Sbapt f = i; 207257309Sbapt priority = res[i]->priority; 208257309Sbapt } 209257309Sbapt l = i; 210257309Sbapt } 211257309Sbapt 212257309Sbapt qsort(res, n, sizeof(res[0]), srv_final_cmp); 213257309Sbapt 214243883Sbapt for (i = 0; i < n - 1; i++) 215243883Sbapt res[i]->next = res[i + 1]; 216243883Sbapt 217243883Sbapt first = res[0]; 218243883Sbapt free(res); 219243883Sbapt 220243883Sbapt return (first); 221243883Sbapt} 222