1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2012-2013 Baptiste Daroussin <bapt@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer, 12 * without modification, immediately at the beginning of the file. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <netinet/in.h> 33 #include <resolv.h> 34 35 #include "dns_utils.h" 36 37 typedef union { 38 HEADER hdr; 39 unsigned char buf[1024]; 40 } dns_query; 41 42 static int 43 srv_priority_cmp(const void *a, const void *b) 44 { 45 const struct dns_srvinfo *da, *db; 46 unsigned int r, l; 47 48 da = *(struct dns_srvinfo * const *)a; 49 db = *(struct dns_srvinfo * const *)b; 50 51 l = da->priority; 52 r = db->priority; 53 54 return ((l > r) - (l < r)); 55 } 56 57 static int 58 srv_final_cmp(const void *a, const void *b) 59 { 60 const struct dns_srvinfo *da, *db; 61 unsigned int r, l, wr, wl; 62 int res; 63 64 da = *(struct dns_srvinfo * const *)a; 65 db = *(struct dns_srvinfo * const *)b; 66 67 l = da->priority; 68 r = db->priority; 69 70 res = ((l > r) - (l < r)); 71 72 if (res == 0) { 73 wl = da->finalweight; 74 wr = db->finalweight; 75 res = ((wr > wl) - (wr < wl)); 76 } 77 78 return (res); 79 } 80 81 static void 82 compute_weight(struct dns_srvinfo **d, int first, int last) 83 { 84 int i, j, totalweight; 85 int *chosen; 86 87 totalweight = 0; 88 89 for (i = 0; i <= last; i++) 90 totalweight += d[i]->weight; 91 92 if (totalweight == 0) 93 return; 94 95 chosen = malloc(sizeof(int) * (last - first + 1)); 96 97 for (i = 0; i <= last; i++) { 98 for (;;) { 99 chosen[i] = arc4random_uniform(d[i]->weight * 100 / 100 totalweight); 101 for (j = 0; j < i; j++) { 102 if (chosen[i] == chosen[j]) 103 break; 104 } 105 if (j == i) { 106 d[i]->finalweight = chosen[i]; 107 break; 108 } 109 } 110 } 111 112 free(chosen); 113 } 114 115 struct dns_srvinfo * 116 dns_getsrvinfo(const char *zone) 117 { 118 struct dns_srvinfo **res, *first; 119 unsigned char *end, *p; 120 char host[MAXHOSTNAMELEN]; 121 dns_query q; 122 int len, qdcount, ancount, n, i, f, l; 123 unsigned int type, class, ttl, priority, weight, port; 124 125 if ((len = res_query(zone, C_IN, T_SRV, q.buf, sizeof(q.buf))) == -1 || 126 len < (int)sizeof(HEADER)) 127 return (NULL); 128 129 qdcount = ntohs(q.hdr.qdcount); 130 ancount = ntohs(q.hdr.ancount); 131 132 end = q.buf + len; 133 p = q.buf + sizeof(HEADER); 134 135 while(qdcount > 0 && p < end) { 136 qdcount--; 137 if((len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN)) < 0) 138 return (NULL); 139 p += len + NS_QFIXEDSZ; 140 } 141 142 res = calloc(ancount, sizeof(struct dns_srvinfo *)); 143 if (res == NULL) 144 return (NULL); 145 146 n = 0; 147 while (ancount > 0 && p < end) { 148 ancount--; 149 len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN); 150 if (len < 0) { 151 for (i = 0; i < n; i++) 152 free(res[i]); 153 free(res); 154 return NULL; 155 } 156 157 p += len; 158 159 NS_GET16(type, p); 160 NS_GET16(class, p); 161 NS_GET32(ttl, p); 162 NS_GET16(len, p); 163 164 if (type != T_SRV) { 165 p += len; 166 continue; 167 } 168 169 NS_GET16(priority, p); 170 NS_GET16(weight, p); 171 NS_GET16(port, p); 172 173 len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN); 174 if (len < 0) { 175 for (i = 0; i < n; i++) 176 free(res[i]); 177 free(res); 178 return (NULL); 179 } 180 181 res[n] = malloc(sizeof(struct dns_srvinfo)); 182 if (res[n] == NULL) { 183 for (i = 0; i < n; i++) 184 free(res[i]); 185 free(res); 186 return (NULL); 187 } 188 res[n]->type = type; 189 res[n]->class = class; 190 res[n]->ttl = ttl; 191 res[n]->priority = priority; 192 res[n]->weight = weight; 193 res[n]->port = port; 194 res[n]->next = NULL; 195 strlcpy(res[n]->host, host, MAXHOSTNAMELEN); 196 197 p += len; 198 n++; 199 } 200 201 qsort(res, n, sizeof(res[0]), srv_priority_cmp); 202 203 priority = f = l = 0; 204 for (i = 0; i < n; i++) { 205 if (res[i]->priority != priority) { 206 if (f != l) 207 compute_weight(res, f, l); 208 f = i; 209 priority = res[i]->priority; 210 } 211 l = i; 212 } 213 214 qsort(res, n, sizeof(res[0]), srv_final_cmp); 215 216 for (i = 0; i < n - 1; i++) 217 res[i]->next = res[i + 1]; 218 219 first = res[0]; 220 free(res); 221 222 return (first); 223 } 224