1 static wdns_res
gen_label_offsets(wdns_name_t * name,size_t n_labels,uint8_t * offsets)2 gen_label_offsets(wdns_name_t *name, size_t n_labels, uint8_t *offsets)
3 {
4 	size_t n = 0;
5 	uint8_t c, *data;
6 
7 	data = name->data;
8 
9 	while ((c = *data) != 0) {
10 		if (c <= 63) {
11 			offsets[n++] = data - name->data;
12 			if (n == n_labels)
13 				return (wdns_res_success);
14 			data += c;
15 			if (data - name->data > name->len)
16 				return (wdns_res_name_overflow);
17 		} else {
18 			return (wdns_res_invalid_length_octet);
19 		}
20 		data++;
21 	}
22 	return (wdns_res_success);
23 }
24 
25 static bool
compare_label(uint8_t * l0,uint8_t * l1)26 compare_label(uint8_t *l0, uint8_t *l1)
27 {
28 	uint8_t len0, len1;
29 	len0 = *l0++;
30 	len1 = *l1++;
31 	if (len0 == len1)
32 		return (memcmp(l0, l1, len0) == 0);
33 	return (false);
34 }
35 
36 /**
37  * Determine if a name is a subdomain of another domain.
38  *
39  * A domain is not a subdomain of itself.
40  *
41  * \param[in] n0
42  * \param[in] n1
43  * \param[out] is_subdomain
44  *
45  * \return wdns_res_success
46  * \return wdns_res_parse_error
47  */
48 
49 wdns_res
wdns_is_subdomain(wdns_name_t * n0,wdns_name_t * n1,bool * is_subdomain)50 wdns_is_subdomain(wdns_name_t *n0, wdns_name_t *n1, bool *is_subdomain)
51 {
52 	wdns_res res;
53 	size_t n0_nlabels, n1_nlabels;
54 	ssize_t n0_idx, n1_idx;
55 
56 	*is_subdomain = false;
57 
58 	/* count the number of labels in each name */
59 	res = wdns_count_labels(n0, &n0_nlabels);
60 	if (res != wdns_res_success)
61 		return (wdns_res_parse_error);
62 
63 	res = wdns_count_labels(n1, &n1_nlabels);
64 	if (res != wdns_res_success)
65 		return (wdns_res_parse_error);
66 
67 	/* exclude any cases that can be determined solely by label counts */
68 	if (n0_nlabels <= n1_nlabels) {
69 		/* a subdomain must have more labels than any of its parents */
70 		return (wdns_res_success);
71 	}
72 	if (n0_nlabels == 0) {
73 		/* the root cannot be a subdomain of any other domain */
74 		return (wdns_res_success);
75 	}
76 	if (n1_nlabels == 0) {
77 		/* all other domains are subdomains of the root */
78 		*is_subdomain = true;
79 		return (wdns_res_success);
80 	}
81 
82 	/* for each name, create an array of label offsets */
83 	uint8_t n0_offsets[n0_nlabels];
84 	uint8_t n1_offsets[n1_nlabels];
85 	memset(n0_offsets, 0, sizeof(n0_offsets));
86 	memset(n1_offsets, 0, sizeof(n1_offsets));
87 
88 	res = gen_label_offsets(n0, n0_nlabels, n0_offsets);
89 	if (res != wdns_res_success)
90 		return (wdns_res_parse_error);
91 
92 	res = gen_label_offsets(n1, n1_nlabels, n1_offsets);
93 	if (res != wdns_res_success)
94 		return (wdns_res_parse_error);
95 
96 	/* compare each label, right-to-left */
97 	n0_idx = n0_nlabels - 1;
98 	n1_idx = n1_nlabels - 1;
99 	do {
100 		if (!compare_label(n0->data + n0_offsets[n0_idx],
101 				   n1->data + n1_offsets[n1_idx]))
102 		{
103 			return (wdns_res_success);
104 		}
105 		n0_idx--;
106 		n1_idx--;
107 	} while (n1_idx >= 0);
108 
109 	/* all labels of the potential parent domain have compared true,
110 	 * thus n1 is a suffix of n0 */
111 	*is_subdomain = true;
112 	return (wdns_res_success);
113 }
114