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