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