Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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 namespace std;
24
25 namespace ceph {
26
27 #ifdef HAVE_RES_NQUERY
28
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);
32 }
33
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);
37 }
38
39 #else
40
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);
44 }
45
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);
49 }
50
51 #endif
52
53 DNSResolver::~DNSResolver()
54 {
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;
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   Mutex::Locker 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     Mutex::Locker 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   return this->resolve_ip_addr(cct, &res, hostname, addr);
201 #else
202   return this->resolve_ip_addr(cct, NULL, hostname, addr);
203 #endif
204
205 }
206
207 int DNSResolver::resolve_ip_addr(CephContext *cct, res_state *res, const string& hostname, 
208     entity_addr_t *addr) {
209
210   u_char nsbuf[NS_PACKETSZ];
211   int len;
212
213
214 #ifdef HAVE_RES_NQUERY
215   len = resolv_h->res_nquery(*res, hostname.c_str(), ns_c_in, ns_t_a, nsbuf, sizeof(nsbuf));
216 #else
217   {
218 # ifndef HAVE_THREAD_SAFE_RES_QUERY
219     Mutex::Locker l(lock);
220 # endif
221     len = resolv_h->res_query(hostname.c_str(), ns_c_in, ns_t_a, nsbuf, sizeof(nsbuf));
222   }
223 #endif
224   if (len < 0) {
225     lderr(cct) << "res_query() failed" << dendl;
226     return len;
227   }
228   else if (len == 0) {
229     ldout(cct, 20) << "no address found for hostname " << hostname << dendl;
230     return -1;
231   }
232
233   ns_msg handle;
234   ns_initparse(nsbuf, len, &handle);
235
236   if (ns_msg_count(handle, ns_s_an) == 0) {
237     ldout(cct, 20) << "no address found for hostname " << hostname << dendl;
238     return -1;
239   }
240
241   ns_rr rr;
242   int r;
243   if ((r = ns_parserr(&handle, ns_s_an, 0, &rr)) < 0) {
244       lderr(cct) << "error while parsing DNS record" << dendl;
245       return r;
246   }
247
248   char addr_buf[64];
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) 
253         << "'" << dendl;
254       return -1;
255   }
256
257   return 0;
258 }
259
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);
264 }
265
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) {
269
270 #ifdef HAVE_RES_NQUERY
271   res_state res;
272   int r = get_state(cct, &res);
273   if (r < 0) {
274     return r;
275   }
276   auto put_state = make_scope_guard([res, this] {
277       this->put_state(res);
278     });
279 #endif
280
281   u_char nsbuf[NS_PACKETSZ];
282   int num_hosts;
283
284   string proto_str = srv_protocol_to_str(trans_protocol);
285   string query_str = "_"+service_name+"._"+proto_str+(domain.empty() ? ""
286       : "."+domain);
287   int len;
288
289 #ifdef HAVE_RES_NQUERY
290   len = resolv_h->res_nsearch(res, query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
291       sizeof(nsbuf));
292 #else
293   {
294 # ifndef HAVE_THREAD_SAFE_RES_QUERY
295     Mutex::Locker l(lock);
296 # endif
297     len = resolv_h->res_search(query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
298         sizeof(nsbuf));
299   }
300 #endif
301   if (len < 0) {
302     lderr(cct) << "failed for service " << query_str << dendl;
303     return len;
304   }
305   else if (len == 0) {
306     ldout(cct, 20) << "No hosts found for service " << query_str << dendl;
307     return 0;
308   }
309
310   ns_msg handle;
311
312   ns_initparse(nsbuf, len, &handle);
313
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;
317     return 0;
318   }
319
320   ns_rr rr;
321   char full_target[NS_MAXDNAME];
322
323   for (int i = 0; i < num_hosts; i++) {
324     int r;
325     if ((r = ns_parserr(&handle, ns_s_an, i, &rr)) < 0) {
326       lderr(cct) << "Error while parsing DNS record" << dendl;
327       return r;
328     }
329
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());
334
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));
342
343     entity_addr_t addr;
344 #ifdef HAVE_RES_NQUERY
345     r = this->resolve_ip_addr(cct, &res, full_target, &addr);
346 #else
347     r = this->resolve_ip_addr(cct, NULL, full_target, &addr);
348 #endif
349
350     if (r == 0) {
351       addr.set_port(port);
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;
357         return -EINVAL;
358       }
359       target = target.substr(0, end);
360       (*srv_hosts)[target] = {priority, addr};
361     }
362   }
363   return 0;
364 }
365
366 }