]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/dns_resolve.cc
3227c88e2fd2f6297abd32ccb7a75e2a5fc4f2a6
[ceph.git] / 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 "dns_resolve.h"
18 #include "common/debug.h"
19
20 #define dout_subsys ceph_subsys_
21
22 using namespace std;
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 {
89 Mutex::Locker 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 #endif
106
107 int ret;
108
109 #define LARGE_ENOUGH_DNS_BUFSIZE 1024
110 unsigned char buf[LARGE_ENOUGH_DNS_BUFSIZE];
111
112 #define MAX_FQDN_SIZE 255
113 char host[MAX_FQDN_SIZE + 1];
114 const char *origname = hostname.c_str();
115 unsigned char *pt, *answer;
116 unsigned char *answend;
117 int len;
118
119 #ifdef HAVE_RES_NQUERY
120 len = resolv_h->res_nquery(res, origname, ns_c_in, ns_t_cname, buf, sizeof(buf));
121 #else
122 {
123 # ifndef HAVE_THREAD_SAFE_RES_QUERY
124 Mutex::Locker l(lock);
125 # endif
126 len = resolv_h->res_query(origname, ns_c_in, ns_t_cname, buf, sizeof(buf));
127 }
128 #endif
129 if (len < 0) {
130 lderr(cct) << "res_query() failed" << dendl;
131 ret = 0;
132 goto done;
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 ret = -EINVAL;
143 goto done;
144 }
145 pt += len;
146
147 if (pt + 4 > answend) {
148 lderr(cct) << "ERROR: bad reply" << dendl;
149 ret = -EIO;
150 goto done;
151 }
152
153 int type;
154 NS_GET16(type, pt);
155
156 if (type != ns_t_cname) {
157 lderr(cct) << "ERROR: failed response type: type=" << type <<
158 " (was expecting " << ns_t_cname << ")" << dendl;
159 ret = -EIO;
160 goto done;
161 }
162
163 pt += NS_INT16SZ; /* class */
164
165 /* read answer */
166 if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) {
167 ret = 0;
168 goto done;
169 }
170 pt += len;
171 ldout(cct, 20) << "name=" << host << dendl;
172
173 if (pt + 10 > answend) {
174 lderr(cct) << "ERROR: bad reply" << dendl;
175 ret = -EIO;
176 goto done;
177 }
178
179 NS_GET16(type, pt);
180 pt += NS_INT16SZ; /* class */
181 pt += NS_INT32SZ; /* ttl */
182 pt += NS_INT16SZ; /* size */
183
184 if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) {
185 ret = 0;
186 goto done;
187 }
188 ldout(cct, 20) << "cname host=" << host << dendl;
189 *cname = host;
190
191 *found = true;
192 ret = 0;
193 done:
194 #ifdef HAVE_RES_NQUERY
195 put_state(res);
196 #endif
197 return ret;
198 }
199
200
201 int DNSResolver::resolve_ip_addr(CephContext *cct, const string& hostname,
202 entity_addr_t *addr) {
203
204 #ifdef HAVE_RES_NQUERY
205 res_state res;
206 int r = get_state(cct, &res);
207 if (r < 0) {
208 return r;
209 }
210 return this->resolve_ip_addr(cct, &res, hostname, addr);
211 #else
212 return this->resolve_ip_addr(cct, NULL, hostname, addr);
213 #endif
214
215 }
216
217 int DNSResolver::resolve_ip_addr(CephContext *cct, res_state *res, const string& hostname,
218 entity_addr_t *addr) {
219
220 u_char nsbuf[NS_PACKETSZ];
221 int len;
222
223
224 #ifdef HAVE_RES_NQUERY
225 len = resolv_h->res_nquery(*res, hostname.c_str(), ns_c_in, ns_t_a, nsbuf, sizeof(nsbuf));
226 #else
227 {
228 # ifndef HAVE_THREAD_SAFE_RES_QUERY
229 Mutex::Locker l(lock);
230 # endif
231 len = resolv_h->res_query(hostname.c_str(), ns_c_in, ns_t_a, nsbuf, sizeof(nsbuf));
232 }
233 #endif
234 if (len < 0) {
235 lderr(cct) << "res_query() failed" << dendl;
236 return len;
237 }
238 else if (len == 0) {
239 ldout(cct, 20) << "no address found for hostname " << hostname << dendl;
240 return -1;
241 }
242
243 ns_msg handle;
244 ns_initparse(nsbuf, len, &handle);
245
246 if (ns_msg_count(handle, ns_s_an) == 0) {
247 ldout(cct, 20) << "no address found for hostname " << hostname << dendl;
248 return -1;
249 }
250
251 ns_rr rr;
252 int r;
253 if ((r = ns_parserr(&handle, ns_s_an, 0, &rr)) < 0) {
254 lderr(cct) << "error while parsing DNS record" << dendl;
255 return r;
256 }
257
258 char addr_buf[64];
259 memset(addr_buf, 0, sizeof(addr_buf));
260 inet_ntop(AF_INET, ns_rr_rdata(rr), addr_buf, sizeof(addr_buf));
261 if (!addr->parse(addr_buf)) {
262 lderr(cct) << "failed to parse address '" << (const char *)ns_rr_rdata(rr)
263 << "'" << dendl;
264 return -1;
265 }
266
267 return 0;
268 }
269
270 int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name,
271 const SRV_Protocol trans_protocol, map<string, entity_addr_t> *srv_hosts) {
272 return this->resolve_srv_hosts(cct, service_name, trans_protocol, "", srv_hosts);
273 }
274
275 int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name,
276 const SRV_Protocol trans_protocol, const string& domain,
277 map<string, entity_addr_t> *srv_hosts) {
278
279 #ifdef HAVE_RES_NQUERY
280 res_state res;
281 int r = get_state(cct, &res);
282 if (r < 0) {
283 return r;
284 }
285 #endif
286
287 int ret;
288 u_char nsbuf[NS_PACKETSZ];
289 int num_hosts;
290
291 string proto_str = srv_protocol_to_str(trans_protocol);
292 string query_str = "_"+service_name+"._"+proto_str+(domain.empty() ? ""
293 : "."+domain);
294 int len;
295
296 #ifdef HAVE_RES_NQUERY
297 len = resolv_h->res_nsearch(res, query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
298 sizeof(nsbuf));
299 #else
300 {
301 # ifndef HAVE_THREAD_SAFE_RES_QUERY
302 Mutex::Locker l(lock);
303 # endif
304 len = resolv_h->res_search(query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
305 sizeof(nsbuf));
306 }
307 #endif
308 if (len < 0) {
309 lderr(cct) << "res_search() failed" << dendl;
310 ret = len;
311 goto done;
312 }
313 else if (len == 0) {
314 ldout(cct, 20) << "No hosts found for service " << query_str << dendl;
315 ret = 0;
316 goto done;
317 }
318
319 ns_msg handle;
320
321 ns_initparse(nsbuf, len, &handle);
322
323 num_hosts = ns_msg_count (handle, ns_s_an);
324 if (num_hosts == 0) {
325 ldout(cct, 20) << "No hosts found for service " << query_str << dendl;
326 ret = 0;
327 goto done;
328 }
329
330 ns_rr rr;
331 char full_target[NS_MAXDNAME];
332
333 for (int i = 0; i < num_hosts; i++) {
334 int r;
335 if ((r = ns_parserr(&handle, ns_s_an, i, &rr)) < 0) {
336 lderr(cct) << "Error while parsing DNS record" << dendl;
337 ret = r;
338 goto done;
339 }
340
341 string full_srv_name = ns_rr_name(rr);
342 string protocol = "_" + proto_str;
343 string srv_domain = full_srv_name.substr(full_srv_name.find(protocol)
344 + protocol.length());
345
346 int port = ns_get16(ns_rr_rdata(rr) + (NS_INT16SZ * 2)); /* port = rdata + priority + weight */
347 memset(full_target, 0, sizeof(full_target));
348 ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
349 ns_rr_rdata(rr) + (NS_INT16SZ * 3), /* comp_dn = rdata + priority + weight + port */
350 full_target, sizeof(full_target));
351
352 entity_addr_t addr;
353 #ifdef HAVE_RES_NQUERY
354 r = this->resolve_ip_addr(cct, &res, full_target, &addr);
355 #else
356 r = this->resolve_ip_addr(cct, NULL, full_target, &addr);
357 #endif
358
359 if (r == 0) {
360 addr.set_port(port);
361 string target = full_target;
362 assert(target.find(srv_domain) != target.npos);
363 target = target.substr(0, target.find(srv_domain));
364 (*srv_hosts)[target] = addr;
365 }
366
367 }
368
369 ret = 0;
370 done:
371 #ifdef HAVE_RES_NQUERY
372 put_state(res);
373 #endif
374 return ret;
375 }
376
377 }
378