blob: eda9ba4367f30e58e597d7eb7ff6baaa40127db1 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
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
21struct dname {
22 struct le he;
23 size_t pos;
24 char *name;
25};
26
27
28static void destructor(void *arg)
29{
30 struct dname *dn = arg;
31
32 hash_unlink(&dn->he);
33 mem_deref(dn->name);
34}
35
36
37static 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
58static 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
66static 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
74static 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 */
91int 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 */
162int 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}