+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2016 SUSE LINUX GmbH
- *
- * This is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1, as published by the Free Software
- * Foundation. See file COPYING.
- *
- */
-
-#include <arpa/inet.h>
-
-#include "include/scope_guard.h"
-#include "dns_resolve.h"
-#include "common/debug.h"
-
-#define dout_subsys ceph_subsys_
-
-using namespace std;
-
-namespace ceph {
-
-#ifdef HAVE_RES_NQUERY
-
-int ResolvHWrapper::res_nquery(res_state s, const char *hostname, int cls,
- int type, u_char *buf, int bufsz) {
- return ::res_nquery(s, hostname, cls, type, buf, bufsz);
-}
-
-int ResolvHWrapper::res_nsearch(res_state s, const char *hostname, int cls,
- int type, u_char *buf, int bufsz) {
- return ::res_nsearch(s, hostname, cls, type, buf, bufsz);
-}
-
-#else
-
-int ResolvHWrapper::res_query(const char *hostname, int cls,
- int type, u_char *buf, int bufsz) {
- return ::res_query(hostname, cls, type, buf, bufsz);
-}
-
-int ResolvHWrapper::res_search(const char *hostname, int cls,
- int type, u_char *buf, int bufsz) {
- return ::res_search(hostname, cls, type, buf, bufsz);
-}
-
-#endif
-
-DNSResolver::~DNSResolver()
-{
-#ifdef HAVE_RES_NQUERY
- list<res_state>::iterator iter;
- for (iter = states.begin(); iter != states.end(); ++iter) {
- struct __res_state *s = *iter;
- delete s;
- }
-#endif
- delete resolv_h;
-}
-
-#ifdef HAVE_RES_NQUERY
-int DNSResolver::get_state(CephContext *cct, res_state *ps)
-{
- lock.Lock();
- if (!states.empty()) {
- res_state s = states.front();
- states.pop_front();
- lock.Unlock();
- *ps = s;
- return 0;
- }
- lock.Unlock();
- struct __res_state *s = new struct __res_state;
- s->options = 0;
- if (res_ninit(s) < 0) {
- delete s;
- lderr(cct) << "ERROR: failed to call res_ninit()" << dendl;
- return -EINVAL;
- }
- *ps = s;
- return 0;
-}
-
-void DNSResolver::put_state(res_state s)
-{
- Mutex::Locker l(lock);
- states.push_back(s);
-}
-#endif
-
-int DNSResolver::resolve_cname(CephContext *cct, const string& hostname,
- string *cname, bool *found)
-{
- *found = false;
-
-#ifdef HAVE_RES_NQUERY
- res_state res;
- int r = get_state(cct, &res);
- if (r < 0) {
- return r;
- }
- auto put_state = make_scope_guard([res, this] {
- this->put_state(res);
- });
-#endif
-
-#define LARGE_ENOUGH_DNS_BUFSIZE 1024
- unsigned char buf[LARGE_ENOUGH_DNS_BUFSIZE];
-
-#define MAX_FQDN_SIZE 255
- char host[MAX_FQDN_SIZE + 1];
- const char *origname = hostname.c_str();
- unsigned char *pt, *answer;
- unsigned char *answend;
- int len;
-
-#ifdef HAVE_RES_NQUERY
- len = resolv_h->res_nquery(res, origname, ns_c_in, ns_t_cname, buf, sizeof(buf));
-#else
- {
-# ifndef HAVE_THREAD_SAFE_RES_QUERY
- Mutex::Locker l(lock);
-# endif
- len = resolv_h->res_query(origname, ns_c_in, ns_t_cname, buf, sizeof(buf));
- }
-#endif
- if (len < 0) {
- lderr(cct) << "res_query() failed" << dendl;
- return 0;
- }
-
- answer = buf;
- pt = answer + NS_HFIXEDSZ;
- answend = answer + len;
-
- /* read query */
- if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) {
- lderr(cct) << "ERROR: dn_expand() failed" << dendl;
- return -EINVAL;
- }
- pt += len;
-
- if (pt + 4 > answend) {
- lderr(cct) << "ERROR: bad reply" << dendl;
- return -EIO;
- }
-
- int type;
- NS_GET16(type, pt);
-
- if (type != ns_t_cname) {
- lderr(cct) << "ERROR: failed response type: type=" << type <<
- " (was expecting " << ns_t_cname << ")" << dendl;
- return -EIO;
- }
-
- pt += NS_INT16SZ; /* class */
-
- /* read answer */
- if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) {
- return 0;
- }
- pt += len;
- ldout(cct, 20) << "name=" << host << dendl;
-
- if (pt + 10 > answend) {
- lderr(cct) << "ERROR: bad reply" << dendl;
- return -EIO;
- }
-
- NS_GET16(type, pt);
- pt += NS_INT16SZ; /* class */
- pt += NS_INT32SZ; /* ttl */
- pt += NS_INT16SZ; /* size */
-
- if ((len = dn_expand(answer, answend, pt, host, sizeof(host))) < 0) {
- return 0;
- }
- ldout(cct, 20) << "cname host=" << host << dendl;
- *cname = host;
-
- *found = true;
- return 0;
-}
-
-
-int DNSResolver::resolve_ip_addr(CephContext *cct, const string& hostname,
- entity_addr_t *addr) {
-
-#ifdef HAVE_RES_NQUERY
- res_state res;
- int r = get_state(cct, &res);
- if (r < 0) {
- return r;
- }
- return this->resolve_ip_addr(cct, &res, hostname, addr);
-#else
- return this->resolve_ip_addr(cct, NULL, hostname, addr);
-#endif
-
-}
-
-int DNSResolver::resolve_ip_addr(CephContext *cct, res_state *res, const string& hostname,
- entity_addr_t *addr) {
-
- u_char nsbuf[NS_PACKETSZ];
- int len;
-
-
-#ifdef HAVE_RES_NQUERY
- len = resolv_h->res_nquery(*res, hostname.c_str(), ns_c_in, ns_t_a, nsbuf, sizeof(nsbuf));
-#else
- {
-# ifndef HAVE_THREAD_SAFE_RES_QUERY
- Mutex::Locker l(lock);
-# endif
- len = resolv_h->res_query(hostname.c_str(), ns_c_in, ns_t_a, nsbuf, sizeof(nsbuf));
- }
-#endif
- if (len < 0) {
- lderr(cct) << "res_query() failed" << dendl;
- return len;
- }
- else if (len == 0) {
- ldout(cct, 20) << "no address found for hostname " << hostname << dendl;
- return -1;
- }
-
- ns_msg handle;
- ns_initparse(nsbuf, len, &handle);
-
- if (ns_msg_count(handle, ns_s_an) == 0) {
- ldout(cct, 20) << "no address found for hostname " << hostname << dendl;
- return -1;
- }
-
- ns_rr rr;
- int r;
- if ((r = ns_parserr(&handle, ns_s_an, 0, &rr)) < 0) {
- lderr(cct) << "error while parsing DNS record" << dendl;
- return r;
- }
-
- char addr_buf[64];
- memset(addr_buf, 0, sizeof(addr_buf));
- inet_ntop(AF_INET, ns_rr_rdata(rr), addr_buf, sizeof(addr_buf));
- if (!addr->parse(addr_buf)) {
- lderr(cct) << "failed to parse address '" << (const char *)ns_rr_rdata(rr)
- << "'" << dendl;
- return -1;
- }
-
- return 0;
-}
-
-int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name,
- const SRV_Protocol trans_protocol,
- map<string, DNSResolver::Record> *srv_hosts) {
- return this->resolve_srv_hosts(cct, service_name, trans_protocol, "", srv_hosts);
-}
-
-int DNSResolver::resolve_srv_hosts(CephContext *cct, const string& service_name,
- const SRV_Protocol trans_protocol, const string& domain,
- map<string, DNSResolver::Record> *srv_hosts) {
-
-#ifdef HAVE_RES_NQUERY
- res_state res;
- int r = get_state(cct, &res);
- if (r < 0) {
- return r;
- }
- auto put_state = make_scope_guard([res, this] {
- this->put_state(res);
- });
-#endif
-
- u_char nsbuf[NS_PACKETSZ];
- int num_hosts;
-
- string proto_str = srv_protocol_to_str(trans_protocol);
- string query_str = "_"+service_name+"._"+proto_str+(domain.empty() ? ""
- : "."+domain);
- int len;
-
-#ifdef HAVE_RES_NQUERY
- len = resolv_h->res_nsearch(res, query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
- sizeof(nsbuf));
-#else
- {
-# ifndef HAVE_THREAD_SAFE_RES_QUERY
- Mutex::Locker l(lock);
-# endif
- len = resolv_h->res_search(query_str.c_str(), ns_c_in, ns_t_srv, nsbuf,
- sizeof(nsbuf));
- }
-#endif
- if (len < 0) {
- lderr(cct) << "failed for service " << query_str << dendl;
- return len;
- }
- else if (len == 0) {
- ldout(cct, 20) << "No hosts found for service " << query_str << dendl;
- return 0;
- }
-
- ns_msg handle;
-
- ns_initparse(nsbuf, len, &handle);
-
- num_hosts = ns_msg_count (handle, ns_s_an);
- if (num_hosts == 0) {
- ldout(cct, 20) << "No hosts found for service " << query_str << dendl;
- return 0;
- }
-
- ns_rr rr;
- char full_target[NS_MAXDNAME];
-
- for (int i = 0; i < num_hosts; i++) {
- int r;
- if ((r = ns_parserr(&handle, ns_s_an, i, &rr)) < 0) {
- lderr(cct) << "Error while parsing DNS record" << dendl;
- return r;
- }
-
- string full_srv_name = ns_rr_name(rr);
- string protocol = "_" + proto_str;
- string srv_domain = full_srv_name.substr(full_srv_name.find(protocol)
- + protocol.length());
-
- auto rdata = ns_rr_rdata(rr);
- uint16_t priority = ns_get16(rdata); rdata += NS_INT16SZ;
- rdata += NS_INT16SZ; // weight
- uint16_t port = ns_get16(rdata); rdata += NS_INT16SZ;
- memset(full_target, 0, sizeof(full_target));
- ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
- rdata, full_target, sizeof(full_target));
-
- entity_addr_t addr;
-#ifdef HAVE_RES_NQUERY
- r = this->resolve_ip_addr(cct, &res, full_target, &addr);
-#else
- r = this->resolve_ip_addr(cct, NULL, full_target, &addr);
-#endif
-
- if (r == 0) {
- addr.set_port(port);
- string target = full_target;
- auto end = target.find(srv_domain);
- if (end == target.npos) {
- lderr(cct) << "resolved target not in search domain: "
- << target << " / " << srv_domain << dendl;
- return -EINVAL;
- }
- target = target.substr(0, end);
- (*srv_hosts)[target] = {priority, addr};
- }
- }
- return 0;
-}
-
-}