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