From 9db949eec327df4173fde3de934a87bedb0db13c Mon Sep 17 00:00:00 2001 From: Bryan O'Sullivan Date: Mon, 2 Jun 2008 21:14:31 +0000 Subject: svn merge -r88066:88786 svn+ssh://svn.lindenlab.com/svn/linden/branches/cmake-9-merge dataserver-is-deprecated for-fucks-sake-whats-with-these-commit-markers --- indra/llmessage/llares.cpp | 804 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 804 insertions(+) create mode 100644 indra/llmessage/llares.cpp (limited to 'indra/llmessage/llares.cpp') diff --git a/indra/llmessage/llares.cpp b/indra/llmessage/llares.cpp new file mode 100644 index 0000000000..d11f227c21 --- /dev/null +++ b/indra/llmessage/llares.cpp @@ -0,0 +1,804 @@ +/** + * @file llares.cpp + * @author Bryan O'Sullivan + * @date 2007-08-15 + * @brief Wrapper for asynchronous DNS lookups. + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include +#include + +#include "apr_portable.h" +#include "apr_network_io.h" +#include "apr_poll.h" + +#include "llapr.h" +#include "llares.h" + +#if defined(LL_WINDOWS) +# define ns_c_in 1 +# define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */ +# define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */ +# define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */ +#else +# include +#endif + +LLAres::HostResponder::~HostResponder() +{ +} + +void LLAres::HostResponder::hostResult(const hostent *ent) +{ + llinfos << "LLAres::HostResponder::hostResult not implemented" << llendl; +} + +void LLAres::HostResponder::hostError(int code) +{ + llinfos << "LLAres::HostResponder::hostError " << code << ": " + << LLAres::strerror(code) << llendl; +} + +LLAres::NameInfoResponder::~NameInfoResponder() +{ +} + +void LLAres::NameInfoResponder::nameInfoResult(const char *node, + const char *service) +{ + llinfos << "LLAres::NameInfoResponder::nameInfoResult not implemented" + << llendl; +} + +void LLAres::NameInfoResponder::nameInfoError(int code) +{ + llinfos << "LLAres::NameInfoResponder::nameInfoError " << code << ": " + << LLAres::strerror(code) << llendl; +} + +LLAres::QueryResponder::~QueryResponder() +{ +} + +void LLAres::QueryResponder::queryResult(const char *buf, size_t len) +{ + llinfos << "LLAres::QueryResponder::queryResult not implemented" + << llendl; +} + +void LLAres::QueryResponder::queryError(int code) +{ + llinfos << "LLAres::QueryResponder::queryError " << code << ": " + << LLAres::strerror(code) << llendl; +} + +LLAres::LLAres() +{ + ares_init(&chan_); +} + +LLAres::~LLAres() +{ + ares_destroy(chan_); +} + +void LLAres::cancel() +{ + ares_cancel(chan_); +} + +static void host_callback_1_5(void *arg, int status, int timeouts, + struct hostent *ent) +{ + LLPointer *resp = + (LLPointer *) arg; + + if (status == ARES_SUCCESS) + { + (*resp)->hostResult(ent); + } else { + (*resp)->hostError(status); + } + + delete resp; +} + +#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4 +static void host_callback(void *arg, int status, struct hostent *ent) +{ + host_callback_1_5(arg, status, 0, ent); +} +#else +# define host_callback host_callback_1_5 +#endif + +void LLAres::getHostByName(const char *name, HostResponder *resp, + int family) +{ + ares_gethostbyname(chan_, name, family, host_callback, + new LLPointer(resp)); +} + +void LLAres::getSrvRecords(const std::string &name, SrvResponder *resp) +{ + search(name, RES_SRV, resp); +} + +void LLAres::rewriteURI(const std::string &uri, UriRewriteResponder *resp) +{ + llinfos << "Rewriting " << uri << llendl; + + resp->mUri = LLURI(uri); + search("_" + resp->mUri.scheme() + "._tcp." + resp->mUri.hostName(), + RES_SRV, resp); +} + +LLQueryResponder::LLQueryResponder() + : LLAres::QueryResponder(), + mResult(ARES_ENODATA) +{ +} + +int LLQueryResponder::parseRR(const char *buf, size_t len, const char *&pos, + LLPointer &r) +{ + std::string rrname; + size_t enclen; + int ret; + + // RR name. + + ret = LLAres::expandName(pos, buf, len, rrname, enclen); + if (ret != ARES_SUCCESS) + { + return ret; + } + + pos += enclen; + + if (pos + NS_RRFIXEDSZ > buf + len) + { + return ARES_EBADRESP; + } + + int rrtype = DNS_RR_TYPE(pos); + int rrclass = DNS_RR_CLASS(pos); + int rrttl = DNS_RR_TTL(pos); + int rrlen = DNS_RR_LEN(pos); + + if (rrclass != ns_c_in) + { + return ARES_EBADRESP; + } + + pos += NS_RRFIXEDSZ; + + if (pos + rrlen > buf + len) + { + return ARES_EBADRESP; + } + + switch (rrtype) + { + case RES_A: + r = new LLARecord(rrname, rrttl); + break; + case RES_NS: + r = new LLNsRecord(rrname, rrttl); + break; + case RES_CNAME: + r = new LLCnameRecord(rrname, rrttl); + break; + case RES_PTR: + r = new LLPtrRecord(rrname, rrttl); + break; + case RES_AAAA: + r = new LLAaaaRecord(rrname, rrttl); + break; + case RES_SRV: + r = new LLSrvRecord(rrname, rrttl); + break; + default: + llinfos << "LLQueryResponder::parseRR got unknown RR type " << rrtype + << llendl; + return ARES_EBADRESP; + } + + ret = r->parse(buf, len, pos, rrlen); + + if (ret == ARES_SUCCESS) + { + pos += rrlen; + } else { + r = NULL; + } + + return ret; +} + +int LLQueryResponder::parseSection(const char *buf, size_t len, + size_t count, const char *&pos, + dns_rrs_t &rrs) +{ + int ret = ARES_SUCCESS; + + for (size_t i = 0; i < count; i++) + { + LLPointer r; + ret = parseRR(buf, len, pos, r); + if (ret != ARES_SUCCESS) + { + break; + } + rrs.push_back(r); + } + + return ret; +} + +void LLQueryResponder::queryResult(const char *buf, size_t len) +{ + const char *pos = buf; + int qdcount = DNS_HEADER_QDCOUNT(pos); + int ancount = DNS_HEADER_ANCOUNT(pos); + int nscount = DNS_HEADER_NSCOUNT(pos); + int arcount = DNS_HEADER_ARCOUNT(pos); + int ret; + + if (qdcount == 0 || ancount + nscount + arcount == 0) + { + ret = ARES_ENODATA; + goto bail; + } + + pos += NS_HFIXEDSZ; + + for (int i = 0; i < qdcount; i++) + { + std::string ignore; + size_t enclen; + + ret = LLAres::expandName(pos, buf, len, i == 0 ? mQuery : ignore, + enclen); + if (ret != ARES_SUCCESS) + { + goto bail; + } + + pos += enclen; + + if (i == 0) + { + int t = DNS_QUESTION_TYPE(pos); + switch (t) + { + case RES_A: + case RES_NS: + case RES_CNAME: + case RES_PTR: + case RES_AAAA: + case RES_SRV: + mType = (LLResType) t; + break; + default: + llinfos << "Cannot grok query type " << t << llendl; + ret = ARES_EBADQUERY; + goto bail; + } + } + + pos += NS_QFIXEDSZ; + if (pos > buf + len) + { + ret = ARES_EBADRESP; + goto bail; + } + } + + ret = parseSection(buf, len, ancount, pos, mAnswers); + if (ret != ARES_SUCCESS) + { + goto bail; + } + + ret = parseSection(buf, len, nscount, pos, mAuthorities); + if (ret != ARES_SUCCESS) + { + goto bail; + } + + ret = parseSection(buf, len, arcount, pos, mAdditional); + +bail: + mResult = ret; + if (mResult == ARES_SUCCESS) + { + querySuccess(); + } else { + queryError(mResult); + } +} + +void LLQueryResponder::querySuccess() +{ + llinfos << "LLQueryResponder::queryResult not implemented" << llendl; +} + +void LLAres::SrvResponder::querySuccess() +{ + if (mType == RES_SRV) + { + srvResult(mAnswers); + } else { + srvError(ARES_EBADRESP); + } +} + +void LLAres::SrvResponder::queryError(int code) +{ + srvError(code); +} + +void LLAres::SrvResponder::srvResult(const dns_rrs_t &ents) +{ + llinfos << "LLAres::SrvResponder::srvResult not implemented" << llendl; + + for (size_t i = 0; i < ents.size(); i++) + { + const LLSrvRecord *s = (const LLSrvRecord *) ents[i].get(); + + llinfos << "[" << i << "] " << s->host() << ":" << s->port() + << " priority " << s->priority() + << " weight " << s->weight() + << llendl; + } +} + +void LLAres::SrvResponder::srvError(int code) +{ + llinfos << "LLAres::SrvResponder::srvError " << code << ": " + << LLAres::strerror(code) << llendl; +} + +static void nameinfo_callback_1_5(void *arg, int status, int timeouts, + char *node, char *service) +{ + LLPointer *resp = + (LLPointer *) arg; + + if (status == ARES_SUCCESS) + { + (*resp)->nameInfoResult(node, service); + } else { + (*resp)->nameInfoError(status); + } + + delete resp; +} + +#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4 +static void nameinfo_callback(void *arg, int status, char *node, char *service) +{ + nameinfo_callback_1_5(arg, status, 0, node, service); +} +#else +# define nameinfo_callback nameinfo_callback_1_5 +#endif + +void LLAres::getNameInfo(const struct sockaddr &sa, socklen_t salen, int flags, + NameInfoResponder *resp) +{ + ares_getnameinfo(chan_, &sa, salen, flags, nameinfo_callback, + new LLPointer(resp)); +} + +static void search_callback_1_5(void *arg, int status, int timeouts, + unsigned char *abuf, int alen) +{ + LLPointer *resp = + (LLPointer *) arg; + + if (status == ARES_SUCCESS) + { + (*resp)->queryResult((const char *) abuf, alen); + } else { + (*resp)->queryError(status); + } + + delete resp; +} + +#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4 +static void search_callback(void *arg, int status, unsigned char *abuf, + int alen) +{ + search_callback_1_5(arg, status, 0, abuf, alen); +} +#else +# define search_callback search_callback_1_5 +#endif + +void LLAres::search(const std::string &query, LLResType type, + QueryResponder *resp) +{ + ares_search(chan_, query.c_str(), ns_c_in, type, search_callback, + new LLPointer(resp)); +} + +bool LLAres::process(U64 timeout) +{ + if (!gAPRPoolp) + { + ll_init_apr(); + } + + int socks[ARES_GETSOCK_MAXNUM]; + apr_pollfd_t aprFds[ARES_GETSOCK_MAXNUM]; + apr_int32_t nsds = 0; + apr_status_t status; + apr_pool_t *pool; + int nactive = 0; + int bitmask; + + bitmask = ares_getsock(chan_, socks, ARES_GETSOCK_MAXNUM); + + if (bitmask == 0) + { + goto bail; + } + + status = apr_pool_create(&pool, gAPRPoolp); + ll_apr_assert_status(status); + + for (int i = 0; i < ARES_GETSOCK_MAXNUM; i++) + { + if (ARES_GETSOCK_READABLE(bitmask, i)) + { + aprFds[nactive].reqevents = APR_POLLIN | APR_POLLERR; + } + else if (ARES_GETSOCK_WRITABLE(bitmask, i)) + { + aprFds[nactive].reqevents = APR_POLLOUT | APR_POLLERR; + } else { + continue; + } + + apr_socket_t *aprSock = NULL; + + status = apr_os_sock_put(&aprSock, (apr_os_sock_t *) &socks[i], pool); + if (status != APR_SUCCESS) + { + ll_apr_warn_status(status); + goto bail_pool; + } + + aprFds[nactive].desc.s = aprSock; + aprFds[nactive].desc_type = APR_POLL_SOCKET; + aprFds[nactive].p = pool; + aprFds[nactive].rtnevents = 0; + aprFds[nactive].client_data = &socks[i]; + + nactive++; + } + + if (nactive > 0) + { + status = apr_poll(aprFds, nactive, &nsds, timeout); + + if (status != APR_SUCCESS && status != APR_TIMEUP) + { + ll_apr_warn_status(status); + } + + for (int i = 0; i < nactive; i++) + { + int evts = aprFds[i].rtnevents; + int ifd = (evts & (APR_POLLIN | APR_POLLERR)) + ? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD; + int ofd = (evts & (APR_POLLOUT | APR_POLLERR)) + ? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD; + + ares_process_fd(chan_, ifd, ofd); + } + } + +bail_pool: + apr_pool_destroy(pool); + +bail: + return nsds > 0; +} + +bool LLAres::processAll() +{ + bool anyProcessed = false, ret; + + do { + timeval tv; + + ret = ares_timeout(chan_, NULL, &tv) != NULL; + + if (ret) + { + ret = process(tv.tv_sec * 1000000LL + tv.tv_usec); + anyProcessed |= ret; + } + } while (ret); + + return anyProcessed; +} + +int LLAres::expandName(const char *encoded, const char *abuf, size_t alen, + std::string &s, size_t &enclen) +{ + char *t; + int ret; + long e; + + ret = ares_expand_name((const unsigned char *) encoded, + (const unsigned char *) abuf, alen, &t, &e); + if (ret == ARES_SUCCESS) + { + s.assign(t); + enclen = e; + ares_free_string(t); + } + return ret; +} + +const char *LLAres::strerror(int code) +{ + return ares_strerror(code); +} + +LLAres *gAres; + +LLAres *ll_init_ares() +{ + if (gAres == NULL) + { + gAres = new LLAres(); + } + return gAres; +} + +LLDnsRecord::LLDnsRecord(LLResType type, const std::string &name, + unsigned ttl) + : LLRefCount(), + mType(type), + mName(name), + mTTL(ttl) +{ +} + +LLHostRecord::LLHostRecord(LLResType type, const std::string &name, + unsigned ttl) + : LLDnsRecord(type, name, ttl) +{ +} + +int LLHostRecord::parse(const char *buf, size_t len, const char *pos, + size_t rrlen) +{ + int ret; + + ret = LLAres::expandName(pos, buf, len, mHost); + if (ret != ARES_SUCCESS) + { + goto bail; + } + + ret = ARES_SUCCESS; + +bail: + return ret; +} + +LLCnameRecord::LLCnameRecord(const std::string &name, unsigned ttl) + : LLHostRecord(RES_CNAME, name, ttl) +{ +} + +LLPtrRecord::LLPtrRecord(const std::string &name, unsigned ttl) + : LLHostRecord(RES_PTR, name, ttl) +{ +} + +LLAddrRecord::LLAddrRecord(LLResType type, const std::string &name, + unsigned ttl) + : LLDnsRecord(type, name, ttl) +{ +} + +LLARecord::LLARecord(const std::string &name, unsigned ttl) + : LLAddrRecord(RES_A, name, ttl) +{ +} + +int LLARecord::parse(const char *buf, size_t len, const char *pos, + size_t rrlen) +{ + int ret; + + if (rrlen != sizeof(mSA.sin.sin_addr.s_addr)) + { + ret = ARES_EBADRESP; + goto bail; + } + + memset(&mSA, 0, sizeof(mSA)); + memcpy(&mSA.sin.sin_addr.s_addr, pos, rrlen); + mSA.sin.sin_family = AF_INET6; + mSize = sizeof(mSA.sin); + + ret = ARES_SUCCESS; + +bail: + return ret; +} + +LLAaaaRecord::LLAaaaRecord(const std::string &name, unsigned ttl) + : LLAddrRecord(RES_AAAA, name, ttl) +{ +} + +int LLAaaaRecord::parse(const char *buf, size_t len, const char *pos, + size_t rrlen) +{ + int ret; + + if (rrlen != sizeof(mSA.sin6.sin6_addr)) + { + ret = ARES_EBADRESP; + goto bail; + } + + memset(&mSA, 0, sizeof(mSA)); + memcpy(&mSA.sin6.sin6_addr.s6_addr, pos, rrlen); + mSA.sin6.sin6_family = AF_INET6; + mSize = sizeof(mSA.sin6); + + ret = ARES_SUCCESS; + +bail: + return ret; +} + +LLSrvRecord::LLSrvRecord(const std::string &name, unsigned ttl) + : LLHostRecord(RES_SRV, name, ttl) +{ +} + +int LLSrvRecord::parse(const char *buf, size_t len, const char *pos, + size_t rrlen) +{ + int ret; + + if (rrlen < 6) + { + ret = ARES_EBADRESP; + goto bail; + } + + memcpy(&mPriority, pos, 2); + memcpy(&mWeight, pos + 2, 2); + memcpy(&mPort, pos + 4, 2); + + mPriority = ntohs(mPriority); + mWeight = ntohs(mWeight); + mPort = ntohs(mPort); + + ret = LLHostRecord::parse(buf, len, pos + 6, rrlen - 6); + +bail: + return ret; +} + +LLNsRecord::LLNsRecord(const std::string &name, unsigned ttl) + : LLHostRecord(RES_NS, name, ttl) +{ +} + +void LLAres::UriRewriteResponder::queryError(int code) +{ + std::vector uris; + uris.push_back(mUri.asString()); + rewriteResult(uris); +} + +void LLAres::UriRewriteResponder::querySuccess() +{ + std::vector uris; + + if (mType != RES_SRV) + { + goto bail; + } + + for (size_t i = 0; i < mAnswers.size(); i++) + { + const LLSrvRecord *r = (const LLSrvRecord *) mAnswers[i].get(); + + if (r->type() == RES_SRV) + { + // Check the domain in the response to ensure that it's + // the same as the domain in the request, so that bad guys + // can't forge responses that point to their own login + // servers with their own certificates. + + // Hard-coding the domain to check here is a bit of a + // hack. Hoist it to an outer caller if anyone ever needs + // this functionality on other domains. + + static const std::string domain(".lindenlab.com"); + const std::string &host = r->host(); + + std::string::size_type s = host.find(domain) + domain.length(); + + if (s != host.length() && s != host.length() - 1) + { + continue; + } + + LLURI uri(mUri.scheme(), + mUri.userName(), + mUri.password(), + r->host(), + mUri.defaultPort() ? r->port() : mUri.hostPort(), + mUri.escapedPath(), + mUri.escapedQuery()); + uris.push_back(uri.asString()); + } + } + + if (!uris.empty()) + { + goto done; + } + +bail: + uris.push_back(mUri.asString()); + +done: + rewriteResult(uris); +} + +void LLAres::UriRewriteResponder::rewriteResult( + const std::vector &uris) +{ + llinfos << "LLAres::UriRewriteResponder::rewriteResult not implemented" + << llendl; + + for (size_t i = 0; i < uris.size(); i++) + { + llinfos << "[" << i << "] " << uris[i] << llendl; + } +} -- cgit v1.2.3