1 /**
2  * @file rrlist.c  DNS Resource Records list
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 #include <string.h>
7 #include <re_types.h>
8 #include <re_list.h>
9 #include <re_hash.h>
10 #include <re_mbuf.h>
11 #include <re_fmt.h>
12 #include <re_dns.h>
13 
14 
15 enum {
16 	CNAME_RECURSE_MAX = 16,
17 };
18 
19 
20 struct sort {
21 	uint16_t type;
22 	uint32_t key;
23 };
24 
25 
sidx(const struct dnsrr * rr,uint32_t key)26 static uint32_t sidx(const struct dnsrr *rr, uint32_t key)
27 {
28 	uint32_t addr[4];
29 
30 	switch (rr->type) {
31 
32 	case DNS_TYPE_A:
33 		return rr->rdata.a.addr ^ key;
34 
35 	case DNS_TYPE_AAAA:
36 		memcpy(addr, rr->rdata.aaaa.addr, 16);
37 
38 		return addr[0] ^ addr[1] ^ addr[2] ^ addr[3] ^ key;
39 
40 	case DNS_TYPE_SRV:
41 		return ((hash_fast_str(rr->rdata.srv.target) & 0xfff) ^ key) +
42 			rr->rdata.srv.weight;
43 
44 	default:
45 		return 0;
46 	}
47 }
48 
49 
std_sort_handler(struct le * le1,struct le * le2,void * arg)50 static bool std_sort_handler(struct le *le1, struct le *le2, void *arg)
51 {
52 	struct dnsrr *rr1 = le1->data;
53 	struct dnsrr *rr2 = le2->data;
54 	struct sort *sort = arg;
55 
56 	if (sort->type != rr1->type)
57 		return sort->type != rr2->type;
58 
59 	if (sort->type != rr2->type)
60 		return true;
61 
62 	switch (sort->type) {
63 
64 	case DNS_TYPE_MX:
65 		return rr1->rdata.mx.pref <= rr2->rdata.mx.pref;
66 
67 	case DNS_TYPE_SRV:
68 		if (rr1->rdata.srv.pri == rr2->rdata.srv.pri)
69 			return sidx(rr1, sort->key) >= sidx(rr2, sort->key);
70 
71 		return rr1->rdata.srv.pri < rr2->rdata.srv.pri;
72 
73 	case DNS_TYPE_NAPTR:
74 		if (rr1->rdata.naptr.order == rr2->rdata.naptr.order)
75 			return rr1->rdata.naptr.pref <= rr2->rdata.naptr.pref;
76 
77 		return rr1->rdata.naptr.order < rr2->rdata.naptr.order;
78 
79 	default:
80 		break;
81 	}
82 
83 	return true;
84 }
85 
86 
addr_sort_handler(struct le * le1,struct le * le2,void * arg)87 static bool addr_sort_handler(struct le *le1, struct le *le2, void *arg)
88 {
89 	struct dnsrr *rr1 = le1->data;
90 	struct dnsrr *rr2 = le2->data;
91 	struct sort *sort = arg;
92 
93 	return sidx(rr1, sort->key) >= sidx(rr2, sort->key);
94 }
95 
96 
97 /**
98  * Sort a list of DNS Resource Records
99  *
100  * @param rrl  DNS Resource Record list
101  * @param type DNS Record type
102  * @param key  Sort key
103  */
dns_rrlist_sort(struct list * rrl,uint16_t type,size_t key)104 void dns_rrlist_sort(struct list *rrl, uint16_t type, size_t key)
105 {
106 	struct sort sort = {type, (uint32_t)key>>5};
107 
108 	list_sort(rrl, std_sort_handler, &sort);
109 }
110 
111 
112 /**
113  * Sort a list of A/AAAA DNS Resource Records
114  *
115  * @param rrl  DNS Resource Record list
116  * @param key  Sort key
117  */
dns_rrlist_sort_addr(struct list * rrl,size_t key)118 void dns_rrlist_sort_addr(struct list *rrl, size_t key)
119 {
120 	struct sort sort = {0, (uint32_t)key>>5};
121 
122 	list_sort(rrl, addr_sort_handler, &sort);
123 }
124 
125 
rrlist_apply(struct list * rrl,const char * name,uint16_t type1,uint16_t type2,uint16_t dnsclass,bool recurse,uint32_t depth,dns_rrlist_h * rrlh,void * arg)126 static struct dnsrr *rrlist_apply(struct list *rrl, const char *name,
127 				  uint16_t type1, uint16_t type2,
128 				  uint16_t dnsclass,
129 				  bool recurse, uint32_t depth,
130 				  dns_rrlist_h *rrlh, void *arg)
131 {
132 	struct le *le = list_head(rrl);
133 
134 	if (depth > CNAME_RECURSE_MAX)
135 		return NULL;
136 
137 	while (le) {
138 
139 		struct dnsrr *rr = le->data;
140 
141 		le = le->next;
142 
143 		if (name && str_casecmp(name, rr->name))
144 			continue;
145 
146 		if (type1 != DNS_QTYPE_ANY && type2 != DNS_QTYPE_ANY &&
147 		    rr->type != type1 && rr->type != type2 &&
148 		    (rr->type != DNS_TYPE_CNAME || !recurse))
149 			continue;
150 
151 		if (dnsclass != DNS_QCLASS_ANY && rr->dnsclass != dnsclass)
152 			continue;
153 
154 		if (!rrlh || rrlh(rr, arg))
155 			return rr;
156 
157 		if (recurse &&
158 		    DNS_QTYPE_ANY != type1 && DNS_QTYPE_ANY != type2 &&
159 		    DNS_TYPE_CNAME != type1 && DNS_TYPE_CNAME != type2 &&
160 		    DNS_TYPE_CNAME == rr->type) {
161 			rr = rrlist_apply(rrl, rr->rdata.cname.cname, type1,
162 					  type2, dnsclass, recurse, ++depth,
163 					  rrlh, arg);
164 			if (rr)
165 				return rr;
166 		}
167 	}
168 
169 	return NULL;
170 }
171 
172 
173 /**
174  * Apply a function handler to a list of DNS Resource Records
175  *
176  * @param rrl      DNS Resource Record list
177  * @param name     If set, filter on domain name
178  * @param type     If not DNS_QTYPE_ANY, filter on record type
179  * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class
180  * @param recurse  Cname recursion
181  * @param rrlh     Resource record handler
182  * @param arg      Handler argument
183  *
184  * @return Matching Resource Record or NULL
185  */
dns_rrlist_apply(struct list * rrl,const char * name,uint16_t type,uint16_t dnsclass,bool recurse,dns_rrlist_h * rrlh,void * arg)186 struct dnsrr *dns_rrlist_apply(struct list *rrl, const char *name,
187 			       uint16_t type, uint16_t dnsclass,
188 			       bool recurse, dns_rrlist_h *rrlh, void *arg)
189 {
190 	return rrlist_apply(rrl, name, type, type, dnsclass,
191 			    recurse, 0, rrlh, arg);
192 }
193 
194 
195 /**
196  * Apply a function handler to a list of DNS Resource Records (two types)
197  *
198  * @param rrl      DNS Resource Record list
199  * @param name     If set, filter on domain name
200  * @param type1    If not DNS_QTYPE_ANY, filter on record type
201  * @param type2    If not DNS_QTYPE_ANY, filter on record type
202  * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class
203  * @param recurse  Cname recursion
204  * @param rrlh     Resource record handler
205  * @param arg      Handler argument
206  *
207  * @return Matching Resource Record or NULL
208  */
dns_rrlist_apply2(struct list * rrl,const char * name,uint16_t type1,uint16_t type2,uint16_t dnsclass,bool recurse,dns_rrlist_h * rrlh,void * arg)209 struct dnsrr *dns_rrlist_apply2(struct list *rrl, const char *name,
210 				uint16_t type1, uint16_t type2,
211 				uint16_t dnsclass, bool recurse,
212 				dns_rrlist_h *rrlh, void *arg)
213 {
214 	return rrlist_apply(rrl, name, type1, type2, dnsclass,
215 			    recurse, 0, rrlh, arg);
216 }
217 
218 
find_handler(struct dnsrr * rr,void * arg)219 static bool find_handler(struct dnsrr *rr, void *arg)
220 {
221 	uint16_t type = *(uint16_t *)arg;
222 
223 	return rr->type == type;
224 }
225 
226 
227 /**
228  * Find a DNS Resource Record in a list
229  *
230  * @param rrl      Resource Record list
231  * @param name     If set, filter on domain name
232  * @param type     If not DNS_QTYPE_ANY, filter on record type
233  * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class
234  * @param recurse  Cname recursion
235  *
236  * @return Matching Resource Record or NULL
237  */
dns_rrlist_find(struct list * rrl,const char * name,uint16_t type,uint16_t dnsclass,bool recurse)238 struct dnsrr *dns_rrlist_find(struct list *rrl, const char *name,
239 			      uint16_t type, uint16_t dnsclass, bool recurse)
240 {
241 	return rrlist_apply(rrl, name, type, type, dnsclass,
242 			    recurse, 0, find_handler, &type);
243 }
244