Commit 4a2d789267e00b5a1175ecd2ddefcc78b83fbf09

Authored by Wang Lei
Committed by Steve French
1 parent c2e8139c9f

DNS: If the DNS server returns an error, allow that to be cached [ver #2]

If the DNS server returns an error, allow that to be cached in the DNS resolver
key in lieu of a value.  Userspace passes the desired error number as an option
in the payload:

	"#dnserror=<number>"

Userspace must map h_errno from the name resolution routines to an appropriate
Linux error before passing it up.  Something like the following mapping is
recommended:

	[HOST_NOT_FOUND]	= ENODATA,
	[TRY_AGAIN]		= EAGAIN,
	[NO_RECOVERY]		= ECONNREFUSED,
	[NO_DATA]		= ENODATA,

in lieu of Linux errors specifically for representing name service errors.  The
filesystem must map these errors appropropriately before passing them to
userspace.  AFS is made to map ENODATA and EAGAIN to EDESTADDRREQ for the
return to userspace; ECONNREFUSED is allowed to stand as is.

The error can be seen in /proc/keys as a negative number after the description
of the key.  Compare, for example, the following key entries:

2f97238c I--Q--     1  53s 3f010000     0     0 dns_resol afsdb:grand.centrall.org: -61
338bfbbe I--Q--     1  59m 3f010000     0     0 dns_resol afsdb:grand.central.org: 37

If the error option is supplied in the payload, the main part of the payload is
discarded.  The key should have an expiry time set by userspace.

Signed-off-by: Wang Lei <wang840925@gmail.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>

Showing 3 changed files with 96 additions and 5 deletions Side-by-side Diff

... ... @@ -73,6 +73,10 @@
73 73 if (!vllist || strlen(vllist) < 7) {
74 74 ret = dns_query("afsdb", name, namelen, "ipv4", &dvllist, NULL);
75 75 if (ret < 0) {
  76 + if (ret == -ENODATA || ret == -EAGAIN || ret == -ENOKEY)
  77 + /* translate these errors into something
  78 + * userspace might understand */
  79 + ret = -EDESTADDRREQ;
76 80 _leave(" = %d", ret);
77 81 return ERR_PTR(ret);
78 82 }
net/dns_resolver/dns_key.c
... ... @@ -29,6 +29,7 @@
29 29 #include <linux/kernel.h>
30 30 #include <linux/keyctl.h>
31 31 #include <linux/err.h>
  32 +#include <linux/seq_file.h>
32 33 #include <keys/dns_resolver-type.h>
33 34 #include <keys/user-type.h>
34 35 #include "internal.h"
... ... @@ -43,6 +44,8 @@
43 44  
44 45 const struct cred *dns_resolver_cache;
45 46  
  47 +#define DNS_ERRORNO_OPTION "dnserror"
  48 +
46 49 /*
47 50 * Instantiate a user defined key for dns_resolver.
48 51 *
49 52  
... ... @@ -59,9 +62,10 @@
59 62 dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)
60 63 {
61 64 struct user_key_payload *upayload;
  65 + unsigned long derrno;
62 66 int ret;
63 67 size_t result_len = 0;
64   - const char *data = _data, *opt;
  68 + const char *data = _data, *end, *opt;
65 69  
66 70 kenter("%%%d,%s,'%s',%zu",
67 71 key->serial, key->description, data, datalen);
68 72  
69 73  
... ... @@ -71,13 +75,77 @@
71 75 datalen--;
72 76  
73 77 /* deal with any options embedded in the data */
  78 + end = data + datalen;
74 79 opt = memchr(data, '#', datalen);
75 80 if (!opt) {
76   - kdebug("no options currently supported");
77   - return -EINVAL;
  81 + /* no options: the entire data is the result */
  82 + kdebug("no options");
  83 + result_len = datalen;
  84 + } else {
  85 + const char *next_opt;
  86 +
  87 + result_len = opt - data;
  88 + opt++;
  89 + kdebug("options: '%s'", opt);
  90 + do {
  91 + const char *eq;
  92 + int opt_len, opt_nlen, opt_vlen, tmp;
  93 +
  94 + next_opt = memchr(opt, '#', end - opt) ?: end;
  95 + opt_len = next_opt - opt;
  96 + if (!opt_len) {
  97 + printk(KERN_WARNING
  98 + "Empty option to dns_resolver key %d\n",
  99 + key->serial);
  100 + return -EINVAL;
  101 + }
  102 +
  103 + eq = memchr(opt, '=', opt_len) ?: end;
  104 + opt_nlen = eq - opt;
  105 + eq++;
  106 + opt_vlen = next_opt - eq; /* will be -1 if no value */
  107 +
  108 + tmp = opt_vlen >= 0 ? opt_vlen : 0;
  109 + kdebug("option '%*.*s' val '%*.*s'",
  110 + opt_nlen, opt_nlen, opt, tmp, tmp, eq);
  111 +
  112 + /* see if it's an error number representing a DNS error
  113 + * that's to be recorded as the result in this key */
  114 + if (opt_nlen == sizeof(DNS_ERRORNO_OPTION) - 1 &&
  115 + memcmp(opt, DNS_ERRORNO_OPTION, opt_nlen) == 0) {
  116 + kdebug("dns error number option");
  117 + if (opt_vlen <= 0)
  118 + goto bad_option_value;
  119 +
  120 + ret = strict_strtoul(eq, 10, &derrno);
  121 + if (ret < 0)
  122 + goto bad_option_value;
  123 +
  124 + if (derrno < 1 || derrno > 511)
  125 + goto bad_option_value;
  126 +
  127 + kdebug("dns error no. = %lu", derrno);
  128 + key->type_data.x[0] = -derrno;
  129 + continue;
  130 + }
  131 +
  132 + bad_option_value:
  133 + printk(KERN_WARNING
  134 + "Option '%*.*s' to dns_resolver key %d:"
  135 + " bad/missing value\n",
  136 + opt_nlen, opt_nlen, opt, key->serial);
  137 + return -EINVAL;
  138 + } while (opt = next_opt + 1, opt < end);
78 139 }
79 140  
80   - result_len = datalen;
  141 + /* don't cache the result if we're caching an error saying there's no
  142 + * result */
  143 + if (key->type_data.x[0]) {
  144 + kleave(" = 0 [h_error %ld]", key->type_data.x[0]);
  145 + return 0;
  146 + }
  147 +
  148 + kdebug("store result");
81 149 ret = key_payload_reserve(key, result_len);
82 150 if (ret < 0)
83 151 return -EINVAL;
84 152  
... ... @@ -135,13 +203,27 @@
135 203 return ret;
136 204 }
137 205  
  206 +/*
  207 + * Describe a DNS key
  208 + */
  209 +static void dns_resolver_describe(const struct key *key, struct seq_file *m)
  210 +{
  211 + int err = key->type_data.x[0];
  212 +
  213 + seq_puts(m, key->description);
  214 + if (err)
  215 + seq_printf(m, ": %d", err);
  216 + else
  217 + seq_printf(m, ": %u", key->datalen);
  218 +}
  219 +
138 220 struct key_type key_type_dns_resolver = {
139 221 .name = "dns_resolver",
140 222 .instantiate = dns_resolver_instantiate,
141 223 .match = dns_resolver_match,
142 224 .revoke = user_revoke,
143 225 .destroy = user_destroy,
144   - .describe = user_describe,
  226 + .describe = dns_resolver_describe,
145 227 .read = user_read,
146 228 };
147 229  
net/dns_resolver/dns_query.c
... ... @@ -136,6 +136,11 @@
136 136 if (ret < 0)
137 137 goto put;
138 138  
  139 + /* If the DNS server gave an error, return that to the caller */
  140 + ret = rkey->type_data.x[0];
  141 + if (ret)
  142 + goto put;
  143 +
139 144 upayload = rcu_dereference_protected(rkey->payload.data,
140 145 lockdep_is_held(&rkey->sem));
141 146 len = upayload->datalen;