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   	
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   	{
67   	  lock.lock();
68   	  if (!states.empty()) {
69   	    res_state s = states.front();
70   	    states.pop_front();
71   	    lock.unlock();
72   	    *ps = s;
73   	    return 0;
74   	  }
75   	  lock.unlock();
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   	{
(1) Event fun_call_w_exception: Called function throws an exception of type "_ZN5boost16exception_detail10clone_implINS0_19error_info_injectorINSt8ios_base7failureB5cxx11EEEEE". [details]
89   	  std::lock_guard l(lock);
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  	  }
105  	  auto put_state = make_scope_guard([res, this] {
106  	      this->put_state(res);
107  	    });
108  	#endif
109  	
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
125  	    std::lock_guard l(lock);
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;
132  	    return 0;
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;
142  	    return -EINVAL;
143  	  }
144  	  pt += len;
145  	
146  	  if (pt + 4 > answend) {
147  	    lderr(cct) << "ERROR: bad reply" << dendl;
148  	    return -EIO;
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;
157  	    return -EIO;
158  	  }
159  	
160  	  pt += NS_INT16SZ; /* class */
161  	
162  	  /* read answer */
163  	  if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) {
164  	    return 0;
165  	  }
166  	  pt += len;
167  	  ldout(cct, 20) << "name=" << host << dendl;
168  	
169  	  if (pt + 10 > answend) {
170  	    lderr(cct) << "ERROR: bad reply" << dendl;
171  	    return -EIO;
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) {
180  	    return 0;
181  	  }
182  	  ldout(cct, 20) << "cname host=" << host << dendl;
183  	  *cname = host;
184  	
185  	  *found = true;
186  	  return 0;
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  	  }
199  	  auto put_state = make_scope_guard([res, this] {
(1) Event fun_call_w_exception: Called function throws an exception of type "_ZN5boost16exception_detail10clone_implINS0_19error_info_injectorINSt8ios_base7failureB5cxx11EEEEE". [details]
200  	      this->put_state(res);
201  	    });
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;
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;
216  	
217  	#ifdef HAVE_RES_NQUERY
218  	  len = resolv_h->res_nquery(*res, hostname.c_str(), ns_c_in, type, nsbuf, sizeof(nsbuf));
219  	#else
220  	  {
221  	# ifndef HAVE_THREAD_SAFE_RES_QUERY
222  	    std::lock_guard l(lock);
223  	# endif
224  	    len = resolv_h->res_query(hostname.c_str(), ns_c_in, type, nsbuf, sizeof(nsbuf));
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];
252  	  memset(addr_buf, 0, sizeof(addr_buf));
253  	  inet_ntop(family, ns_rr_rdata(rr), addr_buf, sizeof(addr_buf));
254  	  if (!addr->parse(addr_buf)) {
255  	      lderr(cct) << "failed to parse address '" << (const char *)ns_rr_rdata(rr) 
256  	        << "'" << dendl;
257  	      return -1;
258  	  }
259  	
260  	  return 0;
261  	}
262  	
263  	int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name, 
264  	    const SRV_Protocol trans_protocol,
265  	    map<string, DNSResolver::Record> *srv_hosts) {
266  	  return this->resolve_srv_hosts(cct, service_name, trans_protocol, "", srv_hosts);
267  	}
268  	
269  	int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name, 
270  	    const SRV_Protocol trans_protocol, const string& domain,
271  	    map<string, DNSResolver::Record> *srv_hosts) {
272  	
273  	#ifdef HAVE_RES_NQUERY
274  	  res_state res;
275  	  int r = get_state(cct, &res);
276  	  if (r < 0) {
277  	    return r;
278  	  }
279  	  auto put_state = make_scope_guard([res, this] {
280  	      this->put_state(res);
281  	    });
282  	#endif
283  	
284  	  u_char nsbuf[NS_PACKETSZ];
285  	  int num_hosts;
286  	
287  	  string proto_str = srv_protocol_to_str(trans_protocol);
288  	  string query_str = "_"+service_name+"._"+proto_str+(domain.empty() ? ""
289  	      : "."+domain);
290  	  int len;
291  	
292  	#ifdef HAVE_RES_NQUERY
293  	  len = resolv_h->res_nsearch(res, query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
294  	      sizeof(nsbuf));
295  	#else
296  	  {
297  	# ifndef HAVE_THREAD_SAFE_RES_QUERY
298  	    std::lock_guard l(lock);
299  	# endif
300  	    len = resolv_h->res_search(query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
301  	        sizeof(nsbuf));
302  	  }
303  	#endif
304  	  if (len < 0) {
305  	    lderr(cct) << "failed for service " << query_str << dendl;
306  	    return len;
307  	  }
308  	  else if (len == 0) {
309  	    ldout(cct, 20) << "No hosts found for service " << query_str << dendl;
310  	    return 0;
311  	  }
312  	
313  	  ns_msg handle;
314  	
315  	  ns_initparse(nsbuf, len, &handle);
316  	
317  	  num_hosts = ns_msg_count (handle, ns_s_an);
318  	  if (num_hosts == 0) {
319  	    ldout(cct, 20) << "No hosts found for service " << query_str << dendl;
320  	    return 0;
321  	  }
322  	
323  	  ns_rr rr;
324  	  char full_target[NS_MAXDNAME];
325  	
326  	  for (int i = 0; i < num_hosts; i++) {
327  	    int r;
328  	    if ((r = ns_parserr(&handle, ns_s_an, i, &rr)) < 0) {
329  	      lderr(cct) << "Error while parsing DNS record" << dendl;
330  	      return r;
331  	    }
332  	
333  	    string full_srv_name = ns_rr_name(rr);
334  	    string protocol = "_" + proto_str;
335  	    string srv_domain = full_srv_name.substr(full_srv_name.find(protocol)
336  	        + protocol.length());
337  	
338  	    auto rdata = ns_rr_rdata(rr);
339  	    uint16_t priority = ns_get16(rdata); rdata += NS_INT16SZ;
340  	    uint16_t weight = ns_get16(rdata); rdata += NS_INT16SZ;
341  	    uint16_t port = ns_get16(rdata); rdata += NS_INT16SZ;
342  	    memset(full_target, 0, sizeof(full_target));
343  	    ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
344  	                       rdata, full_target, sizeof(full_target));
345  	
346  	    entity_addr_t addr;
347  	#ifdef HAVE_RES_NQUERY
348  	    r = this->resolve_ip_addr(cct, &res, full_target, &addr);
349  	#else
350  	    r = this->resolve_ip_addr(cct, NULL, full_target, &addr);
351  	#endif
352  	
353  	    if (r == 0) {
354  	      addr.set_port(port);
355  	      string target = full_target;
356  	      auto end = target.find(srv_domain);
357  	      if (end == target.npos) {
358  		lderr(cct) << "resolved target not in search domain: "
359  			   << target << " / " << srv_domain << dendl;
360  		return -EINVAL;
361  	      }
362  	      target = target.substr(0, end);
363  	      (*srv_hosts)[target] = {priority, weight, addr};
364  	    }
365  	  }
366  	  return 0;
367  	}
368  	
369  	}
370