129aaa961SBaptiste Daroussin /*-
2*4d846d26SWarner 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