1 /**
2  * @file dname.c  DNS domain names
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 #include <re_types.h>
7 #include <re_fmt.h>
8 #include <re_list.h>
9 #include <re_hash.h>
10 #include <re_mem.h>
11 #include <re_mbuf.h>
12 #include <re_net.h>
13 #include <re_dns.h>
14 
15 
16 #define COMP_MASK   0xc0
17 #define OFFSET_MASK 0x3fff
18 #define COMP_LOOP   255
19 
20 
21 struct dname {
22 	struct le he;
23 	size_t pos;
24 	char *name;
25 };
26 
27 
destructor(void * arg)28 static void destructor(void *arg)
29 {
30 	struct dname *dn = arg;
31 
32 	hash_unlink(&dn->he);
33 	mem_deref(dn->name);
34 }
35 
36 
dname_append(struct hash * ht_dname,const char * name,size_t pos)37 static void dname_append(struct hash *ht_dname, const char *name, size_t pos)
38 {
39 	struct dname *dn;
40 
41 	if (!ht_dname || pos > OFFSET_MASK || !*name)
42 		return;
43 
44 	dn = mem_zalloc(sizeof(*dn), destructor);
45 	if (!dn)
46 		return;
47 
48 	if (str_dup(&dn->name, name)) {
49 		mem_deref(dn);
50 		return;
51 	}
52 
53 	hash_append(ht_dname, hash_joaat_str_ci(name), &dn->he, dn);
54 	dn->pos = pos;
55 }
56 
57 
lookup_handler(struct le * le,void * arg)58 static bool lookup_handler(struct le *le, void *arg)
59 {
60 	struct dname *dn = le->data;
61 
62 	return 0 == str_casecmp(dn->name, arg);
63 }
64 
65 
dname_lookup(struct hash * ht_dname,const char * name)66 static inline struct dname *dname_lookup(struct hash *ht_dname,
67 					 const char *name)
68 {
69 	return list_ledata(hash_lookup(ht_dname, hash_joaat_str_ci(name),
70 				       lookup_handler, (void *)name));
71 }
72 
73 
dname_encode_pointer(struct mbuf * mb,size_t pos)74 static inline int dname_encode_pointer(struct mbuf *mb, size_t pos)
75 {
76 	return mbuf_write_u16(mb, htons(pos | (COMP_MASK<<8)));
77 }
78 
79 
80 /**
81  * Encode a DNS Domain name into a memory buffer
82  *
83  * @param mb       Memory buffer
84  * @param name     Domain name
85  * @param ht_dname Domain name hashtable
86  * @param start    Start position
87  * @param comp     Enable compression
88  *
89  * @return 0 if success, otherwise errorcode
90  */
dns_dname_encode(struct mbuf * mb,const char * name,struct hash * ht_dname,size_t start,bool comp)91 int dns_dname_encode(struct mbuf *mb, const char *name,
92 		     struct hash *ht_dname, size_t start, bool comp)
93 {
94 	struct dname *dn;
95 	size_t pos;
96 	int err;
97 
98 	if (!mb || !name)
99 		return EINVAL;
100 
101 	dn = dname_lookup(ht_dname, name);
102 	if (dn && comp)
103 		return dname_encode_pointer(mb, dn->pos);
104 
105 	pos = mb->pos;
106 	if (!dn)
107 		dname_append(ht_dname, name, pos - start);
108 	err = mbuf_write_u8(mb, 0);
109 
110 	if ('.' == name[0] && '\0' == name[1])
111 		return err;
112 
113 	while (err == 0) {
114 
115 		const size_t lablen = mb->pos - pos - 1;
116 
117 		if ('\0' == *name) {
118 			if (!lablen)
119 				break;
120 
121 			mb->buf[pos] = lablen;
122 			err |= mbuf_write_u8(mb, 0);
123 			break;
124 		}
125 		else if ('.' == *name) {
126 			if (!lablen)
127 				return EINVAL;
128 
129 			mb->buf[pos] = lablen;
130 
131 			dn = dname_lookup(ht_dname, name + 1);
132 			if (dn && comp) {
133 				err |= dname_encode_pointer(mb, dn->pos);
134 				break;
135 			}
136 
137 			pos = mb->pos;
138 			if (!dn)
139 				dname_append(ht_dname, name + 1, pos - start);
140 			err |= mbuf_write_u8(mb, 0);
141 		}
142 		else {
143 			err |= mbuf_write_u8(mb, *name);
144 		}
145 
146 		++name;
147 	}
148 
149 	return err;
150 }
151 
152 
153 /**
154  * Decode a DNS domain name from a memory buffer
155  *
156  * @param mb    Memory buffer to decode from
157  * @param name  Pointer to allocated string with domain name
158  * @param start Start position
159  *
160  * @return 0 if success, otherwise errorcode
161  */
dns_dname_decode(struct mbuf * mb,char ** name,size_t start)162 int dns_dname_decode(struct mbuf *mb, char **name, size_t start)
163 {
164 	uint32_t i = 0, loopc = 0;
165 	bool comp = false;
166 	size_t pos = 0;
167 	char buf[256];
168 
169 	if (!mb || !name)
170 		return EINVAL;
171 
172 	while (mb->pos < mb->end) {
173 
174 		uint8_t len = mb->buf[mb->pos++];
175 		if (!len) {
176 			if (comp)
177 				mb->pos = pos;
178 
179 			buf[i++] = '\0';
180 
181 			*name = mem_alloc(i, NULL);
182 			if (!*name)
183 				return ENOMEM;
184 
185 			str_ncpy(*name, buf, i);
186 
187 			return 0;
188 		}
189 		else if ((len & COMP_MASK) == COMP_MASK) {
190 			uint16_t offset;
191 
192 			if (loopc++ > COMP_LOOP)
193 				break;
194 
195 			--mb->pos;
196 
197 			offset = ntohs(mbuf_read_u16(mb)) & OFFSET_MASK;
198 			if (!comp) {
199 				pos  = mb->pos;
200 				comp = true;
201 			}
202 
203 			mb->pos = offset + start;
204 			continue;
205 		}
206 		else if (len > mbuf_get_left(mb))
207 			break;
208 		else if (len > sizeof(buf) - i - 2)
209 			break;
210 
211 		if (i > 0)
212 			buf[i++] = '.';
213 
214 		while (len--)
215 			buf[i++] = mb->buf[mb->pos++];
216 	}
217 
218 	return EINVAL;
219 }
220