]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/dns_resolve.cc
add subtree-ish sources for 12.0.3
[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
15#include <sys/types.h>
16#include <netinet/in.h>
17#include <arpa/nameser.h>
18#include <arpa/inet.h>
19#include <resolv.h>
20
21#include "dns_resolve.h"
22
23#include "acconfig.h"
24#include "common/debug.h"
25#include "msg/msg_types.h"
26
27
28#define dout_subsys ceph_subsys_
29
30using namespace std;
31
32namespace ceph {
33
34#ifdef HAVE_RES_NQUERY
35
36int ResolvHWrapper::res_nquery(res_state s, const char *hostname, int cls,
37 int type, u_char *buf, int bufsz) {
38 return ::res_nquery(s, hostname, cls, type, buf, bufsz);
39}
40
41int ResolvHWrapper::res_nsearch(res_state s, const char *hostname, int cls,
42 int type, u_char *buf, int bufsz) {
43 return ::res_nsearch(s, hostname, cls, type, buf, bufsz);
44}
45
46#else
47
48int ResolvHWrapper::res_query(const char *hostname, int cls,
49 int type, u_char *buf, int bufsz) {
50 return ::res_query(hostname, cls, type, buf, bufsz);
51}
52
53int ResolvHWrapper::res_search(const char *hostname, int cls,
54 int type, u_char *buf, int bufsz) {
55 return ::res_search(hostname, cls, type, buf, bufsz);
56}
57
58#endif
59
60DNSResolver::~DNSResolver()
61{
62#ifdef HAVE_RES_NQUERY
63 list<res_state>::iterator iter;
64 for (iter = states.begin(); iter != states.end(); ++iter) {
65 struct __res_state *s = *iter;
66 delete s;
67 }
68#endif
69 delete resolv_h;
70}
71
72#ifdef HAVE_RES_NQUERY
73int DNSResolver::get_state(CephContext *cct, res_state *ps)
74{
75 lock.Lock();
76 if (!states.empty()) {
77 res_state s = states.front();
78 states.pop_front();
79 lock.Unlock();
80 *ps = s;
81 return 0;
82 }
83 lock.Unlock();
84 struct __res_state *s = new struct __res_state;
85 s->options = 0;
86 if (res_ninit(s) < 0) {
87 delete s;
88 lderr(cct) << "ERROR: failed to call res_ninit()" << dendl;
89 return -EINVAL;
90 }
91 *ps = s;
92 return 0;
93}
94
95void DNSResolver::put_state(res_state s)
96{
97 Mutex::Locker l(lock);
98 states.push_back(s);
99}
100#endif
101
102int DNSResolver::resolve_cname(CephContext *cct, const string& hostname,
103 string *cname, bool *found)
104{
105 *found = false;
106
107#ifdef HAVE_RES_NQUERY
108 res_state res;
109 int r = get_state(cct, &res);
110 if (r < 0) {
111 return r;
112 }
113#endif
114
115 int ret;
116
117#define LARGE_ENOUGH_DNS_BUFSIZE 1024
118 unsigned char buf[LARGE_ENOUGH_DNS_BUFSIZE];
119
120#define MAX_FQDN_SIZE 255
121 char host[MAX_FQDN_SIZE + 1];
122 const char *origname = hostname.c_str();
123 unsigned char *pt, *answer;
124 unsigned char *answend;
125 int len;
126
127#ifdef HAVE_RES_NQUERY
128 len = resolv_h->res_nquery(res, origname, ns_c_in, ns_t_cname, buf, sizeof(buf));
129#else
130 {
131# ifndef HAVE_THREAD_SAFE_RES_QUERY
132 Mutex::Locker l(lock);
133# endif
134 len = resolv_h->res_query(origname, ns_c_in, ns_t_cname, buf, sizeof(buf));
135 }
136#endif
137 if (len < 0) {
138 lderr(cct) << "res_query() failed" << dendl;
139 ret = 0;
140 goto done;
141 }
142
143 answer = buf;
144 pt = answer + NS_HFIXEDSZ;
145 answend = answer + len;
146
147 /* read query */
148 if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) {
149 lderr(cct) << "ERROR: dn_expand() failed" << dendl;
150 ret = -EINVAL;
151 goto done;
152 }
153 pt += len;
154
155 if (pt + 4 > answend) {
156 lderr(cct) << "ERROR: bad reply" << dendl;
157 ret = -EIO;
158 goto done;
159 }
160
161 int type;
162 NS_GET16(type, pt);
163
164 if (type != ns_t_cname) {
165 lderr(cct) << "ERROR: failed response type: type=" << type <<
166 " (was expecting " << ns_t_cname << ")" << dendl;
167 ret = -EIO;
168 goto done;
169 }
170
171 pt += NS_INT16SZ; /* class */
172
173 /* read answer */
174 if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) {
175 ret = 0;
176 goto done;
177 }
178 pt += len;
179 ldout(cct, 20) << "name=" << host << dendl;
180
181 if (pt + 10 > answend) {
182 lderr(cct) << "ERROR: bad reply" << dendl;
183 ret = -EIO;
184 goto done;
185 }
186
187 NS_GET16(type, pt);
188 pt += NS_INT16SZ; /* class */
189 pt += NS_INT32SZ; /* ttl */
190 pt += NS_INT16SZ; /* size */
191
192 if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) {
193 ret = 0;
194 goto done;
195 }
196 ldout(cct, 20) << "cname host=" << host << dendl;
197 *cname = host;
198
199 *found = true;
200 ret = 0;
201done:
202#ifdef HAVE_RES_NQUERY
203 put_state(res);
204#endif
205 return ret;
206}
207
208
209int DNSResolver::resolve_ip_addr(CephContext *cct, const string& hostname,
210 entity_addr_t *addr) {
211
212#ifdef HAVE_RES_NQUERY
213 res_state res;
214 int r = get_state(cct, &res);
215 if (r < 0) {
216 return r;
217 }
218 return this->resolve_ip_addr(cct, &res, hostname, addr);
219#else
220 return this->resolve_ip_addr(cct, NULL, hostname, addr);
221#endif
222
223}
224
225int DNSResolver::resolve_ip_addr(CephContext *cct, res_state *res, const string& hostname,
226 entity_addr_t *addr) {
227
228 u_char nsbuf[NS_PACKETSZ];
229 int len;
230
231
232#ifdef HAVE_RES_NQUERY
233 len = resolv_h->res_nquery(*res, hostname.c_str(), ns_c_in, ns_t_a, nsbuf, sizeof(nsbuf));
234#else
235 {
236# ifndef HAVE_THREAD_SAFE_RES_QUERY
237 Mutex::Locker l(lock);
238# endif
239 len = resolv_h->res_query(hostname.c_str(), ns_c_in, ns_t_a, nsbuf, sizeof(nsbuf));
240 }
241#endif
242 if (len < 0) {
243 lderr(cct) << "res_query() failed" << dendl;
244 return len;
245 }
246 else if (len == 0) {
247 ldout(cct, 20) << "no address found for hostname " << hostname << dendl;
248 return -1;
249 }
250
251 ns_msg handle;
252 ns_initparse(nsbuf, len, &handle);
253
254 if (ns_msg_count(handle, ns_s_an) == 0) {
255 ldout(cct, 20) << "no address found for hostname " << hostname << dendl;
256 return -1;
257 }
258
259 ns_rr rr;
260 int r;
261 if ((r = ns_parserr(&handle, ns_s_an, 0, &rr)) < 0) {
262 lderr(cct) << "error while parsing DNS record" << dendl;
263 return r;
264 }
265
266 char addr_buf[64];
267 memset(addr_buf, 0, sizeof(addr_buf));
268 inet_ntop(AF_INET, ns_rr_rdata(rr), addr_buf, sizeof(addr_buf));
269 if (!addr->parse(addr_buf)) {
270 lderr(cct) << "failed to parse address '" << (const char *)ns_rr_rdata(rr)
271 << "'" << dendl;
272 return -1;
273 }
274
275 return 0;
276}
277
278int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name,
279 const SRV_Protocol trans_protocol, map<string, entity_addr_t> *srv_hosts) {
280 return this->resolve_srv_hosts(cct, service_name, trans_protocol, "", srv_hosts);
281}
282
283int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name,
284 const SRV_Protocol trans_protocol, const string& domain,
285 map<string, entity_addr_t> *srv_hosts) {
286
287#ifdef HAVE_RES_NQUERY
288 res_state res;
289 int r = get_state(cct, &res);
290 if (r < 0) {
291 return r;
292 }
293#endif
294
295 int ret;
296 u_char nsbuf[NS_PACKETSZ];
297 int num_hosts;
298
299 string proto_str = srv_protocol_to_str(trans_protocol);
300 string query_str = "_"+service_name+"._"+proto_str+(domain.empty() ? ""
301 : "."+domain);
302 int len;
303
304#ifdef HAVE_RES_NQUERY
305 len = resolv_h->res_nsearch(res, query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
306 sizeof(nsbuf));
307#else
308 {
309# ifndef HAVE_THREAD_SAFE_RES_QUERY
310 Mutex::Locker l(lock);
311# endif
312 len = resolv_h->res_search(query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
313 sizeof(nsbuf));
314 }
315#endif
316 if (len < 0) {
317 lderr(cct) << "res_search() failed" << dendl;
318 ret = len;
319 goto done;
320 }
321 else if (len == 0) {
322 ldout(cct, 20) << "No hosts found for service " << query_str << dendl;
323 ret = 0;
324 goto done;
325 }
326
327 ns_msg handle;
328
329 ns_initparse(nsbuf, len, &handle);
330
331 num_hosts = ns_msg_count (handle, ns_s_an);
332 if (num_hosts == 0) {
333 ldout(cct, 20) << "No hosts found for service " << query_str << dendl;
334 ret = 0;
335 goto done;
336 }
337
338 ns_rr rr;
339 char full_target[NS_MAXDNAME];
340
341 for (int i = 0; i < num_hosts; i++) {
342 int r;
343 if ((r = ns_parserr(&handle, ns_s_an, i, &rr)) < 0) {
344 lderr(cct) << "Error while parsing DNS record" << dendl;
345 ret = r;
346 goto done;
347 }
348
349 string full_srv_name = ns_rr_name(rr);
350 string protocol = "_" + proto_str;
351 string srv_domain = full_srv_name.substr(full_srv_name.find(protocol)
352 + protocol.length());
353
354 int port = ns_get16(ns_rr_rdata(rr) + (NS_INT16SZ * 2)); /* port = rdata + priority + weight */
355 memset(full_target, 0, sizeof(full_target));
356 ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
357 ns_rr_rdata(rr) + (NS_INT16SZ * 3), /* comp_dn = rdata + priority + weight + port */
358 full_target, sizeof(full_target));
359
360 entity_addr_t addr;
361#ifdef HAVE_RES_NQUERY
362 r = this->resolve_ip_addr(cct, &res, full_target, &addr);
363#else
364 r = this->resolve_ip_addr(cct, NULL, full_target, &addr);
365#endif
366
367 if (r == 0) {
368 addr.set_port(port);
369 string target = full_target;
370 assert(target.find(srv_domain) != target.npos);
371 target = target.substr(0, target.find(srv_domain));
372 (*srv_hosts)[target] = addr;
373 }
374
375 }
376
377 ret = 0;
378done:
379#ifdef HAVE_RES_NQUERY
380 put_state(res);
381#endif
382 return ret;
383}
384
385}
386