1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2016 SUSE LINUX GmbH
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.
15 #include <arpa/inet.h>
17 #include "include/scope_guard.h"
18 #include "dns_resolve.h"
19 #include "common/debug.h"
21 #define dout_subsys ceph_subsys_
28 #ifdef HAVE_RES_NQUERY
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
);
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
);
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
);
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
);
54 DNSResolver::~DNSResolver()
56 #ifdef HAVE_RES_NQUERY
57 for (auto iter
= states
.begin(); iter
!= states
.end(); ++iter
) {
58 struct __res_state
*s
= *iter
;
65 #ifdef HAVE_RES_NQUERY
66 int DNSResolver::get_state(CephContext
*cct
, res_state
*ps
)
69 if (!states
.empty()) {
70 res_state s
= states
.front();
77 struct __res_state
*s
= new struct __res_state
;
79 if (res_ninit(s
) < 0) {
81 lderr(cct
) << "ERROR: failed to call res_ninit()" << dendl
;
88 void DNSResolver::put_state(res_state s
)
90 std::lock_guard
l(lock
);
95 int DNSResolver::resolve_cname(CephContext
*cct
, const string
& hostname
,
96 string
*cname
, bool *found
)
100 #ifdef HAVE_RES_NQUERY
102 int r
= get_state(cct
, &res
);
106 auto put_state
= make_scope_guard([res
, this] {
107 this->put_state(res
);
111 #define LARGE_ENOUGH_DNS_BUFSIZE 1024
112 unsigned char buf
[LARGE_ENOUGH_DNS_BUFSIZE
];
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
;
121 #ifdef HAVE_RES_NQUERY
122 len
= resolv_h
->res_nquery(res
, origname
, ns_c_in
, ns_t_cname
, buf
, sizeof(buf
));
125 # ifndef HAVE_THREAD_SAFE_RES_QUERY
126 std::lock_guard
l(lock
);
128 len
= resolv_h
->res_query(origname
, ns_c_in
, ns_t_cname
, buf
, sizeof(buf
));
132 lderr(cct
) << "res_query() failed" << dendl
;
137 pt
= answer
+ NS_HFIXEDSZ
;
138 answend
= answer
+ len
;
141 if ((len
= dn_expand(answer
, answend
, pt
, host
, sizeof(host
))) < 0) {
142 lderr(cct
) << "ERROR: dn_expand() failed" << dendl
;
147 if (pt
+ 4 > answend
) {
148 lderr(cct
) << "ERROR: bad reply" << dendl
;
155 if (type
!= ns_t_cname
) {
156 lderr(cct
) << "ERROR: failed response type: type=" << type
<<
157 " (was expecting " << ns_t_cname
<< ")" << dendl
;
161 pt
+= NS_INT16SZ
; /* class */
164 if ((len
= dn_expand(answer
, answend
, pt
, host
, sizeof(host
))) < 0) {
168 ldout(cct
, 20) << "name=" << host
<< dendl
;
170 if (pt
+ 10 > answend
) {
171 lderr(cct
) << "ERROR: bad reply" << dendl
;
176 pt
+= NS_INT16SZ
; /* class */
177 pt
+= NS_INT32SZ
; /* ttl */
178 pt
+= NS_INT16SZ
; /* size */
180 if ((len
= dn_expand(answer
, answend
, pt
, host
, sizeof(host
))) < 0) {
183 ldout(cct
, 20) << "cname host=" << host
<< dendl
;
191 int DNSResolver::resolve_ip_addr(CephContext
*cct
, const string
& hostname
,
192 entity_addr_t
*addr
) {
194 #ifdef HAVE_RES_NQUERY
196 int r
= get_state(cct
, &res
);
200 auto put_state
= make_scope_guard([res
, this] {
201 this->put_state(res
);
203 return this->resolve_ip_addr(cct
, &res
, hostname
, addr
);
205 return this->resolve_ip_addr(cct
, NULL
, hostname
, addr
);
210 int DNSResolver::resolve_ip_addr(CephContext
*cct
, res_state
*res
, const string
& hostname
,
211 entity_addr_t
*addr
) {
213 u_char nsbuf
[NS_PACKETSZ
];
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
;
218 #ifdef HAVE_RES_NQUERY
219 len
= resolv_h
->res_nquery(*res
, hostname
.c_str(), ns_c_in
, type
, nsbuf
, sizeof(nsbuf
));
222 # ifndef HAVE_THREAD_SAFE_RES_QUERY
223 std::lock_guard
l(lock
);
225 len
= resolv_h
->res_query(hostname
.c_str(), ns_c_in
, type
, nsbuf
, sizeof(nsbuf
));
229 lderr(cct
) << "res_query() failed" << dendl
;
233 ldout(cct
, 20) << "no address found for hostname " << hostname
<< dendl
;
238 ns_initparse(nsbuf
, len
, &handle
);
240 if (ns_msg_count(handle
, ns_s_an
) == 0) {
241 ldout(cct
, 20) << "no address found for hostname " << hostname
<< dendl
;
247 if ((r
= ns_parserr(&handle
, ns_s_an
, 0, &rr
)) < 0) {
248 lderr(cct
) << "error while parsing DNS record" << dendl
;
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
)
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
);
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
) {
275 #ifdef HAVE_RES_NQUERY
277 int r
= get_state(cct
, &res
);
281 auto put_state
= make_scope_guard([res
, this] {
282 this->put_state(res
);
286 u_char nsbuf
[NS_PACKETSZ
];
289 string proto_str
= srv_protocol_to_str(trans_protocol
);
290 string query_str
= "_"+service_name
+"._"+proto_str
+(domain
.empty() ? ""
294 #ifdef HAVE_RES_NQUERY
295 len
= resolv_h
->res_nsearch(res
, query_str
.c_str(), ns_c_in
, ns_t_srv
, nsbuf
,
299 # ifndef HAVE_THREAD_SAFE_RES_QUERY
300 std::lock_guard
l(lock
);
302 len
= resolv_h
->res_search(query_str
.c_str(), ns_c_in
, ns_t_srv
, nsbuf
,
307 lderr(cct
) << "failed for service " << query_str
<< dendl
;
311 ldout(cct
, 20) << "No hosts found for service " << query_str
<< dendl
;
317 ns_initparse(nsbuf
, len
, &handle
);
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
;
326 char full_target
[NS_MAXDNAME
];
328 for (int i
= 0; i
< num_hosts
; i
++) {
330 if ((r
= ns_parserr(&handle
, ns_s_an
, i
, &rr
)) < 0) {
331 lderr(cct
) << "Error while parsing DNS record" << dendl
;
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());
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
));
350 #ifdef HAVE_RES_NQUERY
351 r
= this->resolve_ip_addr(cct
, &res
, full_target
, &addr
);
353 r
= this->resolve_ip_addr(cct
, NULL
, full_target
, &addr
);
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
;
365 target
= target
.substr(0, end
);
366 (*srv_hosts
)[target
] = {priority
, weight
, addr
};