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