PageRenderTime 34ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llmessage/llares.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 815 lines | 637 code | 143 blank | 35 comment | 98 complexity | 55e50a3a0f92e85a8052d85b2e22754c MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llares.cpp
  3. * @author Bryan O'Sullivan
  4. * @date 2007-08-15
  5. * @brief Wrapper for asynchronous DNS lookups.
  6. *
  7. * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  8. * Second Life Viewer Source Code
  9. * Copyright (C) 2010, Linden Research, Inc.
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation;
  14. * version 2.1 of the License only.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  26. * $/LicenseInfo$
  27. */
  28. #include "linden_common.h"
  29. #include "llares.h"
  30. #include <ares_dns.h>
  31. #include <ares_version.h>
  32. #include "apr_portable.h"
  33. #include "apr_network_io.h"
  34. #include "apr_poll.h"
  35. #include "llapr.h"
  36. #include "llareslistener.h"
  37. #if defined(LL_WINDOWS)
  38. #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
  39. # define ns_c_in 1
  40. # define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */
  41. # define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */
  42. # define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */
  43. #else
  44. # include <arpa/nameser.h>
  45. #endif
  46. LLAres::HostResponder::~HostResponder()
  47. {
  48. }
  49. void LLAres::HostResponder::hostResult(const hostent *ent)
  50. {
  51. llinfos << "LLAres::HostResponder::hostResult not implemented" << llendl;
  52. }
  53. void LLAres::HostResponder::hostError(int code)
  54. {
  55. llinfos << "LLAres::HostResponder::hostError " << code << ": "
  56. << LLAres::strerror(code) << llendl;
  57. }
  58. LLAres::NameInfoResponder::~NameInfoResponder()
  59. {
  60. }
  61. void LLAres::NameInfoResponder::nameInfoResult(const char *node,
  62. const char *service)
  63. {
  64. llinfos << "LLAres::NameInfoResponder::nameInfoResult not implemented"
  65. << llendl;
  66. }
  67. void LLAres::NameInfoResponder::nameInfoError(int code)
  68. {
  69. llinfos << "LLAres::NameInfoResponder::nameInfoError " << code << ": "
  70. << LLAres::strerror(code) << llendl;
  71. }
  72. LLAres::QueryResponder::~QueryResponder()
  73. {
  74. }
  75. void LLAres::QueryResponder::queryResult(const char *buf, size_t len)
  76. {
  77. llinfos << "LLAres::QueryResponder::queryResult not implemented"
  78. << llendl;
  79. }
  80. void LLAres::QueryResponder::queryError(int code)
  81. {
  82. llinfos << "LLAres::QueryResponder::queryError " << code << ": "
  83. << LLAres::strerror(code) << llendl;
  84. }
  85. LLAres::LLAres() :
  86. chan_(NULL),
  87. mInitSuccess(false),
  88. mListener(new LLAresListener(this))
  89. {
  90. if (ares_library_init( ARES_LIB_INIT_ALL ) != ARES_SUCCESS ||
  91. ares_init(&chan_) != ARES_SUCCESS)
  92. {
  93. llwarns << "Could not succesfully initialize ares!" << llendl;
  94. return;
  95. }
  96. mInitSuccess = true;
  97. }
  98. LLAres::~LLAres()
  99. {
  100. ares_destroy(chan_);
  101. ares_library_cleanup();
  102. }
  103. void LLAres::cancel()
  104. {
  105. ares_cancel(chan_);
  106. }
  107. static void host_callback_1_5(void *arg, int status, int timeouts,
  108. struct hostent *ent)
  109. {
  110. LLPointer<LLAres::HostResponder> *resp =
  111. (LLPointer<LLAres::HostResponder> *) arg;
  112. if (status == ARES_SUCCESS)
  113. {
  114. (*resp)->hostResult(ent);
  115. } else {
  116. (*resp)->hostError(status);
  117. }
  118. delete resp;
  119. }
  120. #if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4
  121. static void host_callback(void *arg, int status, struct hostent *ent)
  122. {
  123. host_callback_1_5(arg, status, 0, ent);
  124. }
  125. #else
  126. # define host_callback host_callback_1_5
  127. #endif
  128. void LLAres::getHostByName(const char *name, HostResponder *resp,
  129. int family)
  130. {
  131. ares_gethostbyname(chan_, name, family, host_callback,
  132. new LLPointer<LLAres::HostResponder>(resp));
  133. }
  134. void LLAres::getSrvRecords(const std::string &name, SrvResponder *resp)
  135. {
  136. search(name, RES_SRV, resp);
  137. }
  138. void LLAres::rewriteURI(const std::string &uri, UriRewriteResponder *resp)
  139. {
  140. llinfos << "Rewriting " << uri << llendl;
  141. resp->mUri = LLURI(uri);
  142. search("_" + resp->mUri.scheme() + "._tcp." + resp->mUri.hostName(),
  143. RES_SRV, resp);
  144. }
  145. LLQueryResponder::LLQueryResponder()
  146. : LLAres::QueryResponder(),
  147. mResult(ARES_ENODATA),
  148. mType(RES_INVALID)
  149. {
  150. }
  151. int LLQueryResponder::parseRR(const char *buf, size_t len, const char *&pos,
  152. LLPointer<LLDnsRecord> &r)
  153. {
  154. std::string rrname;
  155. size_t enclen;
  156. int ret;
  157. // RR name.
  158. ret = LLAres::expandName(pos, buf, len, rrname, enclen);
  159. if (ret != ARES_SUCCESS)
  160. {
  161. return ret;
  162. }
  163. pos += enclen;
  164. if (pos + NS_RRFIXEDSZ > buf + len)
  165. {
  166. return ARES_EBADRESP;
  167. }
  168. int rrtype = DNS_RR_TYPE(pos);
  169. int rrclass = DNS_RR_CLASS(pos);
  170. int rrttl = DNS_RR_TTL(pos);
  171. int rrlen = DNS_RR_LEN(pos);
  172. if (rrclass != ns_c_in)
  173. {
  174. return ARES_EBADRESP;
  175. }
  176. pos += NS_RRFIXEDSZ;
  177. if (pos + rrlen > buf + len)
  178. {
  179. return ARES_EBADRESP;
  180. }
  181. switch (rrtype)
  182. {
  183. case RES_A:
  184. r = new LLARecord(rrname, rrttl);
  185. break;
  186. case RES_NS:
  187. r = new LLNsRecord(rrname, rrttl);
  188. break;
  189. case RES_CNAME:
  190. r = new LLCnameRecord(rrname, rrttl);
  191. break;
  192. case RES_PTR:
  193. r = new LLPtrRecord(rrname, rrttl);
  194. break;
  195. case RES_AAAA:
  196. r = new LLAaaaRecord(rrname, rrttl);
  197. break;
  198. case RES_SRV:
  199. r = new LLSrvRecord(rrname, rrttl);
  200. break;
  201. default:
  202. llinfos << "LLQueryResponder::parseRR got unknown RR type " << rrtype
  203. << llendl;
  204. return ARES_EBADRESP;
  205. }
  206. ret = r->parse(buf, len, pos, rrlen);
  207. if (ret == ARES_SUCCESS)
  208. {
  209. pos += rrlen;
  210. } else {
  211. r = NULL;
  212. }
  213. return ret;
  214. }
  215. int LLQueryResponder::parseSection(const char *buf, size_t len,
  216. size_t count, const char *&pos,
  217. dns_rrs_t &rrs)
  218. {
  219. int ret = ARES_SUCCESS;
  220. for (size_t i = 0; i < count; i++)
  221. {
  222. LLPointer<LLDnsRecord> r;
  223. ret = parseRR(buf, len, pos, r);
  224. if (ret != ARES_SUCCESS)
  225. {
  226. break;
  227. }
  228. rrs.push_back(r);
  229. }
  230. return ret;
  231. }
  232. void LLQueryResponder::queryResult(const char *buf, size_t len)
  233. {
  234. const char *pos = buf;
  235. int qdcount = DNS_HEADER_QDCOUNT(pos);
  236. int ancount = DNS_HEADER_ANCOUNT(pos);
  237. int nscount = DNS_HEADER_NSCOUNT(pos);
  238. int arcount = DNS_HEADER_ARCOUNT(pos);
  239. int ret;
  240. if (qdcount == 0 || ancount + nscount + arcount == 0)
  241. {
  242. ret = ARES_ENODATA;
  243. goto bail;
  244. }
  245. pos += NS_HFIXEDSZ;
  246. for (int i = 0; i < qdcount; i++)
  247. {
  248. std::string ignore;
  249. size_t enclen;
  250. ret = LLAres::expandName(pos, buf, len, i == 0 ? mQuery : ignore,
  251. enclen);
  252. if (ret != ARES_SUCCESS)
  253. {
  254. goto bail;
  255. }
  256. pos += enclen;
  257. if (i == 0)
  258. {
  259. int t = DNS_QUESTION_TYPE(pos);
  260. switch (t)
  261. {
  262. case RES_A:
  263. case RES_NS:
  264. case RES_CNAME:
  265. case RES_PTR:
  266. case RES_AAAA:
  267. case RES_SRV:
  268. mType = (LLResType) t;
  269. break;
  270. default:
  271. llinfos << "Cannot grok query type " << t << llendl;
  272. ret = ARES_EBADQUERY;
  273. goto bail;
  274. }
  275. }
  276. pos += NS_QFIXEDSZ;
  277. if (pos > buf + len)
  278. {
  279. ret = ARES_EBADRESP;
  280. goto bail;
  281. }
  282. }
  283. ret = parseSection(buf, len, ancount, pos, mAnswers);
  284. if (ret != ARES_SUCCESS)
  285. {
  286. goto bail;
  287. }
  288. ret = parseSection(buf, len, nscount, pos, mAuthorities);
  289. if (ret != ARES_SUCCESS)
  290. {
  291. goto bail;
  292. }
  293. ret = parseSection(buf, len, arcount, pos, mAdditional);
  294. bail:
  295. mResult = ret;
  296. if (mResult == ARES_SUCCESS)
  297. {
  298. querySuccess();
  299. } else {
  300. queryError(mResult);
  301. }
  302. }
  303. void LLQueryResponder::querySuccess()
  304. {
  305. llinfos << "LLQueryResponder::queryResult not implemented" << llendl;
  306. }
  307. void LLAres::SrvResponder::querySuccess()
  308. {
  309. if (mType == RES_SRV)
  310. {
  311. srvResult(mAnswers);
  312. } else {
  313. srvError(ARES_EBADRESP);
  314. }
  315. }
  316. void LLAres::SrvResponder::queryError(int code)
  317. {
  318. srvError(code);
  319. }
  320. void LLAres::SrvResponder::srvResult(const dns_rrs_t &ents)
  321. {
  322. llinfos << "LLAres::SrvResponder::srvResult not implemented" << llendl;
  323. for (size_t i = 0; i < ents.size(); i++)
  324. {
  325. const LLSrvRecord *s = (const LLSrvRecord *) ents[i].get();
  326. llinfos << "[" << i << "] " << s->host() << ":" << s->port()
  327. << " priority " << s->priority()
  328. << " weight " << s->weight()
  329. << llendl;
  330. }
  331. }
  332. void LLAres::SrvResponder::srvError(int code)
  333. {
  334. llinfos << "LLAres::SrvResponder::srvError " << code << ": "
  335. << LLAres::strerror(code) << llendl;
  336. }
  337. static void nameinfo_callback_1_5(void *arg, int status, int timeouts,
  338. char *node, char *service)
  339. {
  340. LLPointer<LLAres::NameInfoResponder> *resp =
  341. (LLPointer<LLAres::NameInfoResponder> *) arg;
  342. if (status == ARES_SUCCESS)
  343. {
  344. (*resp)->nameInfoResult(node, service);
  345. } else {
  346. (*resp)->nameInfoError(status);
  347. }
  348. delete resp;
  349. }
  350. #if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4
  351. static void nameinfo_callback(void *arg, int status, char *node, char *service)
  352. {
  353. nameinfo_callback_1_5(arg, status, 0, node, service);
  354. }
  355. #else
  356. # define nameinfo_callback nameinfo_callback_1_5
  357. #endif
  358. void LLAres::getNameInfo(const struct sockaddr &sa, socklen_t salen, int flags,
  359. NameInfoResponder *resp)
  360. {
  361. ares_getnameinfo(chan_, &sa, salen, flags, nameinfo_callback,
  362. new LLPointer<NameInfoResponder>(resp));
  363. }
  364. static void search_callback_1_5(void *arg, int status, int timeouts,
  365. unsigned char *abuf, int alen)
  366. {
  367. LLPointer<LLAres::QueryResponder> *resp =
  368. (LLPointer<LLAres::QueryResponder> *) arg;
  369. if (status == ARES_SUCCESS)
  370. {
  371. (*resp)->queryResult((const char *) abuf, alen);
  372. } else {
  373. (*resp)->queryError(status);
  374. }
  375. delete resp;
  376. }
  377. #if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4
  378. static void search_callback(void *arg, int status, unsigned char *abuf,
  379. int alen)
  380. {
  381. search_callback_1_5(arg, status, 0, abuf, alen);
  382. }
  383. #else
  384. # define search_callback search_callback_1_5
  385. #endif
  386. void LLAres::search(const std::string &query, LLResType type,
  387. QueryResponder *resp)
  388. {
  389. ares_search(chan_, query.c_str(), ns_c_in, type, search_callback,
  390. new LLPointer<QueryResponder>(resp));
  391. }
  392. bool LLAres::process(U64 timeout)
  393. {
  394. if (!gAPRPoolp)
  395. {
  396. ll_init_apr();
  397. }
  398. ares_socket_t socks[ARES_GETSOCK_MAXNUM];
  399. apr_pollfd_t aprFds[ARES_GETSOCK_MAXNUM];
  400. apr_int32_t nsds = 0;
  401. int nactive = 0;
  402. int bitmask;
  403. bitmask = ares_getsock(chan_, socks, ARES_GETSOCK_MAXNUM);
  404. if (bitmask == 0)
  405. {
  406. return nsds > 0;
  407. }
  408. apr_status_t status;
  409. LLAPRPool pool;
  410. status = pool.getStatus() ;
  411. ll_apr_assert_status(status);
  412. for (int i = 0; i < ARES_GETSOCK_MAXNUM; i++)
  413. {
  414. if (ARES_GETSOCK_READABLE(bitmask, i))
  415. {
  416. aprFds[nactive].reqevents = APR_POLLIN | APR_POLLERR;
  417. }
  418. else if (ARES_GETSOCK_WRITABLE(bitmask, i))
  419. {
  420. aprFds[nactive].reqevents = APR_POLLOUT | APR_POLLERR;
  421. } else {
  422. continue;
  423. }
  424. apr_socket_t *aprSock = NULL;
  425. status = apr_os_sock_put(&aprSock, (apr_os_sock_t *) &socks[i], pool.getAPRPool());
  426. if (status != APR_SUCCESS)
  427. {
  428. ll_apr_warn_status(status);
  429. return nsds > 0;
  430. }
  431. aprFds[nactive].desc.s = aprSock;
  432. aprFds[nactive].desc_type = APR_POLL_SOCKET;
  433. aprFds[nactive].p = pool.getAPRPool();
  434. aprFds[nactive].rtnevents = 0;
  435. aprFds[nactive].client_data = &socks[i];
  436. nactive++;
  437. }
  438. if (nactive > 0)
  439. {
  440. status = apr_poll(aprFds, nactive, &nsds, timeout);
  441. if (status != APR_SUCCESS && status != APR_TIMEUP)
  442. {
  443. ll_apr_warn_status(status);
  444. }
  445. for (int i = 0; i < nactive; i++)
  446. {
  447. int evts = aprFds[i].rtnevents;
  448. int ifd = (evts & (APR_POLLIN | APR_POLLERR))
  449. ? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD;
  450. int ofd = (evts & (APR_POLLOUT | APR_POLLERR))
  451. ? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD;
  452. ares_process_fd(chan_, ifd, ofd);
  453. }
  454. }
  455. return nsds > 0;
  456. }
  457. bool LLAres::processAll()
  458. {
  459. bool anyProcessed = false, ret;
  460. do {
  461. timeval tv;
  462. ret = ares_timeout(chan_, NULL, &tv) != NULL;
  463. if (ret)
  464. {
  465. ret = process(tv.tv_sec * 1000000LL + tv.tv_usec);
  466. anyProcessed |= ret;
  467. }
  468. } while (ret);
  469. return anyProcessed;
  470. }
  471. int LLAres::expandName(const char *encoded, const char *abuf, size_t alen,
  472. std::string &s, size_t &enclen)
  473. {
  474. char *t;
  475. int ret;
  476. long e;
  477. ret = ares_expand_name((const unsigned char *) encoded,
  478. (const unsigned char *) abuf, alen, &t, &e);
  479. if (ret == ARES_SUCCESS)
  480. {
  481. s.assign(t);
  482. enclen = e;
  483. ares_free_string(t);
  484. }
  485. return ret;
  486. }
  487. const char *LLAres::strerror(int code)
  488. {
  489. return ares_strerror(code);
  490. }
  491. LLAres *gAres;
  492. LLAres *ll_init_ares()
  493. {
  494. if (gAres == NULL)
  495. {
  496. gAres = new LLAres();
  497. }
  498. return gAres;
  499. }
  500. LLDnsRecord::LLDnsRecord(LLResType type, const std::string &name,
  501. unsigned ttl)
  502. : LLRefCount(),
  503. mType(type),
  504. mName(name),
  505. mTTL(ttl)
  506. {
  507. }
  508. LLHostRecord::LLHostRecord(LLResType type, const std::string &name,
  509. unsigned ttl)
  510. : LLDnsRecord(type, name, ttl)
  511. {
  512. }
  513. int LLHostRecord::parse(const char *buf, size_t len, const char *pos,
  514. size_t rrlen)
  515. {
  516. int ret;
  517. ret = LLAres::expandName(pos, buf, len, mHost);
  518. if (ret != ARES_SUCCESS)
  519. {
  520. goto bail;
  521. }
  522. ret = ARES_SUCCESS;
  523. bail:
  524. return ret;
  525. }
  526. LLCnameRecord::LLCnameRecord(const std::string &name, unsigned ttl)
  527. : LLHostRecord(RES_CNAME, name, ttl)
  528. {
  529. }
  530. LLPtrRecord::LLPtrRecord(const std::string &name, unsigned ttl)
  531. : LLHostRecord(RES_PTR, name, ttl)
  532. {
  533. }
  534. LLAddrRecord::LLAddrRecord(LLResType type, const std::string &name,
  535. unsigned ttl)
  536. : LLDnsRecord(type, name, ttl),
  537. mSize(0)
  538. {
  539. }
  540. LLARecord::LLARecord(const std::string &name, unsigned ttl)
  541. : LLAddrRecord(RES_A, name, ttl)
  542. {
  543. }
  544. int LLARecord::parse(const char *buf, size_t len, const char *pos,
  545. size_t rrlen)
  546. {
  547. int ret;
  548. if (rrlen != sizeof(mSA.sin.sin_addr.s_addr))
  549. {
  550. ret = ARES_EBADRESP;
  551. goto bail;
  552. }
  553. memset(&mSA, 0, sizeof(mSA));
  554. memcpy(&mSA.sin.sin_addr.s_addr, pos, rrlen);
  555. mSA.sin.sin_family = AF_INET6;
  556. mSize = sizeof(mSA.sin);
  557. ret = ARES_SUCCESS;
  558. bail:
  559. return ret;
  560. }
  561. LLAaaaRecord::LLAaaaRecord(const std::string &name, unsigned ttl)
  562. : LLAddrRecord(RES_AAAA, name, ttl)
  563. {
  564. }
  565. int LLAaaaRecord::parse(const char *buf, size_t len, const char *pos,
  566. size_t rrlen)
  567. {
  568. int ret;
  569. if (rrlen != sizeof(mSA.sin6.sin6_addr))
  570. {
  571. ret = ARES_EBADRESP;
  572. goto bail;
  573. }
  574. memset(&mSA, 0, sizeof(mSA));
  575. memcpy(&mSA.sin6.sin6_addr.s6_addr, pos, rrlen);
  576. mSA.sin6.sin6_family = AF_INET6;
  577. mSize = sizeof(mSA.sin6);
  578. ret = ARES_SUCCESS;
  579. bail:
  580. return ret;
  581. }
  582. LLSrvRecord::LLSrvRecord(const std::string &name, unsigned ttl)
  583. : LLHostRecord(RES_SRV, name, ttl),
  584. mPriority(0),
  585. mWeight(0),
  586. mPort(0)
  587. {
  588. }
  589. int LLSrvRecord::parse(const char *buf, size_t len, const char *pos,
  590. size_t rrlen)
  591. {
  592. int ret;
  593. if (rrlen < 6)
  594. {
  595. ret = ARES_EBADRESP;
  596. goto bail;
  597. }
  598. memcpy(&mPriority, pos, 2);
  599. memcpy(&mWeight, pos + 2, 2);
  600. memcpy(&mPort, pos + 4, 2);
  601. mPriority = ntohs(mPriority);
  602. mWeight = ntohs(mWeight);
  603. mPort = ntohs(mPort);
  604. ret = LLHostRecord::parse(buf, len, pos + 6, rrlen - 6);
  605. bail:
  606. return ret;
  607. }
  608. LLNsRecord::LLNsRecord(const std::string &name, unsigned ttl)
  609. : LLHostRecord(RES_NS, name, ttl)
  610. {
  611. }
  612. void LLAres::UriRewriteResponder::queryError(int code)
  613. {
  614. std::vector<std::string> uris;
  615. uris.push_back(mUri.asString());
  616. rewriteResult(uris);
  617. }
  618. void LLAres::UriRewriteResponder::querySuccess()
  619. {
  620. std::vector<std::string> uris;
  621. if (mType != RES_SRV)
  622. {
  623. goto bail;
  624. }
  625. for (size_t i = 0; i < mAnswers.size(); i++)
  626. {
  627. const LLSrvRecord *r = (const LLSrvRecord *) mAnswers[i].get();
  628. if (r->type() == RES_SRV)
  629. {
  630. // Check the domain in the response to ensure that it's
  631. // the same as the domain in the request, so that bad guys
  632. // can't forge responses that point to their own login
  633. // servers with their own certificates.
  634. // Hard-coding the domain to check here is a bit of a
  635. // hack. Hoist it to an outer caller if anyone ever needs
  636. // this functionality on other domains.
  637. static const std::string domain(".lindenlab.com");
  638. const std::string &host = r->host();
  639. std::string::size_type s = host.find(domain) + domain.length();
  640. if (s != host.length() && s != host.length() - 1)
  641. {
  642. continue;
  643. }
  644. LLURI uri(mUri.scheme(),
  645. mUri.userName(),
  646. mUri.password(),
  647. r->host(),
  648. mUri.defaultPort() ? r->port() : mUri.hostPort(),
  649. mUri.escapedPath(),
  650. mUri.escapedQuery());
  651. uris.push_back(uri.asString());
  652. }
  653. }
  654. if (!uris.empty())
  655. {
  656. goto done;
  657. }
  658. bail:
  659. uris.push_back(mUri.asString());
  660. done:
  661. rewriteResult(uris);
  662. }
  663. void LLAres::UriRewriteResponder::rewriteResult(
  664. const std::vector<std::string> &uris)
  665. {
  666. llinfos << "LLAres::UriRewriteResponder::rewriteResult not implemented"
  667. << llendl;
  668. for (size_t i = 0; i < uris.size(); i++)
  669. {
  670. llinfos << "[" << i << "] " << uris[i] << llendl;
  671. }
  672. }