Skip to content

Commit 4a2d789

Browse files
weylanSteve French
authored and
Steve French
committed
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>
1 parent c2e8139 commit 4a2d789

File tree

3 files changed

+96
-5
lines changed

3 files changed

+96
-5
lines changed

fs/afs/cell.c

+4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ static struct afs_cell *afs_cell_alloc(const char *name, char *vllist)
7373
if (!vllist || strlen(vllist) < 7) {
7474
ret = dns_query("afsdb", name, namelen, "ipv4", &dvllist, NULL);
7575
if (ret < 0) {
76+
if (ret == -ENODATA || ret == -EAGAIN || ret == -ENOKEY)
77+
/* translate these errors into something
78+
* userspace might understand */
79+
ret = -EDESTADDRREQ;
7680
_leave(" = %d", ret);
7781
return ERR_PTR(ret);
7882
}

net/dns_resolver/dns_key.c

+87-5
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <linux/kernel.h>
3030
#include <linux/keyctl.h>
3131
#include <linux/err.h>
32+
#include <linux/seq_file.h>
3233
#include <keys/dns_resolver-type.h>
3334
#include <keys/user-type.h>
3435
#include "internal.h"
@@ -43,6 +44,8 @@ MODULE_PARM_DESC(debug, "DNS Resolver debugging mask");
4344

4445
const struct cred *dns_resolver_cache;
4546

47+
#define DNS_ERRORNO_OPTION "dnserror"
48+
4649
/*
4750
* Instantiate a user defined key for dns_resolver.
4851
*
@@ -59,9 +62,10 @@ static int
5962
dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)
6063
{
6164
struct user_key_payload *upayload;
65+
unsigned long derrno;
6266
int ret;
6367
size_t result_len = 0;
64-
const char *data = _data, *opt;
68+
const char *data = _data, *end, *opt;
6569

6670
kenter("%%%d,%s,'%s',%zu",
6771
key->serial, key->description, data, datalen);
@@ -71,13 +75,77 @@ dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)
7175
datalen--;
7276

7377
/* deal with any options embedded in the data */
78+
end = data + datalen;
7479
opt = memchr(data, '#', datalen);
7580
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);
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;
78146
}
79147

80-
result_len = datalen;
148+
kdebug("store result");
81149
ret = key_payload_reserve(key, result_len);
82150
if (ret < 0)
83151
return -EINVAL;
@@ -135,13 +203,27 @@ dns_resolver_match(const struct key *key, const void *description)
135203
return ret;
136204
}
137205

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+
138220
struct key_type key_type_dns_resolver = {
139221
.name = "dns_resolver",
140222
.instantiate = dns_resolver_instantiate,
141223
.match = dns_resolver_match,
142224
.revoke = user_revoke,
143225
.destroy = user_destroy,
144-
.describe = user_describe,
226+
.describe = dns_resolver_describe,
145227
.read = user_read,
146228
};
147229

net/dns_resolver/dns_query.c

+5
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ int dns_query(const char *type, const char *name, size_t namelen,
136136
if (ret < 0)
137137
goto put;
138138

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+
139144
upayload = rcu_dereference_protected(rkey->payload.data,
140145
lockdep_is_held(&rkey->sem));
141146
len = upayload->datalen;

0 commit comments

Comments
 (0)