linux/fs/nfs/dns_resolve.c
<<
>>
Prefs
   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
  14ssize_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
  50static struct cache_head *nfs_dns_table[NFS_DNS_HASHTBL_SIZE];
  51
  52struct 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
  63static 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
  76static 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
  96static 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
 105static 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
 118static unsigned int nfs_dns_hash(const struct nfs_dns_ent *key)
 119{
 120        return hash_str(key->hostname, NFS_DNS_HASHBITS);
 121}
 122
 123static 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
 133static 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
 145static 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
 159static 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
 185static 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
 198static 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
 212static 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);
 258out:
 259        return ret;
 260}
 261
 262static 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
 277static 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
 293static 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;
 311out_put:
 312        cache_put(&(*item)->h, cd);
 313out_err:
 314        *item = NULL;
 315        return ret;
 316}
 317
 318static 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);
 335out:
 336        return ret;
 337}
 338
 339ssize_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
 362int nfs_dns_resolver_init(void)
 363{
 364        return nfs_cache_register(&nfs_dns_resolve);
 365}
 366
 367void nfs_dns_resolver_destroy(void)
 368{
 369        nfs_cache_unregister(&nfs_dns_resolve);
 370}
 371
 372#endif
 373