]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/dns_resolve.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / common / dns_resolve.cc
CommitLineData
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
24namespace ceph {
25
26#ifdef HAVE_RES_NQUERY
27
28int 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
33int 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
40int 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
45int 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
52DNSResolver::~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
65int 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
87void 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
94int 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
190int 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
209int 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
264int 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
270int 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}