dns_utils.c
1 /*- 2 * Copyright (c) 2012-2014 Baptiste Daroussin <bapt@FreeBSD.org> 3 * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer, 11 * without modification, immediately at the beginning of the file. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <pkg_config.h> 29 30 #include <sys/stat.h> /* for private.utils.h */ 31 32 #include <string.h> 33 #include <netinet/in.h> 34 #ifdef HAVE_LDNS 35 #include <ldns/ldns.h> 36 #else 37 #define BIND_8_COMPAT 38 #include <arpa/nameser.h> 39 #include <resolv.h> 40 #endif 41 #include <netdb.h> 42 43 #ifndef NS_QFIXEDSZ 44 #define NS_QFIXEDSZ 4 /*%< #/bytes of fixed data in query */ 45 #endif 46 47 #ifndef NS_INT16SZ 48 #define NS_INT16SZ 2 /*%< #/bytes of data in a u_int16_t */ 49 #endif 50 51 #ifndef NS_INT32SZ 52 #define NS_INT32SZ 4 /*%< #/bytes of data in a u_int32_t */ 53 #endif 54 55 /*% 56 * Inline versions of get/put short/long. Pointer is advanced. 57 */ 58 #ifndef NS_GET16 59 #define NS_GET16(s, cp) do { \ 60 register const u_char *t_cp = (const u_char *)(cp); \ 61 (s) = ((u_int16_t)t_cp[0] << 8) \ 62 | ((u_int16_t)t_cp[1]) \ 63 ; \ 64 (cp) += NS_INT16SZ; \ 65 } while (0) 66 #endif 67 68 #ifndef NS_GET32 69 #define NS_GET32(l, cp) do { \ 70 register const u_char *t_cp = (const u_char *)(cp); \ 71 (l) = ((u_int32_t)t_cp[0] << 24) \ 72 | ((u_int32_t)t_cp[1] << 16) \ 73 | ((u_int32_t)t_cp[2] << 8) \ 74 | ((u_int32_t)t_cp[3]) \ 75 ; \ 76 (cp) += NS_INT32SZ; \ 77 } while (0) 78 #endif 79 80 #include <bsd_compat.h> 81 #include "private/utils.h" 82 #include "xmalloc.h" 83 #include "pkg.h" 84 85 #ifndef HAVE_LDNS 86 typedef union { 87 HEADER hdr; 88 unsigned char buf[1024]; 89 } query_t; 90 #endif 91 92 static int 93 srv_priority_cmp(const void *a, const void *b) 94 { 95 const struct dns_srvinfo *da, *db; 96 #ifdef HAVE_LDNS 97 da = (const struct dns_srvinfo *)a; 98 db = (const struct dns_srvinfo *)b; 99 #else 100 da = *(struct dns_srvinfo * const *)a; 101 db = *(struct dns_srvinfo * const *)b; 102 #endif 103 104 return ((da->priority > db->priority) - (da->priority < db->priority)); 105 } 106 107 static int 108 srv_final_cmp(const void *a, const void *b) 109 { 110 const struct dns_srvinfo *da, *db; 111 int res; 112 #ifdef HAVE_LDNS 113 da = (const struct dns_srvinfo *)a; 114 db = (const struct dns_srvinfo *)b; 115 #else 116 da = *(struct dns_srvinfo * const *)a; 117 db = *(struct dns_srvinfo * const *)b; 118 #endif 119 120 res = ((da->priority > db->priority) - (da->priority < db->priority)); 121 if (res == 0) 122 res = ((db->finalweight > da->finalweight) - (db->finalweight < da->finalweight)); 123 124 return (res); 125 } 126 127 #ifndef HAVE_LDNS 128 static void 129 compute_weight(struct dns_srvinfo **d, int first, int last) 130 { 131 int i, j; 132 int totalweight = 0; 133 int *chosen; 134 135 for (i = 0; i <= last; i++) 136 totalweight += d[i]->weight; 137 138 if (totalweight == 0) 139 return; 140 141 chosen = xmalloc(sizeof(int) * (last - first + 1)); 142 143 for (i = 0; i <= last; i++) { 144 for (;;) { 145 chosen[i] = random() % (d[i]->weight * 100 / totalweight); 146 for (j = 0; j < i; j++) { 147 if (chosen[i] == chosen[j]) 148 break; 149 } 150 if (j == i) { 151 d[i]->finalweight = chosen[i]; 152 break; 153 } 154 } 155 } 156 157 free(chosen); 158 } 159 160 struct dns_srvinfo * 161 dns_getsrvinfo(const char *zone) 162 { 163 char host[MAXHOSTNAMELEN]; 164 query_t q; 165 int len, qdcount, ancount, n, i; 166 struct dns_srvinfo **res, *first; 167 unsigned char *end, *p; 168 unsigned int type, class, ttl, priority, weight, port; 169 int f, l; 170 171 if ((len = res_query(zone, C_IN, T_SRV, q.buf, sizeof(q.buf))) == -1 || 172 len < (int)sizeof(HEADER)) 173 return (NULL); 174 175 qdcount = ntohs(q.hdr.qdcount); 176 ancount = ntohs(q.hdr.ancount); 177 178 end = q.buf + len; 179 p = q.buf + sizeof(HEADER); 180 181 while(qdcount > 0 && p < end) { 182 qdcount--; 183 if((len = dn_expand(q.buf, end, p, host, sizeof(host))) < 0) 184 return (NULL); 185 p += len + NS_QFIXEDSZ; 186 } 187 188 res = xcalloc(ancount, sizeof(struct dns_srvinfo *)); 189 n = 0; 190 while (ancount > 0 && p < end) { 191 ancount--; 192 len = dn_expand(q.buf, end, p, host, sizeof(host)); 193 if (len < 0) { 194 for (i = 0; i < n; i++) 195 free(res[i]); 196 free(res); 197 return NULL; 198 } 199 200 p += len; 201 202 NS_GET16(type, p); 203 NS_GET16(class, p); 204 NS_GET32(ttl, p); 205 NS_GET16(len, p); 206 207 if (type != T_SRV) { 208 p += len; 209 continue; 210 } 211 212 NS_GET16(priority, p); 213 NS_GET16(weight, p); 214 NS_GET16(port, p); 215 216 len = dn_expand(q.buf, end, p, host, sizeof(host)); 217 if (len < 0) { 218 for (i = 0; i < n; i++) 219 free(res[i]); 220 free(res); 221 return NULL; 222 } 223 224 res[n] = xmalloc(sizeof(struct dns_srvinfo)); 225 res[n]->type = type; 226 res[n]->class = class; 227 res[n]->ttl = ttl; 228 res[n]->priority = priority; 229 res[n]->weight = weight; 230 res[n]->port = port; 231 res[n]->next = NULL; 232 res[n]->finalweight = 0; 233 strlcpy(res[n]->host, host, sizeof(res[n]->host)); 234 235 p += len; 236 n++; 237 } 238 239 /* order by priority */ 240 qsort(res, n, sizeof(res[0]), srv_priority_cmp); 241 242 priority = 0; 243 f = 0; 244 l = 0; 245 for (i = 0; i < n; i++) { 246 if (res[i]->priority != priority) { 247 if (f != l) 248 compute_weight(res, f, l); 249 f = i; 250 priority = res[i]->priority; 251 } 252 l = i; 253 } 254 255 qsort(res, n, sizeof(res[0]), srv_final_cmp); 256 257 for (i = 0; i < n - 1; i++) 258 res[i]->next = res[i + 1]; 259 260 /* Sort against priority then weight */ 261 262 first = res[0]; 263 free(res); 264 265 return (first); 266 } 267 268 int 269 set_nameserver(const char *nsname) { 270 #ifndef HAVE___RES_SETSERVERS 271 return (-1); 272 #else 273 struct __res_state res; 274 union res_sockaddr_union u[MAXNS]; 275 struct addrinfo *answer = NULL; 276 struct addrinfo *cur = NULL; 277 struct addrinfo hint; 278 int nscount = 0; 279 280 memset(u, 0, sizeof(u)); 281 memset(&hint, 0, sizeof(hint)); 282 memset(&res, 0, sizeof(res)); 283 hint.ai_socktype = SOCK_DGRAM; 284 hint.ai_flags = AI_NUMERICHOST; 285 286 if (res_ninit(&res) == -1) 287 return (-1); 288 289 if (getaddrinfo(nsname, NULL, &hint, &answer) == 0) { 290 for (cur = answer; cur != NULL; cur = cur->ai_next) { 291 if (nscount == MAXNS) 292 break; 293 switch (cur->ai_addr->sa_family) { 294 case AF_INET6: 295 u[nscount].sin6 = *(struct sockaddr_in6*)(void *)cur->ai_addr; 296 u[nscount++].sin6.sin6_port = htons(53); 297 break; 298 case AF_INET: 299 u[nscount].sin = *(struct sockaddr_in*)(void *)cur->ai_addr; 300 u[nscount++].sin.sin_port = htons(53); 301 break; 302 } 303 } 304 if (nscount != 0) 305 res_setservers(&res, u, nscount); 306 freeaddrinfo(answer); 307 } 308 if (nscount == 0) 309 return (-1); 310 311 _res = res; 312 313 return (0); 314 #endif 315 } 316 #else 317 318 static ldns_resolver *lres = NULL; 319 320 static void 321 compute_weight(struct dns_srvinfo *d, int first, int last) 322 { 323 int i, j; 324 int totalweight = 0; 325 int *chosen; 326 327 for (i = 0; i <= last; i++) 328 totalweight += d[i].weight; 329 330 if (totalweight == 0) 331 return; 332 333 chosen = xmalloc(sizeof(int) * (last - first + 1)); 334 335 for (i = 0; i <= last; i++) { 336 for (;;) { 337 chosen[i] = random() % (d[i].weight * 100 / totalweight); 338 for (j = 0; j < i; j++) { 339 if (chosen[i] == chosen[j]) 340 break; 341 } 342 if (j == i) { 343 d[i].finalweight = chosen[i]; 344 break; 345 } 346 } 347 } 348 349 free(chosen); 350 } 351 352 struct dns_srvinfo * 353 dns_getsrvinfo(const char *zone) 354 { 355 ldns_rdf *domain; 356 ldns_pkt *p; 357 ldns_rr_list *srv; 358 struct dns_srvinfo *res; 359 int ancount, i; 360 int f, l, priority; 361 362 if (lres == NULL) 363 if (ldns_resolver_new_frm_file(&lres, NULL) != LDNS_STATUS_OK) 364 return (NULL); 365 366 domain = ldns_dname_new_frm_str(zone); 367 if (domain == NULL) 368 return (NULL); 369 370 p = ldns_resolver_query(lres, domain, 371 LDNS_RR_TYPE_SRV, 372 LDNS_RR_CLASS_IN, 373 LDNS_RD); 374 375 ldns_rdf_deep_free(domain); 376 377 if (p == NULL) 378 return (NULL); 379 380 srv = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_SRV, LDNS_SECTION_ANSWER); 381 ldns_pkt_free(p); 382 383 if (srv == NULL) 384 return (NULL); 385 386 ancount = ldns_rr_list_rr_count(srv); 387 res = xcalloc(ancount, sizeof(struct dns_srvinfo)); 388 389 for (i = 0; i < ancount; i ++) { 390 ldns_rr *rr; 391 392 rr = ldns_rr_list_rr(srv, i); 393 if (rr != NULL) { 394 char *host; 395 res[i].class = ldns_rr_get_class(rr); 396 res[i].ttl = ldns_rr_ttl(rr); 397 res[i].priority = ldns_rdf2native_int16(ldns_rr_rdf(rr, 0)); 398 res[i].weight = ldns_rdf2native_int16(ldns_rr_rdf(rr, 1)); 399 res[i].port = ldns_rdf2native_int16(ldns_rr_rdf(rr, 2)); 400 host = ldns_rdf2str(ldns_rr_rdf(rr, 3)); 401 strlcpy(res[i].host, host, sizeof(res[i].host)); 402 free(host); 403 } 404 } 405 406 ldns_rr_list_deep_free(srv); 407 408 /* order by priority */ 409 qsort(res, ancount, sizeof(res[0]), srv_priority_cmp); 410 411 priority = 0; 412 f = 0; 413 l = 0; 414 for (i = 0; i < ancount; i++) { 415 if (res[i].priority != priority) { 416 if (f != l) 417 compute_weight(res, f, l); 418 f = i; 419 priority = res[i].priority; 420 } 421 l = i; 422 } 423 424 /* Sort against priority then weight */ 425 qsort(res, ancount, sizeof(res[0]), srv_final_cmp); 426 427 for (i = 0; i < ancount - 1; i++) 428 res[i].next = &res[i + 1]; 429 430 return (res); 431 } 432 433 int 434 set_nameserver(const char *nsname) 435 { 436 /* 437 * XXX: can we use the system resolver to resolve this name ?? 438 * The current code does this, but it is unlikely a good solution 439 * So here we allow IP addresses only 440 */ 441 ldns_rdf *rdf; 442 443 rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, nsname); 444 if (rdf == NULL) 445 rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, nsname); 446 447 if (rdf == NULL) 448 return (EPKG_FATAL); 449 450 if (lres == NULL) 451 if (ldns_resolver_new_frm_file(&lres, NULL) != LDNS_STATUS_OK) 452 return (EPKG_FATAL); 453 454 if (ldns_resolver_push_nameserver(lres, rdf) != LDNS_STATUS_OK) 455 return (EPKG_FATAL); 456 457 return (EPKG_OK); 458 } 459 #endif