]>
Commit | Line | Data |
---|---|---|
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 | ||
7c673cae | 15 | #include <arpa/inet.h> |
7c673cae | 16 | |
224ce89b | 17 | #include "include/scope_guard.h" |
7c673cae | 18 | #include "dns_resolve.h" |
7c673cae | 19 | #include "common/debug.h" |
7c673cae FG |
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 | } | |
224ce89b WB |
106 | auto put_state = make_scope_guard([res, this] { |
107 | this->put_state(res); | |
108 | }); | |
7c673cae FG |
109 | #endif |
110 | ||
7c673cae FG |
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; | |
224ce89b | 133 | return 0; |
7c673cae FG |
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; | |
224ce89b | 143 | return -EINVAL; |
7c673cae FG |
144 | } |
145 | pt += len; | |
146 | ||
147 | if (pt + 4 > answend) { | |
148 | lderr(cct) << "ERROR: bad reply" << dendl; | |
224ce89b | 149 | return -EIO; |
7c673cae FG |
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; | |
224ce89b | 158 | return -EIO; |
7c673cae FG |
159 | } |
160 | ||
161 | pt += NS_INT16SZ; /* class */ | |
162 | ||
163 | /* read answer */ | |
164 | if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) { | |
224ce89b | 165 | return 0; |
7c673cae FG |
166 | } |
167 | pt += len; | |
168 | ldout(cct, 20) << "name=" << host << dendl; | |
169 | ||
170 | if (pt + 10 > answend) { | |
171 | lderr(cct) << "ERROR: bad reply" << dendl; | |
224ce89b | 172 | return -EIO; |
7c673cae FG |
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) { | |
224ce89b | 181 | return 0; |
7c673cae FG |
182 | } |
183 | ldout(cct, 20) << "cname host=" << host << dendl; | |
184 | *cname = host; | |
185 | ||
186 | *found = true; | |
224ce89b | 187 | return 0; |
7c673cae FG |
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, | |
224ce89b WB |
261 | const SRV_Protocol trans_protocol, |
262 | map<string, DNSResolver::Record> *srv_hosts) { | |
7c673cae FG |
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, | |
224ce89b | 268 | map<string, DNSResolver::Record> *srv_hosts) { |
7c673cae FG |
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 | } | |
224ce89b WB |
276 | auto put_state = make_scope_guard([res, this] { |
277 | this->put_state(res); | |
278 | }); | |
7c673cae FG |
279 | #endif |
280 | ||
7c673cae FG |
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) { | |
224ce89b WB |
302 | lderr(cct) << "failed for service " << query_str << dendl; |
303 | return len; | |
7c673cae FG |
304 | } |
305 | else if (len == 0) { | |
306 | ldout(cct, 20) << "No hosts found for service " << query_str << dendl; | |
224ce89b | 307 | return 0; |
7c673cae FG |
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; | |
224ce89b | 317 | return 0; |
7c673cae FG |
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; | |
224ce89b | 327 | return r; |
7c673cae FG |
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 | ||
224ce89b WB |
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; | |
7c673cae FG |
339 | memset(full_target, 0, sizeof(full_target)); |
340 | ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | |
224ce89b | 341 | rdata, full_target, sizeof(full_target)); |
7c673cae FG |
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; | |
224ce89b WB |
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}; | |
7c673cae | 361 | } |
7c673cae | 362 | } |
224ce89b | 363 | return 0; |
7c673cae FG |
364 | } |
365 | ||
366 | } |