xref: /openbmc/linux/fs/nfs/dns_resolve.c (revision a09d2831)
1 /*
2  * linux/fs/nfs/dns_resolve.c
3  *
4  * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
5  *
6  * Resolves DNS hostnames into valid ip addresses
7  */
8 
9 #include <linux/hash.h>
10 #include <linux/string.h>
11 #include <linux/kmod.h>
12 #include <linux/module.h>
13 #include <linux/socket.h>
14 #include <linux/seq_file.h>
15 #include <linux/inet.h>
16 #include <linux/sunrpc/clnt.h>
17 #include <linux/sunrpc/cache.h>
18 #include <linux/sunrpc/svcauth.h>
19 
20 #include "dns_resolve.h"
21 #include "cache_lib.h"
22 
23 #define NFS_DNS_HASHBITS 4
24 #define NFS_DNS_HASHTBL_SIZE (1 << NFS_DNS_HASHBITS)
25 
26 static struct cache_head *nfs_dns_table[NFS_DNS_HASHTBL_SIZE];
27 
28 struct nfs_dns_ent {
29 	struct cache_head h;
30 
31 	char *hostname;
32 	size_t namelen;
33 
34 	struct sockaddr_storage addr;
35 	size_t addrlen;
36 };
37 
38 
39 static void nfs_dns_ent_init(struct cache_head *cnew,
40 		struct cache_head *ckey)
41 {
42 	struct nfs_dns_ent *new;
43 	struct nfs_dns_ent *key;
44 
45 	new = container_of(cnew, struct nfs_dns_ent, h);
46 	key = container_of(ckey, struct nfs_dns_ent, h);
47 
48 	kfree(new->hostname);
49 	new->hostname = kstrndup(key->hostname, key->namelen, GFP_KERNEL);
50 	if (new->hostname) {
51 		new->namelen = key->namelen;
52 		memcpy(&new->addr, &key->addr, key->addrlen);
53 		new->addrlen = key->addrlen;
54 	} else {
55 		new->namelen = 0;
56 		new->addrlen = 0;
57 	}
58 }
59 
60 static void nfs_dns_ent_put(struct kref *ref)
61 {
62 	struct nfs_dns_ent *item;
63 
64 	item = container_of(ref, struct nfs_dns_ent, h.ref);
65 	kfree(item->hostname);
66 	kfree(item);
67 }
68 
69 static struct cache_head *nfs_dns_ent_alloc(void)
70 {
71 	struct nfs_dns_ent *item = kmalloc(sizeof(*item), GFP_KERNEL);
72 
73 	if (item != NULL) {
74 		item->hostname = NULL;
75 		item->namelen = 0;
76 		item->addrlen = 0;
77 		return &item->h;
78 	}
79 	return NULL;
80 };
81 
82 static unsigned int nfs_dns_hash(const struct nfs_dns_ent *key)
83 {
84 	return hash_str(key->hostname, NFS_DNS_HASHBITS);
85 }
86 
87 static void nfs_dns_request(struct cache_detail *cd,
88 		struct cache_head *ch,
89 		char **bpp, int *blen)
90 {
91 	struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
92 
93 	qword_add(bpp, blen, key->hostname);
94 	(*bpp)[-1] = '\n';
95 }
96 
97 static int nfs_dns_upcall(struct cache_detail *cd,
98 		struct cache_head *ch)
99 {
100 	struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
101 	int ret;
102 
103 	ret = nfs_cache_upcall(cd, key->hostname);
104 	if (ret)
105 		ret = sunrpc_cache_pipe_upcall(cd, ch, nfs_dns_request);
106 	return ret;
107 }
108 
109 static int nfs_dns_match(struct cache_head *ca,
110 		struct cache_head *cb)
111 {
112 	struct nfs_dns_ent *a;
113 	struct nfs_dns_ent *b;
114 
115 	a = container_of(ca, struct nfs_dns_ent, h);
116 	b = container_of(cb, struct nfs_dns_ent, h);
117 
118 	if (a->namelen == 0 || a->namelen != b->namelen)
119 		return 0;
120 	return memcmp(a->hostname, b->hostname, a->namelen) == 0;
121 }
122 
123 static int nfs_dns_show(struct seq_file *m, struct cache_detail *cd,
124 		struct cache_head *h)
125 {
126 	struct nfs_dns_ent *item;
127 	long ttl;
128 
129 	if (h == NULL) {
130 		seq_puts(m, "# ip address      hostname        ttl\n");
131 		return 0;
132 	}
133 	item = container_of(h, struct nfs_dns_ent, h);
134 	ttl = (long)item->h.expiry_time - (long)get_seconds();
135 	if (ttl < 0)
136 		ttl = 0;
137 
138 	if (!test_bit(CACHE_NEGATIVE, &h->flags)) {
139 		char buf[INET6_ADDRSTRLEN+IPV6_SCOPE_ID_LEN+1];
140 
141 		rpc_ntop((struct sockaddr *)&item->addr, buf, sizeof(buf));
142 		seq_printf(m, "%15s ", buf);
143 	} else
144 		seq_puts(m, "<none>          ");
145 	seq_printf(m, "%15s %ld\n", item->hostname, ttl);
146 	return 0;
147 }
148 
149 static struct nfs_dns_ent *nfs_dns_lookup(struct cache_detail *cd,
150 		struct nfs_dns_ent *key)
151 {
152 	struct cache_head *ch;
153 
154 	ch = sunrpc_cache_lookup(cd,
155 			&key->h,
156 			nfs_dns_hash(key));
157 	if (!ch)
158 		return NULL;
159 	return container_of(ch, struct nfs_dns_ent, h);
160 }
161 
162 static struct nfs_dns_ent *nfs_dns_update(struct cache_detail *cd,
163 		struct nfs_dns_ent *new,
164 		struct nfs_dns_ent *key)
165 {
166 	struct cache_head *ch;
167 
168 	ch = sunrpc_cache_update(cd,
169 			&new->h, &key->h,
170 			nfs_dns_hash(key));
171 	if (!ch)
172 		return NULL;
173 	return container_of(ch, struct nfs_dns_ent, h);
174 }
175 
176 static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
177 {
178 	char buf1[NFS_DNS_HOSTNAME_MAXLEN+1];
179 	struct nfs_dns_ent key, *item;
180 	unsigned long ttl;
181 	ssize_t len;
182 	int ret = -EINVAL;
183 
184 	if (buf[buflen-1] != '\n')
185 		goto out;
186 	buf[buflen-1] = '\0';
187 
188 	len = qword_get(&buf, buf1, sizeof(buf1));
189 	if (len <= 0)
190 		goto out;
191 	key.addrlen = rpc_pton(buf1, len,
192 			(struct sockaddr *)&key.addr,
193 			sizeof(key.addr));
194 
195 	len = qword_get(&buf, buf1, sizeof(buf1));
196 	if (len <= 0)
197 		goto out;
198 
199 	key.hostname = buf1;
200 	key.namelen = len;
201 	memset(&key.h, 0, sizeof(key.h));
202 
203 	ttl = get_expiry(&buf);
204 	if (ttl == 0)
205 		goto out;
206 	key.h.expiry_time = ttl + get_seconds();
207 
208 	ret = -ENOMEM;
209 	item = nfs_dns_lookup(cd, &key);
210 	if (item == NULL)
211 		goto out;
212 
213 	if (key.addrlen == 0)
214 		set_bit(CACHE_NEGATIVE, &key.h.flags);
215 
216 	item = nfs_dns_update(cd, &key, item);
217 	if (item == NULL)
218 		goto out;
219 
220 	ret = 0;
221 	cache_put(&item->h, cd);
222 out:
223 	return ret;
224 }
225 
226 static struct cache_detail nfs_dns_resolve = {
227 	.owner = THIS_MODULE,
228 	.hash_size = NFS_DNS_HASHTBL_SIZE,
229 	.hash_table = nfs_dns_table,
230 	.name = "dns_resolve",
231 	.cache_put = nfs_dns_ent_put,
232 	.cache_upcall = nfs_dns_upcall,
233 	.cache_parse = nfs_dns_parse,
234 	.cache_show = nfs_dns_show,
235 	.match = nfs_dns_match,
236 	.init = nfs_dns_ent_init,
237 	.update = nfs_dns_ent_init,
238 	.alloc = nfs_dns_ent_alloc,
239 };
240 
241 static int do_cache_lookup(struct cache_detail *cd,
242 		struct nfs_dns_ent *key,
243 		struct nfs_dns_ent **item,
244 		struct nfs_cache_defer_req *dreq)
245 {
246 	int ret = -ENOMEM;
247 
248 	*item = nfs_dns_lookup(cd, key);
249 	if (*item) {
250 		ret = cache_check(cd, &(*item)->h, &dreq->req);
251 		if (ret)
252 			*item = NULL;
253 	}
254 	return ret;
255 }
256 
257 static int do_cache_lookup_nowait(struct cache_detail *cd,
258 		struct nfs_dns_ent *key,
259 		struct nfs_dns_ent **item)
260 {
261 	int ret = -ENOMEM;
262 
263 	*item = nfs_dns_lookup(cd, key);
264 	if (!*item)
265 		goto out_err;
266 	ret = -ETIMEDOUT;
267 	if (!test_bit(CACHE_VALID, &(*item)->h.flags)
268 			|| (*item)->h.expiry_time < get_seconds()
269 			|| cd->flush_time > (*item)->h.last_refresh)
270 		goto out_put;
271 	ret = -ENOENT;
272 	if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags))
273 		goto out_put;
274 	return 0;
275 out_put:
276 	cache_put(&(*item)->h, cd);
277 out_err:
278 	*item = NULL;
279 	return ret;
280 }
281 
282 static int do_cache_lookup_wait(struct cache_detail *cd,
283 		struct nfs_dns_ent *key,
284 		struct nfs_dns_ent **item)
285 {
286 	struct nfs_cache_defer_req *dreq;
287 	int ret = -ENOMEM;
288 
289 	dreq = nfs_cache_defer_req_alloc();
290 	if (!dreq)
291 		goto out;
292 	ret = do_cache_lookup(cd, key, item, dreq);
293 	if (ret == -EAGAIN) {
294 		ret = nfs_cache_wait_for_upcall(dreq);
295 		if (!ret)
296 			ret = do_cache_lookup_nowait(cd, key, item);
297 	}
298 	nfs_cache_defer_req_put(dreq);
299 out:
300 	return ret;
301 }
302 
303 ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
304 		struct sockaddr *sa, size_t salen)
305 {
306 	struct nfs_dns_ent key = {
307 		.hostname = name,
308 		.namelen = namelen,
309 	};
310 	struct nfs_dns_ent *item = NULL;
311 	ssize_t ret;
312 
313 	ret = do_cache_lookup_wait(&nfs_dns_resolve, &key, &item);
314 	if (ret == 0) {
315 		if (salen >= item->addrlen) {
316 			memcpy(sa, &item->addr, item->addrlen);
317 			ret = item->addrlen;
318 		} else
319 			ret = -EOVERFLOW;
320 		cache_put(&item->h, &nfs_dns_resolve);
321 	} else if (ret == -ENOENT)
322 		ret = -ESRCH;
323 	return ret;
324 }
325 
326 int nfs_dns_resolver_init(void)
327 {
328 	return nfs_cache_register(&nfs_dns_resolve);
329 }
330 
331 void nfs_dns_resolver_destroy(void)
332 {
333 	nfs_cache_unregister(&nfs_dns_resolve);
334 }
335 
336