aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorWang Lei <wang840925@gmail.com>2010-08-11 04:37:58 -0400
committerSteve French <sfrench@us.ibm.com>2010-08-11 13:11:28 -0400
commit4a2d789267e00b5a1175ecd2ddefcc78b83fbf09 (patch)
treeee0372ca2719a998fca29e6fe3b9c0a3ceabbfc7 /net
parentc2e8139c9f797baa46515af6d350c51823736cbb (diff)
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>
Diffstat (limited to 'net')
-rw-r--r--net/dns_resolver/dns_key.c92
-rw-r--r--net/dns_resolver/dns_query.c5
2 files changed, 92 insertions, 5 deletions
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
index 400a04d5c9a1..739435a6af39 100644
--- a/net/dns_resolver/dns_key.c
+++ b/net/dns_resolver/dns_key.c
@@ -29,6 +29,7 @@
29#include <linux/kernel.h> 29#include <linux/kernel.h>
30#include <linux/keyctl.h> 30#include <linux/keyctl.h>
31#include <linux/err.h> 31#include <linux/err.h>
32#include <linux/seq_file.h>
32#include <keys/dns_resolver-type.h> 33#include <keys/dns_resolver-type.h>
33#include <keys/user-type.h> 34#include <keys/user-type.h>
34#include "internal.h" 35#include "internal.h"
@@ -43,6 +44,8 @@ MODULE_PARM_DESC(debug, "DNS Resolver debugging mask");
43 44
44const struct cred *dns_resolver_cache; 45const struct cred *dns_resolver_cache;
45 46
47#define DNS_ERRORNO_OPTION "dnserror"
48
46/* 49/*
47 * Instantiate a user defined key for dns_resolver. 50 * Instantiate a user defined key for dns_resolver.
48 * 51 *
@@ -59,9 +62,10 @@ static int
59dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen) 62dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)
60{ 63{
61 struct user_key_payload *upayload; 64 struct user_key_payload *upayload;
65 unsigned long derrno;
62 int ret; 66 int ret;
63 size_t result_len = 0; 67 size_t result_len = 0;
64 const char *data = _data, *opt; 68 const char *data = _data, *end, *opt;
65 69
66 kenter("%%%d,%s,'%s',%zu", 70 kenter("%%%d,%s,'%s',%zu",
67 key->serial, key->description, data, datalen); 71 key->serial, key->description, data, datalen);
@@ -71,13 +75,77 @@ dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)
71 datalen--; 75 datalen--;
72 76
73 /* deal with any options embedded in the data */ 77 /* deal with any options embedded in the data */
78 end = data + datalen;
74 opt = memchr(data, '#', datalen); 79 opt = memchr(data, '#', datalen);
75 if (!opt) { 80 if (!opt) {
76 kdebug("no options currently supported"); 81 /* no options: the entire data is the result */
77 return -EINVAL; 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);
139 }
140
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;
78 } 146 }
79 147
80 result_len = datalen; 148 kdebug("store result");
81 ret = key_payload_reserve(key, result_len); 149 ret = key_payload_reserve(key, result_len);
82 if (ret < 0) 150 if (ret < 0)
83 return -EINVAL; 151 return -EINVAL;
@@ -135,13 +203,27 @@ no_match:
135 return ret; 203 return ret;
136} 204}
137 205
206/*
207 * Describe a DNS key
208 */
209static 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
138struct key_type key_type_dns_resolver = { 220struct key_type key_type_dns_resolver = {
139 .name = "dns_resolver", 221 .name = "dns_resolver",
140 .instantiate = dns_resolver_instantiate, 222 .instantiate = dns_resolver_instantiate,
141 .match = dns_resolver_match, 223 .match = dns_resolver_match,
142 .revoke = user_revoke, 224 .revoke = user_revoke,
143 .destroy = user_destroy, 225 .destroy = user_destroy,
144 .describe = user_describe, 226 .describe = dns_resolver_describe,
145 .read = user_read, 227 .read = user_read,
146}; 228};
147 229
diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c
index 03d5255f5cf2..c32be292c7e3 100644
--- a/net/dns_resolver/dns_query.c
+++ b/net/dns_resolver/dns_query.c
@@ -136,6 +136,11 @@ int dns_query(const char *type, const char *name, size_t namelen,
136 if (ret < 0) 136 if (ret < 0)
137 goto put; 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 upayload = rcu_dereference_protected(rkey->payload.data, 144 upayload = rcu_dereference_protected(rkey->payload.data,
140 lockdep_is_held(&rkey->sem)); 145 lockdep_is_held(&rkey->sem));
141 len = upayload->datalen; 146 len = upayload->datalen;