xref: /freebsd/usr.sbin/pkg/dns_utils.c (revision 1d386b48)
129aaa961SBaptiste Daroussin /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
435e07a7aSBaptiste Daroussin  * Copyright (c) 2012-2013 Baptiste Daroussin <bapt@FreeBSD.org>
529aaa961SBaptiste Daroussin  * All rights reserved.
629aaa961SBaptiste Daroussin  *
729aaa961SBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
829aaa961SBaptiste Daroussin  * modification, are permitted provided that the following conditions
929aaa961SBaptiste Daroussin  * are met:
1029aaa961SBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
1129aaa961SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer,
1229aaa961SBaptiste Daroussin  *    without modification, immediately at the beginning of the file.
1329aaa961SBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
1429aaa961SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in the
1529aaa961SBaptiste Daroussin  *    documentation and/or other materials provided with the distribution.
1629aaa961SBaptiste Daroussin  *
1729aaa961SBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1829aaa961SBaptiste Daroussin  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1929aaa961SBaptiste Daroussin  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2029aaa961SBaptiste Daroussin  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2129aaa961SBaptiste Daroussin  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2229aaa961SBaptiste Daroussin  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2329aaa961SBaptiste Daroussin  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2429aaa961SBaptiste Daroussin  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2529aaa961SBaptiste Daroussin  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2629aaa961SBaptiste Daroussin  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2729aaa961SBaptiste Daroussin  */
2829aaa961SBaptiste Daroussin 
2929aaa961SBaptiste Daroussin #include <sys/cdefs.h>
3029aaa961SBaptiste Daroussin #include <stdlib.h>
3129aaa961SBaptiste Daroussin #include <string.h>
3229aaa961SBaptiste Daroussin #include <netinet/in.h>
3329aaa961SBaptiste Daroussin #include <resolv.h>
3429aaa961SBaptiste Daroussin 
3529aaa961SBaptiste Daroussin #include "dns_utils.h"
3629aaa961SBaptiste Daroussin 
3729aaa961SBaptiste Daroussin typedef union {
3829aaa961SBaptiste Daroussin 	HEADER hdr;
3929aaa961SBaptiste Daroussin 	unsigned char buf[1024];
4029aaa961SBaptiste Daroussin } dns_query;
4129aaa961SBaptiste Daroussin 
4235e07a7aSBaptiste Daroussin static int
srv_priority_cmp(const void * a,const void * b)4335e07a7aSBaptiste Daroussin srv_priority_cmp(const void *a, const void *b)
4435e07a7aSBaptiste Daroussin {
45959bd879SBaptiste Daroussin 	const struct dns_srvinfo *da, *db;
4635e07a7aSBaptiste Daroussin 	unsigned int r, l;
4735e07a7aSBaptiste Daroussin 
48959bd879SBaptiste Daroussin 	da = *(struct dns_srvinfo * const *)a;
49959bd879SBaptiste Daroussin 	db = *(struct dns_srvinfo * const *)b;
5035e07a7aSBaptiste Daroussin 
5135e07a7aSBaptiste Daroussin 	l = da->priority;
5235e07a7aSBaptiste Daroussin 	r = db->priority;
5335e07a7aSBaptiste Daroussin 
5435e07a7aSBaptiste Daroussin 	return ((l > r) - (l < r));
5535e07a7aSBaptiste Daroussin }
5635e07a7aSBaptiste Daroussin 
5735e07a7aSBaptiste Daroussin static int
srv_final_cmp(const void * a,const void * b)5835e07a7aSBaptiste Daroussin srv_final_cmp(const void *a, const void *b)
5935e07a7aSBaptiste Daroussin {
60959bd879SBaptiste Daroussin 	const struct dns_srvinfo *da, *db;
6135e07a7aSBaptiste Daroussin 	unsigned int r, l, wr, wl;
6235e07a7aSBaptiste Daroussin 	int res;
6335e07a7aSBaptiste Daroussin 
64959bd879SBaptiste Daroussin 	da = *(struct dns_srvinfo * const *)a;
65959bd879SBaptiste Daroussin 	db = *(struct dns_srvinfo * const *)b;
6635e07a7aSBaptiste Daroussin 
6735e07a7aSBaptiste Daroussin 	l = da->priority;
6835e07a7aSBaptiste Daroussin 	r = db->priority;
6935e07a7aSBaptiste Daroussin 
7035e07a7aSBaptiste Daroussin 	res = ((l > r) - (l < r));
7135e07a7aSBaptiste Daroussin 
7235e07a7aSBaptiste Daroussin 	if (res == 0) {
7335e07a7aSBaptiste Daroussin 		wl = da->finalweight;
7435e07a7aSBaptiste Daroussin 		wr = db->finalweight;
7535e07a7aSBaptiste Daroussin 		res = ((wr > wl) - (wr < wl));
7635e07a7aSBaptiste Daroussin 	}
7735e07a7aSBaptiste Daroussin 
7835e07a7aSBaptiste Daroussin 	return (res);
7935e07a7aSBaptiste Daroussin }
8035e07a7aSBaptiste Daroussin 
8135e07a7aSBaptiste Daroussin static void
compute_weight(struct dns_srvinfo ** d,int first,int last)8235e07a7aSBaptiste Daroussin compute_weight(struct dns_srvinfo **d, int first, int last)
8335e07a7aSBaptiste Daroussin {
8435e07a7aSBaptiste Daroussin 	int i, j, totalweight;
8535e07a7aSBaptiste Daroussin 	int *chosen;
8635e07a7aSBaptiste Daroussin 
8735e07a7aSBaptiste Daroussin 	totalweight = 0;
8835e07a7aSBaptiste Daroussin 
8935e07a7aSBaptiste Daroussin 	for (i = 0; i <= last; i++)
9035e07a7aSBaptiste Daroussin 		totalweight += d[i]->weight;
9135e07a7aSBaptiste Daroussin 
9235e07a7aSBaptiste Daroussin 	if (totalweight == 0)
9335e07a7aSBaptiste Daroussin 		return;
9435e07a7aSBaptiste Daroussin 
95f595a30bSXin LI 	chosen = malloc(sizeof(int) * (last - first + 1));
96f595a30bSXin LI 
9735e07a7aSBaptiste Daroussin 	for (i = 0; i <= last; i++) {
9835e07a7aSBaptiste Daroussin 		for (;;) {
99b2c4ca8dSKyle Evans 			chosen[i] = arc4random_uniform(d[i]->weight * 100 /
100b2c4ca8dSKyle Evans 			    totalweight);
10135e07a7aSBaptiste Daroussin 			for (j = 0; j < i; j++) {
10235e07a7aSBaptiste Daroussin 				if (chosen[i] == chosen[j])
10335e07a7aSBaptiste Daroussin 					break;
10435e07a7aSBaptiste Daroussin 			}
10535e07a7aSBaptiste Daroussin 			if (j == i) {
10635e07a7aSBaptiste Daroussin 				d[i]->finalweight = chosen[i];
10735e07a7aSBaptiste Daroussin 				break;
10835e07a7aSBaptiste Daroussin 			}
10935e07a7aSBaptiste Daroussin 		}
11035e07a7aSBaptiste Daroussin 	}
11135e07a7aSBaptiste Daroussin 
11235e07a7aSBaptiste Daroussin 	free(chosen);
11335e07a7aSBaptiste Daroussin }
11435e07a7aSBaptiste Daroussin 
11529aaa961SBaptiste Daroussin struct dns_srvinfo *
dns_getsrvinfo(const char * zone)11629aaa961SBaptiste Daroussin dns_getsrvinfo(const char *zone)
11729aaa961SBaptiste Daroussin {
11829aaa961SBaptiste Daroussin 	struct dns_srvinfo **res, *first;
11929aaa961SBaptiste Daroussin 	unsigned char *end, *p;
12029aaa961SBaptiste Daroussin 	char host[MAXHOSTNAMELEN];
12129aaa961SBaptiste Daroussin 	dns_query q;
12235e07a7aSBaptiste Daroussin 	int len, qdcount, ancount, n, i, f, l;
12329aaa961SBaptiste Daroussin 	unsigned int type, class, ttl, priority, weight, port;
12429aaa961SBaptiste Daroussin 
12529aaa961SBaptiste Daroussin 	if ((len = res_query(zone, C_IN, T_SRV, q.buf, sizeof(q.buf))) == -1 ||
12629aaa961SBaptiste Daroussin 	    len < (int)sizeof(HEADER))
12729aaa961SBaptiste Daroussin 		return (NULL);
12829aaa961SBaptiste Daroussin 
12929aaa961SBaptiste Daroussin 	qdcount = ntohs(q.hdr.qdcount);
13029aaa961SBaptiste Daroussin 	ancount = ntohs(q.hdr.ancount);
13129aaa961SBaptiste Daroussin 
13229aaa961SBaptiste Daroussin 	end = q.buf + len;
13329aaa961SBaptiste Daroussin 	p = q.buf + sizeof(HEADER);
13429aaa961SBaptiste Daroussin 
13529aaa961SBaptiste Daroussin 	while(qdcount > 0 && p < end) {
13629aaa961SBaptiste Daroussin 		qdcount--;
13729aaa961SBaptiste Daroussin 		if((len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN)) < 0)
13829aaa961SBaptiste Daroussin 			return (NULL);
13929aaa961SBaptiste Daroussin 		p += len + NS_QFIXEDSZ;
14029aaa961SBaptiste Daroussin 	}
14129aaa961SBaptiste Daroussin 
1428ad6d917SBaptiste Daroussin 	res = calloc(ancount, sizeof(struct dns_srvinfo *));
14329aaa961SBaptiste Daroussin 	if (res == NULL)
14429aaa961SBaptiste Daroussin 		return (NULL);
14529aaa961SBaptiste Daroussin 
14629aaa961SBaptiste Daroussin 	n = 0;
14729aaa961SBaptiste Daroussin 	while (ancount > 0 && p < end) {
14829aaa961SBaptiste Daroussin 		ancount--;
14929aaa961SBaptiste Daroussin 		len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN);
15029aaa961SBaptiste Daroussin 		if (len < 0) {
15129aaa961SBaptiste Daroussin 			for (i = 0; i < n; i++)
15229aaa961SBaptiste Daroussin 				free(res[i]);
15329aaa961SBaptiste Daroussin 			free(res);
15429aaa961SBaptiste Daroussin 			return NULL;
15529aaa961SBaptiste Daroussin 		}
15629aaa961SBaptiste Daroussin 
15729aaa961SBaptiste Daroussin 		p += len;
15829aaa961SBaptiste Daroussin 
15929aaa961SBaptiste Daroussin 		NS_GET16(type, p);
16029aaa961SBaptiste Daroussin 		NS_GET16(class, p);
16129aaa961SBaptiste Daroussin 		NS_GET32(ttl, p);
16229aaa961SBaptiste Daroussin 		NS_GET16(len, p);
16329aaa961SBaptiste Daroussin 
16429aaa961SBaptiste Daroussin 		if (type != T_SRV) {
16529aaa961SBaptiste Daroussin 			p += len;
16629aaa961SBaptiste Daroussin 			continue;
16729aaa961SBaptiste Daroussin 		}
16829aaa961SBaptiste Daroussin 
16929aaa961SBaptiste Daroussin 		NS_GET16(priority, p);
17029aaa961SBaptiste Daroussin 		NS_GET16(weight, p);
17129aaa961SBaptiste Daroussin 		NS_GET16(port, p);
17229aaa961SBaptiste Daroussin 
17329aaa961SBaptiste Daroussin 		len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN);
17429aaa961SBaptiste Daroussin 		if (len < 0) {
17529aaa961SBaptiste Daroussin 			for (i = 0; i < n; i++)
17629aaa961SBaptiste Daroussin 				free(res[i]);
17729aaa961SBaptiste Daroussin 			free(res);
17829aaa961SBaptiste Daroussin 			return (NULL);
17929aaa961SBaptiste Daroussin 		}
18029aaa961SBaptiste Daroussin 
18129aaa961SBaptiste Daroussin 		res[n] = malloc(sizeof(struct dns_srvinfo));
18229aaa961SBaptiste Daroussin 		if (res[n] == NULL) {
18329aaa961SBaptiste Daroussin 			for (i = 0; i < n; i++)
18429aaa961SBaptiste Daroussin 				free(res[i]);
18529aaa961SBaptiste Daroussin 			free(res);
18629aaa961SBaptiste Daroussin 			return (NULL);
18729aaa961SBaptiste Daroussin 		}
18829aaa961SBaptiste Daroussin 		res[n]->type = type;
18929aaa961SBaptiste Daroussin 		res[n]->class = class;
19029aaa961SBaptiste Daroussin 		res[n]->ttl = ttl;
19129aaa961SBaptiste Daroussin 		res[n]->priority = priority;
19229aaa961SBaptiste Daroussin 		res[n]->weight = weight;
19329aaa961SBaptiste Daroussin 		res[n]->port = port;
19429aaa961SBaptiste Daroussin 		res[n]->next = NULL;
19529aaa961SBaptiste Daroussin 		strlcpy(res[n]->host, host, MAXHOSTNAMELEN);
19629aaa961SBaptiste Daroussin 
19729aaa961SBaptiste Daroussin 		p += len;
19829aaa961SBaptiste Daroussin 		n++;
19929aaa961SBaptiste Daroussin 	}
20029aaa961SBaptiste Daroussin 
20135e07a7aSBaptiste Daroussin 	qsort(res, n, sizeof(res[0]), srv_priority_cmp);
20235e07a7aSBaptiste Daroussin 
20335e07a7aSBaptiste Daroussin 	priority = f = l = 0;
20435e07a7aSBaptiste Daroussin 	for (i = 0; i < n; i++) {
20535e07a7aSBaptiste Daroussin 		if (res[i]->priority != priority) {
20635e07a7aSBaptiste Daroussin 			if (f != l)
20735e07a7aSBaptiste Daroussin 				compute_weight(res, f, l);
20835e07a7aSBaptiste Daroussin 			f = i;
20935e07a7aSBaptiste Daroussin 			priority = res[i]->priority;
21035e07a7aSBaptiste Daroussin 		}
21135e07a7aSBaptiste Daroussin 		l = i;
21235e07a7aSBaptiste Daroussin 	}
21335e07a7aSBaptiste Daroussin 
21435e07a7aSBaptiste Daroussin 	qsort(res, n, sizeof(res[0]), srv_final_cmp);
21535e07a7aSBaptiste Daroussin 
21629aaa961SBaptiste Daroussin 	for (i = 0; i < n - 1; i++)
21729aaa961SBaptiste Daroussin 		res[i]->next = res[i + 1];
21829aaa961SBaptiste Daroussin 
21929aaa961SBaptiste Daroussin 	first = res[0];
22029aaa961SBaptiste Daroussin 	free(res);
22129aaa961SBaptiste Daroussin 
22229aaa961SBaptiste Daroussin 	return (first);
22329aaa961SBaptiste Daroussin }
224