diff options
-rw-r--r-- | fs/afs/cell.c | 4 | ||||
-rw-r--r-- | net/dns_resolver/dns_key.c | 92 | ||||
-rw-r--r-- | net/dns_resolver/dns_query.c | 5 |
3 files changed, 96 insertions, 5 deletions
diff --git a/fs/afs/cell.c b/fs/afs/cell.c index ffea35c63879..d0765883430e 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c | |||
@@ -73,6 +73,10 @@ static struct afs_cell *afs_cell_alloc(const char *name, char *vllist) | |||
73 | if (!vllist || strlen(vllist) < 7) { | 73 | if (!vllist || strlen(vllist) < 7) { |
74 | ret = dns_query("afsdb", name, namelen, "ipv4", &dvllist, NULL); | 74 | ret = dns_query("afsdb", name, namelen, "ipv4", &dvllist, NULL); |
75 | if (ret < 0) { | 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 | _leave(" = %d", ret); | 80 | _leave(" = %d", ret); |
77 | return ERR_PTR(ret); | 81 | return ERR_PTR(ret); |
78 | } | 82 | } |
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 | ||
44 | const struct cred *dns_resolver_cache; | 45 | const 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 | |||
59 | dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen) | 62 | dns_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 | */ | ||
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 | struct key_type key_type_dns_resolver = { | 220 | struct 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; |