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_
27 #ifdef HAVE_RES_NQUERY
29 int ResolvHWrapper::res_nquery(res_state s, const char *hostname, int cls,
30 int type, u_char *buf, int bufsz) {
31 return ::res_nquery(s, hostname, cls, type, buf, bufsz);
34 int ResolvHWrapper::res_nsearch(res_state s, const char *hostname, int cls,
35 int type, u_char *buf, int bufsz) {
36 return ::res_nsearch(s, hostname, cls, type, buf, bufsz);
41 int ResolvHWrapper::res_query(const char *hostname, int cls,
42 int type, u_char *buf, int bufsz) {
43 return ::res_query(hostname, cls, type, buf, bufsz);
46 int ResolvHWrapper::res_search(const char *hostname, int cls,
47 int type, u_char *buf, int bufsz) {
48 return ::res_search(hostname, cls, type, buf, bufsz);
53 DNSResolver::~DNSResolver()
55 #ifdef HAVE_RES_NQUERY
56 list<res_state>::iterator iter;
57 for (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 Mutex::Locker 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 Mutex::Locker 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 return this->resolve_ip_addr(cct, &res, hostname, addr);
202 return this->resolve_ip_addr(cct, NULL, hostname, addr);
207 int DNSResolver::resolve_ip_addr(CephContext *cct, res_state *res, const string& hostname,
208 entity_addr_t *addr) {
210 u_char nsbuf[NS_PACKETSZ];
214 #ifdef HAVE_RES_NQUERY
215 len = resolv_h->res_nquery(*res, hostname.c_str(), ns_c_in, ns_t_a, nsbuf, sizeof(nsbuf));
218 # ifndef HAVE_THREAD_SAFE_RES_QUERY
219 Mutex::Locker l(lock);
221 len = resolv_h->res_query(hostname.c_str(), ns_c_in, ns_t_a, nsbuf, sizeof(nsbuf));
225 lderr(cct) << "res_query() failed" << dendl;
229 ldout(cct, 20) << "no address found for hostname " << hostname << dendl;
234 ns_initparse(nsbuf, len, &handle);
236 if (ns_msg_count(handle, ns_s_an) == 0) {
237 ldout(cct, 20) << "no address found for hostname " << hostname << dendl;
243 if ((r = ns_parserr(&handle, ns_s_an, 0, &rr)) < 0) {
244 lderr(cct) << "error while parsing DNS record" << dendl;
249 memset(addr_buf, 0, sizeof(addr_buf));
250 inet_ntop(AF_INET, ns_rr_rdata(rr), addr_buf, sizeof(addr_buf));
251 if (!addr->parse(addr_buf)) {
252 lderr(cct) << "failed to parse address '" << (const char *)ns_rr_rdata(rr)
260 int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name,
261 const SRV_Protocol trans_protocol,
262 map<string, DNSResolver::Record> *srv_hosts) {
263 return this->resolve_srv_hosts(cct, service_name, trans_protocol, "", srv_hosts);
266 int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name,
267 const SRV_Protocol trans_protocol, const string& domain,
268 map<string, DNSResolver::Record> *srv_hosts) {
270 #ifdef HAVE_RES_NQUERY
272 int r = get_state(cct, &res);
276 auto put_state = make_scope_guard([res, this] {
277 this->put_state(res);
281 u_char nsbuf[NS_PACKETSZ];
284 string proto_str = srv_protocol_to_str(trans_protocol);
285 string query_str = "_"+service_name+"._"+proto_str+(domain.empty() ? ""
289 #ifdef HAVE_RES_NQUERY
290 len = resolv_h->res_nsearch(res, query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
294 # ifndef HAVE_THREAD_SAFE_RES_QUERY
295 Mutex::Locker l(lock);
297 len = resolv_h->res_search(query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
302 lderr(cct) << "failed for service " << query_str << dendl;
306 ldout(cct, 20) << "No hosts found for service " << query_str << dendl;
312 ns_initparse(nsbuf, len, &handle);
314 num_hosts = ns_msg_count (handle, ns_s_an);
315 if (num_hosts == 0) {
316 ldout(cct, 20) << "No hosts found for service " << query_str << dendl;
321 char full_target[NS_MAXDNAME];
323 for (int i = 0; i < num_hosts; i++) {
325 if ((r = ns_parserr(&handle, ns_s_an, i, &rr)) < 0) {
326 lderr(cct) << "Error while parsing DNS record" << dendl;
330 string full_srv_name = ns_rr_name(rr);
331 string protocol = "_" + proto_str;
332 string srv_domain = full_srv_name.substr(full_srv_name.find(protocol)
333 + protocol.length());
335 auto rdata = ns_rr_rdata(rr);
336 uint16_t priority = ns_get16(rdata); rdata += NS_INT16SZ;
337 rdata += NS_INT16SZ; // weight
338 uint16_t port = ns_get16(rdata); rdata += NS_INT16SZ;
339 memset(full_target, 0, sizeof(full_target));
340 ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
341 rdata, full_target, sizeof(full_target));
344 #ifdef HAVE_RES_NQUERY
345 r = this->resolve_ip_addr(cct, &res, full_target, &addr);
347 r = this->resolve_ip_addr(cct, NULL, full_target, &addr);
352 string target = full_target;
353 auto end = target.find(srv_domain);
354 if (end == target.npos) {
355 lderr(cct) << "resolved target not in search domain: "
356 << target << " / " << srv_domain << dendl;
359 target = target.substr(0, end);
360 (*srv_hosts)[target] = {priority, addr};