]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2016 SUSE LINUX GmbH | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
7c673cae | 15 | #include <arpa/inet.h> |
7c673cae | 16 | |
224ce89b | 17 | #include "include/scope_guard.h" |
7c673cae | 18 | #include "dns_resolve.h" |
7c673cae | 19 | #include "common/debug.h" |
7c673cae FG |
20 | |
21 | #define dout_subsys ceph_subsys_ | |
22 | ||
7c673cae FG |
23 | |
24 | namespace ceph { | |
25 | ||
26 | #ifdef HAVE_RES_NQUERY | |
27 | ||
28 | int ResolvHWrapper::res_nquery(res_state s, const char *hostname, int cls, | |
29 | int type, u_char *buf, int bufsz) { | |
30 | return ::res_nquery(s, hostname, cls, type, buf, bufsz); | |
31 | } | |
32 | ||
33 | int ResolvHWrapper::res_nsearch(res_state s, const char *hostname, int cls, | |
34 | int type, u_char *buf, int bufsz) { | |
35 | return ::res_nsearch(s, hostname, cls, type, buf, bufsz); | |
36 | } | |
37 | ||
38 | #else | |
39 | ||
40 | int ResolvHWrapper::res_query(const char *hostname, int cls, | |
41 | int type, u_char *buf, int bufsz) { | |
42 | return ::res_query(hostname, cls, type, buf, bufsz); | |
43 | } | |
44 | ||
45 | int ResolvHWrapper::res_search(const char *hostname, int cls, | |
46 | int type, u_char *buf, int bufsz) { | |
47 | return ::res_search(hostname, cls, type, buf, bufsz); | |
48 | } | |
49 | ||
50 | #endif | |
51 | ||
52 | DNSResolver::~DNSResolver() | |
53 | { | |
54 | #ifdef HAVE_RES_NQUERY | |
55 | list<res_state>::iterator iter; | |
56 | for (iter = states.begin(); iter != states.end(); ++iter) { | |
57 | struct __res_state *s = *iter; | |
58 | delete s; | |
59 | } | |
60 | #endif | |
61 | delete resolv_h; | |
62 | } | |
63 | ||
64 | #ifdef HAVE_RES_NQUERY | |
65 | int DNSResolver::get_state(CephContext *cct, res_state *ps) | |
66 | { | |
11fdf7f2 | 67 | lock.lock(); |
7c673cae FG |
68 | if (!states.empty()) { |
69 | res_state s = states.front(); | |
70 | states.pop_front(); | |
11fdf7f2 | 71 | lock.unlock(); |
7c673cae FG |
72 | *ps = s; |
73 | return 0; | |
74 | } | |
11fdf7f2 | 75 | lock.unlock(); |
7c673cae FG |
76 | struct __res_state *s = new struct __res_state; |
77 | s->options = 0; | |
78 | if (res_ninit(s) < 0) { | |
79 | delete s; | |
80 | lderr(cct) << "ERROR: failed to call res_ninit()" << dendl; | |
81 | return -EINVAL; | |
82 | } | |
83 | *ps = s; | |
84 | return 0; | |
85 | } | |
86 | ||
87 | void DNSResolver::put_state(res_state s) | |
88 | { | |
11fdf7f2 | 89 | std::lock_guard l(lock); |
7c673cae FG |
90 | states.push_back(s); |
91 | } | |
92 | #endif | |
93 | ||
94 | int DNSResolver::resolve_cname(CephContext *cct, const string& hostname, | |
95 | string *cname, bool *found) | |
96 | { | |
97 | *found = false; | |
98 | ||
99 | #ifdef HAVE_RES_NQUERY | |
100 | res_state res; | |
101 | int r = get_state(cct, &res); | |
102 | if (r < 0) { | |
103 | return r; | |
104 | } | |
224ce89b WB |
105 | auto put_state = make_scope_guard([res, this] { |
106 | this->put_state(res); | |
107 | }); | |
7c673cae FG |
108 | #endif |
109 | ||
7c673cae FG |
110 | #define LARGE_ENOUGH_DNS_BUFSIZE 1024 |
111 | unsigned char buf[LARGE_ENOUGH_DNS_BUFSIZE]; | |
112 | ||
113 | #define MAX_FQDN_SIZE 255 | |
114 | char host[MAX_FQDN_SIZE + 1]; | |
115 | const char *origname = hostname.c_str(); | |
116 | unsigned char *pt, *answer; | |
117 | unsigned char *answend; | |
118 | int len; | |
119 | ||
120 | #ifdef HAVE_RES_NQUERY | |
121 | len = resolv_h->res_nquery(res, origname, ns_c_in, ns_t_cname, buf, sizeof(buf)); | |
122 | #else | |
123 | { | |
124 | # ifndef HAVE_THREAD_SAFE_RES_QUERY | |
11fdf7f2 | 125 | std::lock_guard l(lock); |
7c673cae FG |
126 | # endif |
127 | len = resolv_h->res_query(origname, ns_c_in, ns_t_cname, buf, sizeof(buf)); | |
128 | } | |
129 | #endif | |
130 | if (len < 0) { | |
131 | lderr(cct) << "res_query() failed" << dendl; | |
224ce89b | 132 | return 0; |
7c673cae FG |
133 | } |
134 | ||
135 | answer = buf; | |
136 | pt = answer + NS_HFIXEDSZ; | |
137 | answend = answer + len; | |
138 | ||
139 | /* read query */ | |
140 | if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) { | |
141 | lderr(cct) << "ERROR: dn_expand() failed" << dendl; | |
224ce89b | 142 | return -EINVAL; |
7c673cae FG |
143 | } |
144 | pt += len; | |
145 | ||
146 | if (pt + 4 > answend) { | |
147 | lderr(cct) << "ERROR: bad reply" << dendl; | |
224ce89b | 148 | return -EIO; |
7c673cae FG |
149 | } |
150 | ||
151 | int type; | |
152 | NS_GET16(type, pt); | |
153 | ||
154 | if (type != ns_t_cname) { | |
155 | lderr(cct) << "ERROR: failed response type: type=" << type << | |
156 | " (was expecting " << ns_t_cname << ")" << dendl; | |
224ce89b | 157 | return -EIO; |
7c673cae FG |
158 | } |
159 | ||
160 | pt += NS_INT16SZ; /* class */ | |
161 | ||
162 | /* read answer */ | |
163 | if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) { | |
224ce89b | 164 | return 0; |
7c673cae FG |
165 | } |
166 | pt += len; | |
167 | ldout(cct, 20) << "name=" << host << dendl; | |
168 | ||
169 | if (pt + 10 > answend) { | |
170 | lderr(cct) << "ERROR: bad reply" << dendl; | |
224ce89b | 171 | return -EIO; |
7c673cae FG |
172 | } |
173 | ||
174 | NS_GET16(type, pt); | |
175 | pt += NS_INT16SZ; /* class */ | |
176 | pt += NS_INT32SZ; /* ttl */ | |
177 | pt += NS_INT16SZ; /* size */ | |
178 | ||
179 | if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) { | |
224ce89b | 180 | return 0; |
7c673cae FG |
181 | } |
182 | ldout(cct, 20) << "cname host=" << host << dendl; | |
183 | *cname = host; | |
184 | ||
185 | *found = true; | |
224ce89b | 186 | return 0; |
7c673cae FG |
187 | } |
188 | ||
189 | ||
190 | int DNSResolver::resolve_ip_addr(CephContext *cct, const string& hostname, | |
191 | entity_addr_t *addr) { | |
192 | ||
193 | #ifdef HAVE_RES_NQUERY | |
194 | res_state res; | |
195 | int r = get_state(cct, &res); | |
196 | if (r < 0) { | |
197 | return r; | |
198 | } | |
b32b8144 FG |
199 | auto put_state = make_scope_guard([res, this] { |
200 | this->put_state(res); | |
201 | }); | |
7c673cae FG |
202 | return this->resolve_ip_addr(cct, &res, hostname, addr); |
203 | #else | |
204 | return this->resolve_ip_addr(cct, NULL, hostname, addr); | |
205 | #endif | |
206 | ||
207 | } | |
208 | ||
209 | int DNSResolver::resolve_ip_addr(CephContext *cct, res_state *res, const string& hostname, | |
210 | entity_addr_t *addr) { | |
211 | ||
212 | u_char nsbuf[NS_PACKETSZ]; | |
213 | int len; | |
94b18763 FG |
214 | int family = cct->_conf->ms_bind_ipv6 ? AF_INET6 : AF_INET; |
215 | int type = cct->_conf->ms_bind_ipv6 ? ns_t_aaaa : ns_t_a; | |
7c673cae FG |
216 | |
217 | #ifdef HAVE_RES_NQUERY | |
94b18763 | 218 | len = resolv_h->res_nquery(*res, hostname.c_str(), ns_c_in, type, nsbuf, sizeof(nsbuf)); |
7c673cae FG |
219 | #else |
220 | { | |
221 | # ifndef HAVE_THREAD_SAFE_RES_QUERY | |
11fdf7f2 | 222 | std::lock_guard l(lock); |
7c673cae | 223 | # endif |
94b18763 | 224 | len = resolv_h->res_query(hostname.c_str(), ns_c_in, type, nsbuf, sizeof(nsbuf)); |
7c673cae FG |
225 | } |
226 | #endif | |
227 | if (len < 0) { | |
228 | lderr(cct) << "res_query() failed" << dendl; | |
229 | return len; | |
230 | } | |
231 | else if (len == 0) { | |
232 | ldout(cct, 20) << "no address found for hostname " << hostname << dendl; | |
233 | return -1; | |
234 | } | |
235 | ||
236 | ns_msg handle; | |
237 | ns_initparse(nsbuf, len, &handle); | |
238 | ||
239 | if (ns_msg_count(handle, ns_s_an) == 0) { | |
240 | ldout(cct, 20) << "no address found for hostname " << hostname << dendl; | |
241 | return -1; | |
242 | } | |
243 | ||
244 | ns_rr rr; | |
245 | int r; | |
246 | if ((r = ns_parserr(&handle, ns_s_an, 0, &rr)) < 0) { | |
247 | lderr(cct) << "error while parsing DNS record" << dendl; | |
248 | return r; | |
249 | } | |
250 | ||
251 | char addr_buf[64]; | |
92f5a8d4 | 252 | // FIPS zeroization audit 20191115: this memset is not security related. |
7c673cae | 253 | memset(addr_buf, 0, sizeof(addr_buf)); |
94b18763 | 254 | inet_ntop(family, ns_rr_rdata(rr), addr_buf, sizeof(addr_buf)); |
7c673cae FG |
255 | if (!addr->parse(addr_buf)) { |
256 | lderr(cct) << "failed to parse address '" << (const char *)ns_rr_rdata(rr) | |
257 | << "'" << dendl; | |
258 | return -1; | |
259 | } | |
260 | ||
261 | return 0; | |
262 | } | |
263 | ||
264 | int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name, | |
224ce89b WB |
265 | const SRV_Protocol trans_protocol, |
266 | map<string, DNSResolver::Record> *srv_hosts) { | |
7c673cae FG |
267 | return this->resolve_srv_hosts(cct, service_name, trans_protocol, "", srv_hosts); |
268 | } | |
269 | ||
270 | int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name, | |
271 | const SRV_Protocol trans_protocol, const string& domain, | |
224ce89b | 272 | map<string, DNSResolver::Record> *srv_hosts) { |
7c673cae FG |
273 | |
274 | #ifdef HAVE_RES_NQUERY | |
275 | res_state res; | |
276 | int r = get_state(cct, &res); | |
277 | if (r < 0) { | |
278 | return r; | |
279 | } | |
224ce89b WB |
280 | auto put_state = make_scope_guard([res, this] { |
281 | this->put_state(res); | |
282 | }); | |
7c673cae FG |
283 | #endif |
284 | ||
7c673cae FG |
285 | u_char nsbuf[NS_PACKETSZ]; |
286 | int num_hosts; | |
287 | ||
288 | string proto_str = srv_protocol_to_str(trans_protocol); | |
289 | string query_str = "_"+service_name+"._"+proto_str+(domain.empty() ? "" | |
290 | : "."+domain); | |
291 | int len; | |
292 | ||
293 | #ifdef HAVE_RES_NQUERY | |
294 | len = resolv_h->res_nsearch(res, query_str.c_str(), ns_c_in, ns_t_srv, nsbuf, | |
295 | sizeof(nsbuf)); | |
296 | #else | |
297 | { | |
298 | # ifndef HAVE_THREAD_SAFE_RES_QUERY | |
11fdf7f2 | 299 | std::lock_guard l(lock); |
7c673cae FG |
300 | # endif |
301 | len = resolv_h->res_search(query_str.c_str(), ns_c_in, ns_t_srv, nsbuf, | |
302 | sizeof(nsbuf)); | |
303 | } | |
304 | #endif | |
305 | if (len < 0) { | |
224ce89b WB |
306 | lderr(cct) << "failed for service " << query_str << dendl; |
307 | return len; | |
7c673cae FG |
308 | } |
309 | else if (len == 0) { | |
310 | ldout(cct, 20) << "No hosts found for service " << query_str << dendl; | |
224ce89b | 311 | return 0; |
7c673cae FG |
312 | } |
313 | ||
314 | ns_msg handle; | |
315 | ||
316 | ns_initparse(nsbuf, len, &handle); | |
317 | ||
318 | num_hosts = ns_msg_count (handle, ns_s_an); | |
319 | if (num_hosts == 0) { | |
320 | ldout(cct, 20) << "No hosts found for service " << query_str << dendl; | |
224ce89b | 321 | return 0; |
7c673cae FG |
322 | } |
323 | ||
324 | ns_rr rr; | |
325 | char full_target[NS_MAXDNAME]; | |
326 | ||
327 | for (int i = 0; i < num_hosts; i++) { | |
328 | int r; | |
329 | if ((r = ns_parserr(&handle, ns_s_an, i, &rr)) < 0) { | |
330 | lderr(cct) << "Error while parsing DNS record" << dendl; | |
224ce89b | 331 | return r; |
7c673cae FG |
332 | } |
333 | ||
334 | string full_srv_name = ns_rr_name(rr); | |
335 | string protocol = "_" + proto_str; | |
336 | string srv_domain = full_srv_name.substr(full_srv_name.find(protocol) | |
337 | + protocol.length()); | |
338 | ||
224ce89b WB |
339 | auto rdata = ns_rr_rdata(rr); |
340 | uint16_t priority = ns_get16(rdata); rdata += NS_INT16SZ; | |
9f95a23c | 341 | uint16_t weight = ns_get16(rdata); rdata += NS_INT16SZ; |
224ce89b | 342 | uint16_t port = ns_get16(rdata); rdata += NS_INT16SZ; |
92f5a8d4 | 343 | // FIPS zeroization audit 20191115: this memset is not security related. |
7c673cae FG |
344 | memset(full_target, 0, sizeof(full_target)); |
345 | ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | |
224ce89b | 346 | rdata, full_target, sizeof(full_target)); |
7c673cae FG |
347 | |
348 | entity_addr_t addr; | |
349 | #ifdef HAVE_RES_NQUERY | |
350 | r = this->resolve_ip_addr(cct, &res, full_target, &addr); | |
351 | #else | |
352 | r = this->resolve_ip_addr(cct, NULL, full_target, &addr); | |
353 | #endif | |
354 | ||
355 | if (r == 0) { | |
356 | addr.set_port(port); | |
357 | string target = full_target; | |
224ce89b WB |
358 | auto end = target.find(srv_domain); |
359 | if (end == target.npos) { | |
360 | lderr(cct) << "resolved target not in search domain: " | |
361 | << target << " / " << srv_domain << dendl; | |
362 | return -EINVAL; | |
363 | } | |
364 | target = target.substr(0, end); | |
9f95a23c | 365 | (*srv_hosts)[target] = {priority, weight, addr}; |
7c673cae | 366 | } |
7c673cae | 367 | } |
224ce89b | 368 | return 0; |
7c673cae FG |
369 | } |
370 | ||
371 | } |