Author: ruschein
Date: 2011-09-20 09:52:23 -0700 (Tue, 20 Sep 2011)
New Revision: 26879

Added:
   csplugins/trunk/ucsd/ruschein/GSFS/include/ThreadSafeLogger.h
   csplugins/trunk/ucsd/ruschein/GSFS/src/Resolver.cc
   csplugins/trunk/ucsd/ruschein/GSFS/src/ThreadSafeLogger.cc
Modified:
   csplugins/trunk/ucsd/ruschein/GSFS/Makefile
   csplugins/trunk/ucsd/ruschein/GSFS/include/StringUtil.h
   csplugins/trunk/ucsd/ruschein/GSFS/progs/FetchPage.cc
   csplugins/trunk/ucsd/ruschein/GSFS/progs/Makefile
   csplugins/trunk/ucsd/ruschein/GSFS/src/ProcessUtil.cc
   csplugins/trunk/ucsd/ruschein/GSFS/src/WebUtil.cc
Log:
First version that can download a web page.

Modified: csplugins/trunk/ucsd/ruschein/GSFS/Makefile
===================================================================
--- csplugins/trunk/ucsd/ruschein/GSFS/Makefile 2011-09-20 16:29:34 UTC (rev 
26878)
+++ csplugins/trunk/ucsd/ruschein/GSFS/Makefile 2011-09-20 16:52:23 UTC (rev 
26879)
@@ -14,4 +14,5 @@
        @$(CXX) $(CXXFLAGS) $< -c -o $@
 
 clean:
-       rm -f objs/*
+       rm -f objs/* lib/*.a
+

Modified: csplugins/trunk/ucsd/ruschein/GSFS/include/StringUtil.h
===================================================================
--- csplugins/trunk/ucsd/ruschein/GSFS/include/StringUtil.h     2011-09-20 
16:29:34 UTC (rev 26878)
+++ csplugins/trunk/ucsd/ruschein/GSFS/include/StringUtil.h     2011-09-20 
16:52:23 UTC (rev 26879)
@@ -81,8 +81,8 @@
 
 #if defined(__linux__)
 const std::string IVIA_STANDARD_LOCALE("en_US.ISO-8859-15");
-#elif defined(__APPLE__)
-const std::string IVIA_STANDARD_LOCALE("en_US.ISO-8859-15");
+#elif defined(__MACH__)
+const std::string IVIA_STANDARD_LOCALE("en_US.ISO8859-15");
 #else
 #      error Your OS is not supported!
 #endif

Added: csplugins/trunk/ucsd/ruschein/GSFS/include/ThreadSafeLogger.h
===================================================================
--- csplugins/trunk/ucsd/ruschein/GSFS/include/ThreadSafeLogger.h               
                (rev 0)
+++ csplugins/trunk/ucsd/ruschein/GSFS/include/ThreadSafeLogger.h       
2011-09-20 16:52:23 UTC (rev 26879)
@@ -0,0 +1,69 @@
+/** \file    ThreadSafeLogger.h
+ *  \brief   Declaration of class ThreadSafeLogger.
+ *  \author  Dr. Johannes Ruscheinski
+ */
+
+/*
+ *  Copyright 2006-2007 Project iVia.
+ *  Copyright 2006-2007 The Regents of The University of California.
+ *
+ *  This file is part of the libiViaCore package.
+ *
+ *  The libiViaCore package is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License as 
published
+ *  by the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libiViaCore is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with libiViaCore; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef THREAD_SAFE_LOGGER_H
+#define THREAD_SAFE_LOGGER_H
+
+
+#include <Logger.h>
+#include <ThreadUtil.h>
+
+
+/** \class ThreadSafeLogger
+ *  \brief A class for logging timestamped messages that is thread-safe.
+ */
+class ThreadSafeLogger: public Logger {
+       ThreadUtil::Mutex mutex_;
+public:
+       /** \brief  Creates a new ThreadSafeLogger object that writes to a log 
file.
+        *  \param  log_filename  The log file name.
+        *  \param  open_mode     Whether to clear the log file upon opening it 
or not.
+        */
+       explicit ThreadSafeLogger(const std::string &log_filename, const 
Logger::VerbosityLevel default_verbosity = Logger::VL_NORMAL,
+                                 const Logger::OpenMode open_mode = 
Logger::DO_NOT_CLEAR)
+               : Logger(log_filename, default_verbosity, open_mode) { }
+
+       /** \brief  Creates a new ThreadSafeLogger object that writes to a log 
file.
+        *  \param  log_filename  The log file name.
+        *  \param  open_mode     Whether to clear the log file upon opening it 
or not.
+        */
+       explicit ThreadSafeLogger(const char * const log_filename, const 
Logger::VerbosityLevel default_verbosity = Logger::VL_NORMAL,
+                                 const Logger::OpenMode open_mode = 
Logger::DO_NOT_CLEAR)
+               : Logger(log_filename, default_verbosity, open_mode) { }
+
+       /** \brief  Creates a new ThreadSafeLogger object that writes to a File.
+        *  \param  log_file   The File to write to.
+        *  \param  open_mode  Whether to clear the log file upon opening it or 
not.
+        */
+       explicit ThreadSafeLogger(File * const log_file, const 
Logger::VerbosityLevel default_verbosity = Logger::VL_NORMAL,
+                                 const Logger::OpenMode open_mode = 
Logger::DO_NOT_CLEAR)
+               : Logger(log_file, default_verbosity, open_mode) { }
+protected:
+       virtual void writeLog(const std::string &message, const unsigned 
log_mask);
+};
+
+
+#endif // ifndef THREAD_SAFE_LOGGER_H

Modified: csplugins/trunk/ucsd/ruschein/GSFS/progs/FetchPage.cc
===================================================================
--- csplugins/trunk/ucsd/ruschein/GSFS/progs/FetchPage.cc       2011-09-20 
16:29:34 UTC (rev 26878)
+++ csplugins/trunk/ucsd/ruschein/GSFS/progs/FetchPage.cc       2011-09-20 
16:52:23 UTC (rev 26879)
@@ -1,5 +1,6 @@
 #include <iostream>
 #include <MsgUtil.h>
+#include <Downloader.h>
 
 
 void PrintUsage() {
@@ -11,5 +12,13 @@
        if (argc != 2)
                PrintUsage();
 
-       std::cout << "Got URL: " << argv[1] << '\n';
+       try {
+               Downloader downloader(argv[1]);
+               if (downloader.anErrorOccurred())
+                       std::cerr << downloader.getLastErrorMessage() << '\n';
+               else
+                       std::cout << downloader.getMessageBody() << '\n';
+       } catch (const std::exception &x) {
+               std::cerr << "** Caught exception: " << x.what() << '\n';
+       }
 }

Modified: csplugins/trunk/ucsd/ruschein/GSFS/progs/Makefile
===================================================================
--- csplugins/trunk/ucsd/ruschein/GSFS/progs/Makefile   2011-09-20 16:29:34 UTC 
(rev 26878)
+++ csplugins/trunk/ucsd/ruschein/GSFS/progs/Makefile   2011-09-20 16:52:23 UTC 
(rev 26879)
@@ -3,7 +3,10 @@
 
 
 FetchPage: FetchPage.o
-       $(CXX) -o $@ $^ -lcurl -lpcre -lssl -L/opt/local/lib -lmagic -lz -L 
../lib -liViaCoreSubset
+       $(CXX) -o $@ $^ -lcurl -lpcre -lssl -lcrypto -L/opt/local/lib -lmagic 
-lz -L ../lib -liViaCoreSubset
 
 FetchPage.o: FetchPage.cc
        $(CXX) $(CXXFLAGS) -c $<
+
+clean:
+       rm -f *.o *~

Modified: csplugins/trunk/ucsd/ruschein/GSFS/src/ProcessUtil.cc
===================================================================
--- csplugins/trunk/ucsd/ruschein/GSFS/src/ProcessUtil.cc       2011-09-20 
16:29:34 UTC (rev 26878)
+++ csplugins/trunk/ucsd/ruschein/GSFS/src/ProcessUtil.cc       2011-09-20 
16:52:23 UTC (rev 26879)
@@ -547,6 +547,7 @@
 typedef char *CharPtr;
 
 
+       /*
 #ifdef __MACH__
 extern char **environ;
 #endif
@@ -565,7 +566,7 @@
                char **argv = (char **)::alloca((args.size() + 1) * sizeof(char 
*));
                argv[0] = ::strdup(::basename(::strdup(args[0].c_str())));
                unsigned arg_no = 1;
-               for (/* Empty! */; arg_no < args.size(); ++arg_no)
+               for (; arg_no < args.size(); ++arg_no)
                        argv[arg_no] = ::strdup(args[arg_no].c_str());
                argv[arg_no] = NULL;
 
@@ -581,7 +582,7 @@
        // If we make it here, we're the parent process:
        *child_pid = pid;
        return master_fd;
-}
+}*/
 
 
 namespace {

Added: csplugins/trunk/ucsd/ruschein/GSFS/src/Resolver.cc
===================================================================
--- csplugins/trunk/ucsd/ruschein/GSFS/src/Resolver.cc                          
(rev 0)
+++ csplugins/trunk/ucsd/ruschein/GSFS/src/Resolver.cc  2011-09-20 16:52:23 UTC 
(rev 26879)
@@ -0,0 +1,1074 @@
+/** \file    Resolver.cc
+ *  \brief   Implementation of class Resolver.  Based on RFC1035.
+ *  \author  Dr. Johannes Ruscheinski
+ *  \author  Jiangtao Hu
+ */
+
+/*
+ *  Copyright 2005-2008 Project iVia.
+ *  Copyright 2005-2008 The Regents of The University of California.
+ *
+ *  This file is part of the libiViaCore package.
+ *
+ *  The libiViaCore package is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License as 
published
+ *  by the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libiViaCore is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with libiViaCore; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <Resolver.h>
+#include <fstream>
+#include <map>
+#include <stdexcept>
+#include <cerrno>
+#include <ctime>
+#include <inttypes.h>
+#include <arpa/nameser_compat.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <Compiler.h>
+#include <File.h>
+#include <FileDescriptor.h>
+#include <FileUtil.h>
+#include <IniFile.h>
+#include <MiscUtil.h>
+#include <NetUtil.h>
+#include <SocketUtil.h>
+#include <StringUtil.h>
+#include <ThreadSafeLogger.h>
+#include <ThreadUtil.h>
+#include <TimerUtil.h>
+
+
+uint16_t Resolver::next_request_id_;
+
+
+namespace {
+
+
+ThreadUtil::Mutex resolver_mutex;
+
+
+std::string GetEtcDir() {
+#ifdef __MACH__
+       return MiscUtil::GetEnv("HOME") + std::string("/etc");
+#else
+       return ETC_DIR;
+#endif
+}
+
+
+} // unnamed namespace
+
+
+bool Resolver::Cache::lookup(const std::string &hostname, std::set<in_addr_t> 
* const ip_addresses)
+{
+       GNU_HASH_MAP<std::string, CacheEntry>::iterator 
entry(resolved_hostnames_cache_.find(hostname));
+       if (entry != resolved_hostnames_cache_.end()) {
+               const time_t now(std::time(NULL));
+               if (entry->second.expire_time_ > now) {
+                       *ip_addresses = entry->second.ip_addresses_;
+                       return true;
+               }
+
+               // Entry has expired => remove it from the cache:
+               resolved_hostnames_cache_.erase(entry);
+       }
+
+       return false;
+}
+
+
+void Resolver::Cache::insert(const std::string &hostname, const 
std::set<in_addr_t> &ip_addresses, const uint32_t ttl)
+{
+       // Flush the cache if it contains more than 100,000 entries:
+       if (resolved_hostnames_cache_.size() > 100000)
+               resolved_hostnames_cache_.clear();
+       else {
+               // Check to see whether we already have information about this 
"hostname":
+               GNU_HASH_MAP<std::string, CacheEntry>::iterator 
cache_entry(resolved_hostnames_cache_.find(hostname));
+               if (unlikely(cache_entry != resolved_hostnames_cache_.end())) {
+                       for (std::set<in_addr_t>::const_iterator 
ip_address(ip_addresses.begin());
+                            ip_address != ip_addresses.end(); ++ip_address)
+                               
cache_entry->second.ip_addresses_.insert(*ip_address);
+                       return;
+               }
+       }
+
+       // Create a new cache entry:
+       const time_t now(std::time(NULL));
+       const Cache::CacheEntry new_cache_entry(now + ttl, ip_addresses);
+       resolved_hostnames_cache_.insert(std::make_pair<std::string, 
CacheEntry>(hostname, new_cache_entry));
+}
+
+
+Resolver::Resolver(const std::list<std::string> &dns_servers, Logger * const 
logger, const unsigned verbosity)
+       : verbosity_(logger != NULL ? verbosity : 0), logger_(logger), 
cleanup_logger_(false), udp_fd_(-1),
+         reply_packet_(NULL), reply_packet_size_(0)
+{
+       // This variable keeps track of whether we have read in a
+       // list of DNS servers from one of the three sources yet:
+       bool dns_servers_processed = false;
+
+       // Get the resolver IP addresses from the "dns_server" parameter:
+       if (not dns_servers.empty()) {
+               for (std::list<std::string>::const_iterator 
dns_server(dns_servers.begin());
+                    dns_server != dns_servers.end(); ++dns_server)
+               {
+                       in_addr_t ip_address;
+                       if (unlikely(not 
NetUtil::StringToNetworkAddress(*dns_server, &ip_address)))
+                               throw Exception("in Resolver::Resolver: \"" + 
*dns_server
+                                               + "\" is not a valid IP address 
(1)!");
+                       dns_server_ip_addresses_and_busy_counts_.insert(
+                               std::make_pair<in_addr_t, unsigned>(ip_address, 
0));
+               }
+
+               dns_servers_processed = true;
+       }
+
+       // If a Resolver.conf file exists, read it:
+       if (FileUtil::Exists(GetEtcDir() + "/Resolver.conf")) {
+               IniFile ini_file(GetEtcDir() + "/Resolver.conf");
+
+               if (logger_ == NULL) {
+                       // Read logging instructions:
+                       verbosity_ = ini_file.getUnsigned("Logging", 
"verbosity", 0);
+                       if (verbosity_ > 5)
+                               verbosity_ = 5;
+                       if (verbosity_ > 0) {
+                               const std::string 
log_filename(ini_file.getString("Logging", "log_filename"));
+                               logger_ = new ThreadSafeLogger(log_filename);
+                               cleanup_logger_ = true;
+                       }
+               }
+
+               // Read DNS servers:
+               if (not dns_servers_processed and 
ini_file.sectionIsDefined("DNS Servers")) {
+                       // Add each entry in the [DNS Servers] section of 
Resolver.conf:
+                       const std::list<std::string> 
names(ini_file.getSectionEntryNames("DNS Servers"));
+                       for (std::list<std::string>::const_iterator 
name(names.begin()); name != names.end(); ++name) {
+                               const std::string 
ip_address_str(ini_file.getString("DNS Servers", *name));
+                               in_addr_t ip_address;
+                               if (unlikely(not 
NetUtil::StringToNetworkAddress(ip_address_str, &ip_address)))
+                                       throw Exception("Resolver.conf: \"" + 
ip_address_str + "\""
+                                                                " is not a 
valid IP address (2)!");
+                               dns_server_ip_addresses_and_busy_counts_.insert(
+                                       std::make_pair<in_addr_t, 
unsigned>(ip_address, 0));
+                       }
+
+                       dns_servers_processed = true;
+               }
+       }
+
+       // As a last resort, attempt to get the resolver IP addresses from 
/etc/resolv.conf:
+       if (not dns_servers_processed) {
+               std::vector<in_addr_t> server_ip_addresses;
+               Resolver::GetServersFromResolvDotConf(&server_ip_addresses);
+               for (std::vector<in_addr_t>::const_iterator 
server_address(server_ip_addresses.begin());
+                    server_address != server_ip_addresses.end(); 
++server_address)
+                       dns_server_ip_addresses_and_busy_counts_.insert(
+                               std::make_pair<in_addr_t, 
unsigned>(*server_address, 0));
+
+               dns_servers_processed = true;
+       }
+
+       // Ensure we have at least one DNS Server:
+       if (dns_server_ip_addresses_and_busy_counts_.empty())
+               throw Exception("in Resolver::Resolver: no DNS Servers found");
+
+       initUdpSocket();
+}
+
+
+Resolver::Resolver(const std::string &dns_server, Logger * const logger, const 
unsigned verbosity)
+       : verbosity_(logger != NULL ? verbosity : 0), logger_(logger), 
cleanup_logger_(false)
+{
+       in_addr_t ip_address;
+       if (unlikely(not NetUtil::StringToNetworkAddress(dns_server, 
&ip_address)))
+               throw Exception("in Resolver::Resolver: \"" + dns_server + "\" 
is not a valid IP address (3)!");
+
+       
dns_server_ip_addresses_and_busy_counts_.insert(std::make_pair<in_addr_t, 
unsigned>(ip_address, 0));
+
+       initUdpSocket();
+}
+
+
+Resolver::Resolver(const in_addr_t dns_server, Logger * const logger, const 
unsigned verbosity)
+       : verbosity_(logger != NULL ? verbosity : 0), logger_(logger), 
cleanup_logger_(false)
+{
+       
dns_server_ip_addresses_and_busy_counts_.insert(std::make_pair<in_addr_t, 
unsigned>(dns_server, 0));
+
+       initUdpSocket();
+}
+
+
+Resolver::~Resolver()
+{
+       if (udp_fd_ != -1)
+               ::close(udp_fd_);
+       delete [] reply_packet_;
+
+       if (cleanup_logger_)
+               delete logger_;
+}
+
+
+void Resolver::initUdpSocket()
+{
+       udp_fd_ = ::socket(PF_INET, SOCK_DGRAM, 0);
+       if (unlikely(udp_fd_ == -1))
+               throw Exception("in Resolver::Resolver: socket(2) failed (" + 
MsgUtil::ErrnoToString() + ")!");
+
+       // Turn off blocking because we are going to use select(2) which on 
Linux doesn't reliably work with
+       // blocking file descriptors:
+       FileUtil::SetNonblocking(udp_fd_);
+
+       // Allocate a buffer to hold UDP DNS server replies and make sure that 
it is 4-byte aligned:
+       const size_t MAX_UDP_REPLY_PACKET_SIZE(512);
+       reply_packet_ = reinterpret_cast<byte *>(new 
uint32_t[(MAX_UDP_REPLY_PACKET_SIZE + sizeof(uint32_t) - 1)
+                                                             / 
sizeof(uint32_t)]);
+       reply_packet_size_ = MAX_UDP_REPLY_PACKET_SIZE;
+}
+
+
+namespace {
+
+
+// IsValidHostname -- sloppy implementation that only tests whether the 
overall hostname is too long.
+//
+inline bool IsValidHostname(const std::string &hostname)
+{
+       return hostname.length() <= 255;
+}
+
+
+} // unnamed namespace
+
+
+void Resolver::submitRequest(const std::string &mixed_case_hostname)
+{
+       // If "name" is a dotted quad, we can just store it for future 
reference by poll():
+       in_addr_t network_address;
+       if (NetUtil::StringToNetworkAddress(mixed_case_hostname, 
&network_address)) {
+               std::set<in_addr_t> network_addresses;
+               network_addresses.insert(network_address);
+               resolved_addresses_.push_back(Result(RESOLVED, 
mixed_case_hostname, network_addresses));
+               return;
+       }
+
+       const std::string hostname(StringUtil::ToLower(mixed_case_hostname));
+       if (unlikely(not IsValidHostname(hostname)))
+               throw Exception("in Resolver::submitRequest: \"" + hostname + 
"\" is not a valid hostname!");
+
+       // Get the next request ID in a threadsafe manner:
+       uint16_t request_id;
+       {
+               ThreadUtil::MutexLocker mutex_locker(&resolver_mutex);
+               request_id = Resolver::next_request_id_;
+               ++Resolver::next_request_id_;
+       }
+
+       // Create the request packet:
+       unsigned char packet[512];
+       const ptrdiff_t packet_size(Resolver::GenerateRequestPacket(hostname, 
request_id, packet));
+
+       // Find the least loaded server:
+       GNU_HASH_MAP<in_addr_t, unsigned>::const_iterator server_and_busy_count(
+               dns_server_ip_addresses_and_busy_counts_.begin());
+       in_addr_t least_loaded_server(server_and_busy_count->first);
+       unsigned lowest_busy_count_so_far(server_and_busy_count->second);
+       for (/* Empty! */; server_and_busy_count != 
dns_server_ip_addresses_and_busy_counts_.end();
+                        ++server_and_busy_count)
+       {
+               if (server_and_busy_count->second < lowest_busy_count_so_far) {
+                       lowest_busy_count_so_far = 
server_and_busy_count->second;
+                       least_loaded_server = server_and_busy_count->first;
+               }
+       }
+
+       // Now submit the request...
+       sendUdpRequest(least_loaded_server, packet, 
static_cast<unsigned>(packet_size));
+
+       // ...and update the busy count of the server we used.
+       GNU_HASH_MAP<in_addr_t, unsigned>::iterator least_loaded_server_iter(
+               
dns_server_ip_addresses_and_busy_counts_.find(least_loaded_server));
+       ++least_loaded_server_iter->second;
+}
+
+
+void Resolver::poll(std::list<Result> * const results)
+{
+       *results = resolved_addresses_;
+       resolved_addresses_.clear();
+
+       ssize_t read_retcode;
+       unsigned char packet[1000];
+       const TimeLimit time_limit(0);
+       while ((read_retcode = SocketUtil::TimedRead(udp_fd_, time_limit, 
packet, sizeof(packet))) > 0) {
+               std::set<std::string> domainnames;
+               std::set<in_addr_t> ip_addresses;
+               uint32_t ttl;
+               uint16_t reply_id;
+               bool truncated;
+               if (DecodeReply(packet, read_retcode, &domainnames, 
&ip_addresses, &ttl, &reply_id, &truncated, logger_, verbosity_)) {
+                       // For now, we ignore truncated packets.  We should 
really send a TCP request instead:
+                       if (truncated)
+                               continue;
+
+                       for (std::set<std::string>::const_iterator 
domainname(domainnames.begin()); domainname!= domainnames.end(); ++domainname)
+                               cache_.insert(*domainname, ip_addresses, ttl);
+               }
+       }
+}
+
+
+namespace { // helper functions for logging
+
+
+void LogIpAddresses(const std::string &domainname, const std::string &event, 
const std::set<in_addr_t> &ip_addresses, const TimeLimit &time_limit,
+                   Logger * const logger)
+{
+       if (logger == NULL)
+               return;
+
+       std::string result(domainname + " (" + 
StringUtil::ToString(time_limit.getRemainingTime()) + "ms): " + event + ":");
+       for (std::set<in_addr_t>::const_iterator ip(ip_addresses.begin()); ip 
!= ip_addresses.end(); ++ip)
+               result += " " + NetUtil::NetworkAddressToString(*ip);
+
+       logger->log(result);
+}
+
+
+void LogIpAddress(const std::string &domainname, const std::string &event,
+                 const in_addr_t &ip_address, const TimeLimit &time_limit, 
Logger * const logger)
+{
+
+       logger->log(domainname + " (" + 
StringUtil::ToString(time_limit.getRemainingTime()) + "ms): "
+                   + event + ": " + 
NetUtil::NetworkAddressToString(ip_address));
+}
+
+
+} // unnamed namespace
+
+
+bool Resolver::resolve(const std::string &mixed_case_domainname, const 
TimeLimit &time_limit, std::set<in_addr_t> * const ip_addresses)
+{
+       ip_addresses->clear();
+       const std::string 
domainname(StringUtil::ToLower(mixed_case_domainname));
+
+       if (verbosity_ >= 3)
+               logger_->log("%s (%ums): Requested...", domainname.c_str(), 
time_limit.getRemainingTime());
+
+       // Check to see whether "domainname" is already an IP address:
+        in_addr domainname_as_address;
+        if (::inet_aton(domainname.c_str(), &domainname_as_address)) {
+               ip_addresses->insert(domainname_as_address.s_addr);
+               if (verbosity_ >= 4)
+                       LogIpAddress(domainname, "Already an address", 
domainname_as_address.s_addr, time_limit, logger_);
+               return true;
+       }
+
+       // First see if we already know the answer to our query:
+       if (cache_.lookup(domainname, ip_addresses)) {
+               if (verbosity_ >= 4)
+                       LogIpAddresses(domainname, "In cache", *ip_addresses, 
time_limit, logger_);
+               return true;
+       }
+
+       // Decide which resolver we should use and get the next request ID in a 
threadsafe manner:
+       uint16_t request_id;
+       static size_t next_resolver_index(::getpid());
+       in_addr_t selected_dns_server;
+       {
+               ThreadUtil::MutexLocker mutex_locker(&resolver_mutex);
+
+               // 1. Decide which resolver we should use:
+               const size_t 
resolver_count(dns_server_ip_addresses_and_busy_counts_.size());
+               next_resolver_index = (next_resolver_index + 1) % 
resolver_count;
+               GNU_HASH_MAP<in_addr_t, unsigned>::const_iterator 
dns_server_ip_address_and_busy_count(
+                       dns_server_ip_addresses_and_busy_counts_.begin());
+               for (unsigned i = 0; i < next_resolver_index; ++i)
+                       ++dns_server_ip_address_and_busy_count;
+               selected_dns_server = 
dns_server_ip_address_and_busy_count->first;
+               if (verbosity_ >= 5)
+                       LogIpAddress(domainname, "DNS Server", 
selected_dns_server, time_limit, logger_);
+
+               // 2. Get the next request ID in a threadsafe manner:
+               request_id = Resolver::next_request_id_;
+               ++Resolver::next_request_id_;
+       }
+
+       // Create the request packet:
+       unsigned char packet[512] __attribute__((aligned(sizeof(uint32_t))));
+       const ptrdiff_t packet_size(Resolver::GenerateRequestPacket(domainname, 
request_id, packet));
+
+       // Now submit the request...
+       if (verbosity_ >= 5)
+               logger_->log("in resolve: about to send UPD request (ID %u).", 
request_id);
+       sendUdpRequest(selected_dns_server, packet, 
static_cast<unsigned>(packet_size));
+
+       // Now wait for the server's response:
+       for (;;) {
+               if (verbosity_ >= 5)
+                       logger_->log("in resolve: remaining time %u ms", 
time_limit.getRemainingTime());
+               ssize_t actual_packet_size(SocketUtil::TimedRead(udp_fd_, 
time_limit, reply_packet_, reply_packet_size_));
+process_reply_packet:
+               if (actual_packet_size <= 0) {
+                       if (errno == ETIMEDOUT) {
+                               if (verbosity_ >= 4)
+                                       logger_->log("%s failed: 
actual_packet_size <= 0, (timed out)", domainname.c_str());
+                               return false;
+                       }
+                       else {
+                               if (verbosity_ >= 4)
+                                       logger_->log("in resolve: resolving of 
\"" + domainname + "\" failed: actual_packet_size <= 0, looping ("
+                                                    + MsgUtil::ErrnoToString() 
+ ")!");
+                               continue;
+                       }
+               }
+
+               std::set<std::string> resolved_domainnames;
+               uint32_t ttl;
+               uint16_t reply_id;
+               bool truncated;
+               if (DecodeReply(reply_packet_, actual_packet_size, 
&resolved_domainnames, ip_addresses, &ttl, &reply_id, &truncated, logger_, 
verbosity_))
+               {
+                       if (ip_addresses->empty()) {
+                               if (reply_id == request_id)
+                                       return false;
+                               else
+                                       continue;
+                       }
+
+                       for (std::set<std::string>::const_iterator 
resolved_domainname(resolved_domainnames.begin());
+                            resolved_domainname != resolved_domainnames.end(); 
++resolved_domainname)
+                       {
+                               if (not truncated)
+                                       cache_.insert(*resolved_domainname, 
*ip_addresses, ttl);
+                       }
+
+                       // Make sure we got the reply we were waiting for:
+                       if (reply_id != request_id) {
+                               if (verbosity_ >= 5)
+                                       logger_->log("in resolve: discarding 
reply with unexpected ID: %u.", reply_id);
+                               ip_addresses->clear();
+                               continue;
+                       }
+
+                       if (truncated) { // => We attempt to get the 
information using TCP instead of UDP.
+                               if (verbosity_ >= 4)
+                                       logger_->log("in resolve: UDP packet 
was truncated.  Attempting TCP request.");
+                               FileDescriptor 
tcp_fd(sendTcpRequest(selected_dns_server, time_limit, packet, 
static_cast<unsigned>(packet_size)));
+                               if (tcp_fd == -1) {
+                                       if (verbosity_ >= 4)
+                                               logger_->log("in resolve: 
failed to send request " + MsgUtil::ErrnoToString() + "!");
+                                       return false;
+                               }
+
+                               if (verbosity_ >= 5)
+                                       logger_->log("in resolve: remaining 
time %u ms.", time_limit.getRemainingTime());
+                               if (unlikely(not FileUtil::SetBlocking(tcp_fd)))
+                                       throw Exception("in Resolver::resolve: 
FileUtil::SetBlocking() failed (" + MsgUtil::ErrnoToString() + ")!");
+                               uint16_t reply_data_size;
+                               SocketUtil::TimedRead(tcp_fd, time_limit, 
&reply_data_size, sizeof(reply_data_size));
+                               reply_data_size = ntohs(reply_data_size);
+                               if (reply_data_size > reply_packet_size_) {
+                                       delete [] reply_packet_;
+                                       reply_packet_ = NULL;
+                                       reply_packet_ = reinterpret_cast<byte 
*>(new uint32_t[(reply_data_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)]);
+                                       reply_packet_size_ = reply_data_size;
+                               }
+
+                               actual_packet_size = reply_data_size;
+                               ssize_t byte_got(0);
+                               byte *buf_pointer = reply_packet_;
+
+                               while (reply_data_size > 0 and (byte_got = 
SocketUtil::TimedRead(tcp_fd, time_limit, buf_pointer, reply_data_size)) > 0) {
+                                       reply_data_size = 
static_cast<uint16_t>(reply_data_size - byte_got);
+                                       buf_pointer += byte_got;
+                               }
+
+                               if (byte_got == -1) {
+                                       if (verbosity_ >= 4)
+                                               logger_->log("in resolve: 
SocketUtil::TimedRead() failed: " + MsgUtil::ErrnoToString() + "!");
+                                       return false;
+                               }
+                               if (verbosity_ >= 5)
+                                       logger_->log("in resolve: 
actual_packet_size = %zd.", actual_packet_size);
+
+                               goto process_reply_packet;
+                       }
+
+                       if (verbosity_ >= 4)
+                               LogIpAddresses(domainname, "Reply", 
*ip_addresses, time_limit, logger_);
+                       return true;
+               }
+               else {
+                       if (verbosity_ >= 4)
+                               logger_->log("%s Failed: could not decode 
reply", domainname.c_str());
+                       return false;
+               }
+       }
+}
+
+
+// Resolver::generateRequestPacket -- generates a DNS query packet.  See 
RFC1035 for details.
+//
+ptrdiff_t Resolver::GenerateRequestPacket(const std::string &hostname, const 
uint16_t request_id, unsigned char * const packet)
+{
+       // Initialise the query header:
+       HEADER * const dns_header = reinterpret_cast<HEADER *>(packet);
+       std::memset(dns_header, '\0', sizeof(HEADER));
+       dns_header->id = htons(request_id);
+       dns_header->rd = 1; // Request recursion.
+       dns_header->qdcount = htons(1);
+
+       // Split the hostname into "labels" and append them to the packet 
header:
+       std::list<std::string> labels;
+       StringUtil::Split(hostname, '.', &labels, /* suppress_empty_components 
= */ false);
+       unsigned char *cp = packet + sizeof(HEADER);
+       for (std::list<std::string>::const_iterator label(labels.begin()); 
label != labels.end(); ++label) {
+               *cp++ = static_cast<unsigned char>(label->length());
+               for (const char *label_cp(label->c_str()); *label_cp != '\0'; 
++label_cp)
+                       *cp++ = *label_cp;
+       }
+       *cp++ = 0;
+
+       uint16_t *sp = reinterpret_cast<uint16_t *>(cp);
+       *sp++ = htons(1); // Set the qtype field to type "A", indicating a host 
address.
+       *sp++ = htons(1); // Set the qclass field to type "IN", indicating the 
Internet.
+
+       return reinterpret_cast<unsigned char *>(sp) - packet;
+}
+
+
+void Resolver::sendUdpRequest(const in_addr_t resolver_ip_address, const 
unsigned char * const packet, const unsigned packet_size) const
+{
+       if (unlikely(not SocketUtil::SendUdpRequest(udp_fd_, 
resolver_ip_address, 53 /* DNS service port */, packet, packet_size)))
+               throw Exception("in Resolver::sendUdpRequest: sending a UDP 
request failed (" + MsgUtil::ErrnoToString() + ")!");
+}
+
+
+int Resolver::sendTcpRequest(const in_addr_t resolver_ip_address, const 
TimeLimit &time_limit, const unsigned char * const packet, const unsigned 
packet_size) const
+{
+       SocketUtil::NagleOptionType nagle_option_type;
+#ifdef TCP_CORK
+       nagle_option_type = SocketUtil::USE_NAGLE;
+#else
+       nagle_option_type = SocketUtil::DISABLE_NAGLE;
+#endif
+       std::string error_message;
+        const int socket_fd(SocketUtil::TcpConnect(resolver_ip_address, 53, 
time_limit, &error_message, nagle_option_type));
+        if (socket_fd == -1) {
+                if (verbosity_ >= 4)
+                        logger_->log("in Resolver::sendTcpRequest: 
SocketUtil::TcpConnect() failed (" + error_message + ")!");
+                return -1;
+        }
+
+#ifdef TCP_CORK
+       int optval(1);
+       if (unlikely(::setsockopt(socket_fd, SOL_TCP, TCP_CORK, &optval, sizeof 
optval) == -1))
+               throw Exception("in Resolver::sendTcpRequest: setsockopt(2) 
failed (1) (" + MsgUtil::ErrnoToString() + ")!");
+#endif
+
+       uint16_t buf_size((uint16_t)(htons(packet_size)));
+       if (SocketUtil::TimedWrite(socket_fd, time_limit, &buf_size, 
sizeof(buf_size)) == -1) {
+                if (verbosity_ >= 4)
+                        logger_->log("in Resolver::sendTcpRequest: 
SocketUtil::TimedWrite() failed (" + MsgUtil::ErrnoToString() + ")!");
+               ::close(socket_fd);
+                return -1;
+       }
+
+       if (SocketUtil::TimedWrite(socket_fd, time_limit, packet, packet_size) 
== -1) {
+                if (verbosity_ >= 4)
+                        logger_->log("in Resolver::sendTcpRequest: 
SocketUtil::TimedWrite() failed (" + MsgUtil::ErrnoToString() + ")!");
+               ::close(socket_fd);
+                return -1;
+       }
+
+#ifdef TCP_CORK
+       optval = 0;
+       if (unlikely(::setsockopt(socket_fd, SOL_TCP, TCP_CORK, &optval, sizeof 
optval) == -1))
+               throw Exception("in Resolver::sendTcpRequest: setsockopt(2) 
failed (2) (" + MsgUtil::ErrnoToString() + ")!");
+#endif
+
+       return socket_fd;
+}
+
+
+namespace {
+
+
+// ExtractDomainName -- extract a domain name from a DNS server response.  See 
RFC1035 for details.
+//
+//                      The trickiest part is that at any point in decoding we 
can encounter what the RFC calls "message
+//                      compression".  This is indicated that the two highest 
bits of a 16 bit quantity are set.  The
+//                      lower 14 bits then indicate an offset relative to the 
start of the packet.  Message compression
+//                      can occurr more than once withing one domainname.
+//
+std::string ExtractDomainName(const unsigned char * const packet_start, const 
unsigned char *&cp)
+{
+       std::string domain_name;
+       bool first_label(true);
+       while (*cp != 0) {
+               if (not first_label)
+                       domain_name += '.';
+               else
+                       first_label = false;
+
+               // Message compression?
+               if ((*cp & 0xC0u) == 0xC0u) { // 2 high-order bits are set => 
message compression!
+                       union {
+                               unsigned char bytes_[2];
+                               uint16_t u16_;
+                       } overlay;
+                       overlay.bytes_[0] = static_cast<unsigned char>(*cp & 
~0xC0u);
+                       ++cp;
+                       overlay.bytes_[1] = *cp;
+                       ++cp;
+                       const uint16_t offset(ntohs(overlay.u16_));
+                       const unsigned char *cp1(packet_start + offset);
+                       return domain_name + ExtractDomainName(packet_start, 
cp1);
+               }
+
+               const std::string::size_type label_length(*cp++);
+               domain_name += std::string(reinterpret_cast<const char *>(cp), 
label_length);
+               cp += label_length;
+       }
+       ++cp; // Skip over trailing zero byte.
+
+       return domain_name;
+}
+
+
+} // unnamed namespace
+
+
+bool Resolver::DecodeReply(const unsigned char * const packet_start, const 
size_t packet_size, std::set<std::string> * const domainnames,
+                          std::set<in_addr_t> * const ip_addresses, uint32_t * 
const ttl, uint16_t * const reply_id, bool * const truncated,
+                          Logger * const logger, const unsigned _verbosity)
+{
+       // Set the actual verbosity:
+       const unsigned verbosity(logger != NULL ? _verbosity : 0);
+
+       if (unlikely(packet_size < sizeof(HEADER))) {
+               if (logger != NULL and verbosity > 2)
+                       logger->log("in Resolver::DecodeReply: got a short 
packet!");
+               return false;
+       }
+
+       domainnames->clear();
+       const HEADER *dns_header(reinterpret_cast<const HEADER 
*>(packet_start));
+       *reply_id = ntohs(dns_header->id);
+       *truncated = dns_header->tc;
+       if (verbosity >= 5)
+               logger->log("in Resolver::DecodeReply: TC bit is %d", 
(*truncated ? 1 : 0));
+       if (unlikely(*truncated))
+               return true; // Need to retry using TCP?
+
+       // We set the "recursion desired" bit and therefore also expect it in 
our reply:
+       if (unlikely(dns_header->rd != 1))
+               return false;
+
+       switch (dns_header->rcode) {
+       case 0:
+               /* We succeeded! */
+               break;
+       case 1:
+               if (verbosity >= 2)
+                       logger->log("in Resolver::DecodeReply: the server 
indicated that we sent an invalid request!");
+       default:
+               /* Some kind of error condition that we ignore. */
+               return false;
+       }
+
+       const uint16_t qdcount = ntohs(dns_header->qdcount);
+       const uint16_t ancount = ntohs(dns_header->ancount);
+
+       // Skip over the question section:
+       const unsigned char *cp(packet_start + sizeof(HEADER));
+       for (unsigned question_record_index(0); question_record_index < 
qdcount; ++question_record_index) {
+               const std::string 
query_domainname(ExtractDomainName(packet_start, cp));
+               domainnames->insert(query_domainname);
+
+               //const uint16_t rr_type(ntohs(*reinterpret_cast<const uint16_t 
*>(cp)));
+               cp += sizeof(uint16_t);
+
+               //const uint16_t rr_class(ntohs(*reinterpret_cast<const 
uint16_t *>(cp)));
+               cp += sizeof(uint16_t);
+       }
+
+       for (unsigned resource_record_index(0); resource_record_index < 
ancount; ++resource_record_index) {
+               const std::string rr_domainname(ExtractDomainName(packet_start, 
cp));
+
+               const uint16_t rr_type(ntohs(*reinterpret_cast<const uint16_t 
*>(cp)));
+               cp += sizeof(uint16_t);
+
+               const uint16_t rr_class(ntohs(*reinterpret_cast<const uint16_t 
*>(cp)));
+               cp += sizeof(uint16_t);
+
+               *ttl = *reinterpret_cast<const uint32_t * const>(cp);
+               *ttl = ntohl(*ttl);
+               cp += sizeof(uint32_t);
+
+               const uint16_t rdlength(ntohs(*reinterpret_cast<const uint16_t 
*>(cp)));
+               cp += sizeof(uint16_t);
+
+               if (rr_type == 1 and rr_class == 1) { // Type == A and class == 
IN.
+                       if (rdlength != sizeof(in_addr_t)) {
+                               if (verbosity >= 2)
+                                       logger->log("in Resolver::DecodeReply: 
unexpected \"rdlength\" for A type RR record!");
+                               return false;
+                       }
+
+                       domainnames->insert(rr_domainname);
+                       in_addr_t ip_address;
+                       std::memcpy(&ip_address, cp, sizeof(in_addr_t)); // 
Intentionally returned in network byte order!
+                       ip_addresses->insert(ip_address);
+                       cp += rdlength;
+
+                       if (verbosity >= 5)
+                               logger->log("in Resolver::DecodeReply: %s: %s", 
rr_domainname.c_str(),
+                                           
NetUtil::NetworkAddressToString(ip_address).c_str());
+               }
+               else if (rr_type == 5 and rr_class == 1) { // Type == CNAME and 
class == IN.
+                       const std::string 
cname_domainname(ExtractDomainName(packet_start, cp));
+                       domainnames->insert(cname_domainname);
+                       if (verbosity >= 5)
+                               logger->log("in Resolver::DecodeReply: %s: 
CNAME", rr_domainname.c_str());
+
+               }
+               else // Nothing we're interested in.
+                       cp += rdlength; // Skip over the rdata field.
+       }
+
+       return true;
+}
+
+
+bool Resolver::ServerIsAlive(const std::string &server_ip_address, const 
std::string &hostname_to_resolve,
+                            const unsigned time_limit, Logger * const logger, 
const unsigned verbosity)
+{
+       Resolver resolver(server_ip_address, logger, verbosity);
+
+       std::set<in_addr_t> ip_addresses;
+       if (not resolver.resolve(hostname_to_resolve, time_limit, 
&ip_addresses))
+               return false;
+
+       return not ip_addresses.empty();
+}
+
+
+bool Resolver::ServerIsAlive(const in_addr_t server_ip_address, const 
std::string &hostname_to_resolve,
+                            const unsigned time_limit, Logger * const logger, 
const unsigned verbosity)
+{
+       Resolver resolver(server_ip_address, logger, verbosity);
+
+       std::set<in_addr_t> ip_addresses;
+       if (not resolver.resolve(hostname_to_resolve, time_limit, 
&ip_addresses))
+               return false;
+
+       return not ip_addresses.empty();
+}
+
+
+unsigned Resolver::GetServersFromResolvDotConf(std::vector<in_addr_t> * const 
server_ip_addresses)
+{
+       server_ip_addresses->clear();
+
+       File resolv_conf("/etc/resolv.conf", "r");
+       if (resolv_conf.fail())
+               throw Exception("in Resolver::GetServersFromResolvDotConf: 
can't open \"/etc/resolv.conf\" for reading!");
+
+       while (not resolv_conf.eof()) {
+               std::string line;
+               resolv_conf.getline(&line);
+               StringUtil::Trim(" \t", &line);
+               if (line.length() < 12)
+                       continue;
+               if (line.substr(0, 10) == "nameserver") {
+                       std::string resolver_ip_address;
+                       resolver_ip_address = line.substr(10);
+                       StringUtil::LeftTrim(&resolver_ip_address);
+
+                       in_addr_t ip_address;
+                       if (unlikely(not 
NetUtil::StringToNetworkAddress(resolver_ip_address, &ip_address)))
+                               throw Exception("in 
Resolver::GetServersFromResolvDotConf: \"" + resolver_ip_address + "\" is not a 
valid IP address (2)!");
+                       server_ip_addresses->push_back(ip_address);
+               }
+       }
+
+       return static_cast<unsigned>(server_ip_addresses->size());
+}
+
+
+bool ThreadSafeDnsCache::lookup(const std::string &hostname, 
std::set<in_addr_t> * const ip_addresses)
+{
+       // Synchronize access to the internal cache data structures:
+       ThreadUtil::MutexLocker mutex_locker(&cache_access_mutex_);
+
+       GNU_HASH_MAP<std::string, ThreadSafeDnsCacheEntry>::iterator 
entry(resolved_hostnames_cache_.find(hostname));
+       if (entry != resolved_hostnames_cache_.end()) {
+               const time_t now(std::time(NULL));
+               if (entry->second.expire_time_ > now) {
+                       *ip_addresses = entry->second.ip_addresses_;
+                       return true;
+               }
+
+               // Entry has expired => remove it from the cache:
+               resolved_hostnames_cache_.erase(entry);
+       }
+
+       return false;
+}
+
+
+void ThreadSafeDnsCache::insert(const std::string &hostname, const 
std::set<in_addr_t> &ip_addresses, const uint32_t ttl)
+{
+       // Synchronize access to the internal cache data structures:
+       ThreadUtil::MutexLocker mutex_locker(&cache_access_mutex_);
+
+       // Flush the cache if it contains more than 100,000 entries:
+       if (resolved_hostnames_cache_.size() > 100000)
+               resolved_hostnames_cache_.clear();
+       else {
+               // Check to see whether we already have information about this 
"hostname":
+               GNU_HASH_MAP<std::string, ThreadSafeDnsCacheEntry>::iterator 
cache_entry(resolved_hostnames_cache_.find(hostname));
+               if (unlikely(cache_entry != resolved_hostnames_cache_.end())) {
+                       for (std::set<in_addr_t>::const_iterator 
ip_address(ip_addresses.begin()); ip_address != ip_addresses.end(); 
++ip_address)
+                               
cache_entry->second.ip_addresses_.insert(*ip_address);
+                       return;
+               }
+       }
+
+       // Create a new cache entry:
+       const time_t now(std::time(NULL));
+       const ThreadSafeDnsCache::ThreadSafeDnsCacheEntry new_cache_entry(now + 
ttl, ip_addresses);
+       resolved_hostnames_cache_.insert(std::make_pair<std::string, 
ThreadSafeDnsCacheEntry>(hostname, new_cache_entry));
+}
+
+
+SimpleResolver::SimpleResolver(const std::vector<std::string> &dns_servers)
+{
+       // Get the resolver IP addresses from the "dns_server" parameter:
+       if (not dns_servers.empty()) {
+               for (std::vector<std::string>::const_iterator 
dns_server(dns_servers.begin());
+                    dns_server != dns_servers.end(); ++dns_server)
+               {
+                       in_addr_t ip_address;
+                       if (unlikely(not 
NetUtil::StringToNetworkAddress(*dns_server, &ip_address)))
+                               throw Exception("in SimpleResolver::init: \"" + 
*dns_server
+                                               + "\" is not a valid IP address 
(1)!");
+                       dns_server_ip_addresses_and_busy_counts_.push_back(
+                               std::make_pair<in_addr_t, unsigned>(ip_address, 
0));
+               }
+       }
+       else if (FileUtil::Exists(GetEtcDir() + "/Resolver.conf")) { // If a 
Resolver.conf file exists, read it.
+               IniFile ini_file(GetEtcDir() + "/Resolver.conf");
+
+               // Read DNS servers:
+               if (ini_file.sectionIsDefined("DNS Servers")) {
+                       // Add each entry in the [DNS Servers] section of 
Resolver.conf:
+                       const std::list<std::string> 
names(ini_file.getSectionEntryNames("DNS Servers"));
+                       for (std::list<std::string>::const_iterator 
name(names.begin()); name != names.end(); ++name) {
+                               const std::string 
ip_address_str(ini_file.getString("DNS Servers", *name));
+                               in_addr_t ip_address;
+                               if (unlikely(not 
NetUtil::StringToNetworkAddress(ip_address_str, &ip_address)))
+                                       throw Exception("Resolver.conf: \"" + 
ip_address_str + "\""
+                                                       " is not a valid IP 
address (2)!");
+                               
dns_server_ip_addresses_and_busy_counts_.push_back(
+                                       std::make_pair<in_addr_t, 
unsigned>(ip_address, 0));
+                       }
+               }
+
+               if (unlikely(dns_server_ip_addresses_and_busy_counts_.empty()))
+                       throw Exception("in SimpleResolver::init: failed to 
find DNS server IP address in " + GetEtcDir() +
+                                       "/Resolver.conf!");
+       }
+       else { // As a last resort, attempt to get the resolver IP addresses 
from /etc/resolv.conf.
+               std::ifstream resolv_conf("/etc/resolv.conf");
+               if (resolv_conf.fail())
+                       throw Exception("in SimpleResolver::init: can't open 
\"/etc/resolv.conf\" for reading!");
+
+               std::vector<std::string> resolvers;
+               while (resolv_conf) {
+                       std::string line;
+                       std::getline(resolv_conf, line);
+                       StringUtil::Trim(" \t", &line);
+                       if (line.length() < 12)
+                               continue;
+                       if (line.substr(0, 10) == "nameserver") {
+                               std::string resolver_ip_address;
+                               resolver_ip_address = line.substr(10);
+                               StringUtil::LeftTrim(&resolver_ip_address);
+
+                               in_addr_t ip_address;
+                               if (unlikely(not 
NetUtil::StringToNetworkAddress(resolver_ip_address, &ip_address)))
+                                       throw Exception("in 
SimpleResolver::init: \"" + resolver_ip_address
+                                                                + "\" is not a 
valid IP address (2)!");
+                               
dns_server_ip_addresses_and_busy_counts_.push_back(
+                                       std::make_pair<in_addr_t, 
unsigned>(ip_address, 0));
+                       }
+               }
+
+               if (unlikely(dns_server_ip_addresses_and_busy_counts_.empty()))
+                       throw Exception("in SimpleResolver::init: failed to 
find DNS server IP address in /etc/resolv.conf!");
+       }
+
+       // Ensure we have at least one DNS Server:
+       if (dns_server_ip_addresses_and_busy_counts_.empty())
+               throw Exception("in SimpleResolver::init: no DNS Servers 
found");
+}
+
+
+bool SimpleResolver::resolve(const std::string &hostname, const TimeLimit 
&time_limit, std::set<in_addr_t> * const ip_addresses)
+{
+       ip_addresses->clear();
+
+       // Check to see whether "hostname" is already an IP address:
+        in_addr hostname_as_address;
+        if (::inet_aton(hostname.c_str(), &hostname_as_address)) {
+               ip_addresses->insert(hostname_as_address.s_addr);
+               return true;
+       }
+
+       // See if we already know the answer to our query:
+       if (dns_cache_.lookup(hostname, ip_addresses))
+               return true;
+
+       const FileDescriptor udp_fd(::socket(PF_INET, SOCK_DGRAM, 0));
+       if (unlikely(udp_fd == -1))
+               throw Exception("in SimpleResolver::resolve: socket(2) failed 
(" + MsgUtil::ErrnoToString() + ")!");
+
+       // Turn off blocking because we are going to use select(2) which on 
Linux doesn't reliably work with
+       // blocking file descriptors:
+       FileUtil::SetNonblocking(udp_fd);
+
+       // Decide which resolver we should use and get the next request ID in a 
threadsafe manner:
+       const in_addr_t 
resolver_address(getLeastBusyDnsServerAndIncUsageCount());
+
+       //
+       // Send the DNS request...
+       //
+
+       const uint16_t request_id(getNextRequestId());
+       unsigned char packet[512];
+       const ptrdiff_t packet_size(Resolver::GenerateRequestPacket(hostname, 
request_id, packet));
+       if (unlikely(not SocketUtil::SendUdpRequest(udp_fd, resolver_address, 
53 /* DNS service port */, packet, static_cast<unsigned>(packet_size))))
+               throw Exception("in SimpleResolver::resolve: sending a UDP 
request failed (" + MsgUtil::ErrnoToString() + ")!");
+
+       //
+       // ...and now wait for a reply:
+       //
+
+       // Allocate a buffer on the stack to hold UDP DNS server replies and 
make sure that it is 4-byte aligned:
+       const size_t MAX_UDP_REPLY_PACKET_SIZE(512);
+       uint32_t reply_packet_buffer[(MAX_UDP_REPLY_PACKET_SIZE + 
sizeof(uint32_t) - 1) / sizeof(uint32_t)];
+       unsigned char * const reply_packet(reinterpret_cast<unsigned char 
*>(reply_packet_buffer));
+       const size_t max_reply_packet_size(MAX_UDP_REPLY_PACKET_SIZE);
+
+       for (;;) {
+               const ssize_t 
actual_reply_packet_size(SocketUtil::TimedRead(udp_fd, time_limit, 
reply_packet, max_reply_packet_size));
+               if (actual_reply_packet_size <= 0) {
+                       if (likely(errno == ETIMEDOUT))
+                               return false;
+                       throw Exception("in SimpleResolver::resolve: 
SocketUtil::TimedRead() failed (" + MsgUtil::ErrnoToString() + ")!");
+               }
+               else if (processServerReply(reply_packet, 
actual_reply_packet_size, request_id, ip_addresses))
+                       break;
+       }
+
+       decDnsServerUsageCount(resolver_address);
+
+       return not ip_addresses->empty();
+}
+
+
+in_addr_t SimpleResolver::getLeastBusyDnsServerAndIncUsageCount()
+{
+       ThreadUtil::MutexLocker 
mutex_locker(&dns_server_ip_addresses_and_busy_count_access_mutex_);
+
+       std::vector< std::pair<in_addr_t, unsigned> >::iterator 
server_and_usage_count(
+               dns_server_ip_addresses_and_busy_counts_.begin());
+       std::vector< std::pair<in_addr_t, unsigned> >::iterator 
least_busy_server(server_and_usage_count);
+       for (++server_and_usage_count; server_and_usage_count != 
dns_server_ip_addresses_and_busy_counts_.end();
+            ++server_and_usage_count)
+       {
+               if (server_and_usage_count->second < least_busy_server->second)
+                       least_busy_server = server_and_usage_count;
+       }
+
+       // Update usage count...
+       ++least_busy_server->second;
+
+       // ...and return IP address.
+       return least_busy_server->first;
+}
+
+
+void SimpleResolver::decDnsServerUsageCount(const in_addr_t server_ip_address)
+{
+       ThreadUtil::MutexLocker 
mutex_locker(&dns_server_ip_addresses_and_busy_count_access_mutex_);
+
+       // Look for the entry in "dns_server_ip_addresses_and_busy_counts_" and 
decrement the busy count:
+       for (std::vector< std::pair<in_addr_t, unsigned> >::iterator 
server_and_usage_count(
+                    dns_server_ip_addresses_and_busy_counts_.begin());
+            server_and_usage_count != 
dns_server_ip_addresses_and_busy_counts_.end(); ++server_and_usage_count)
+       {
+               if (server_and_usage_count->first == server_ip_address) {
+                       if (unlikely(server_and_usage_count->second == 0))
+                               throw Exception("in 
SimpleResolver::decDnsServerUsageCount: this should never happen!");
+                       --server_and_usage_count->second;
+
+                       return;
+               }
+       }
+
+       // If we get here we did not find a server entry matching 
"server_ip_address" which should never happen!
+       throw Exception("in SimpleResolver::decDnsServerUsageCount: we should 
never get here!");
+}
+
+
+uint16_t SimpleResolver::getNextRequestId()
+{
+       ThreadUtil::MutexLocker mutex_locker(&request_id_mutex_);
+
+       ++next_request_id_;
+       return next_request_id_;
+}
+
+
+bool SimpleResolver::processServerReply(const unsigned char * const 
reply_packet, const size_t reply_packet_size, const uint16_t expected_reply_id,
+                                       std::set<in_addr_t> * const 
ip_addresses)
+{
+       std::set<std::string> hostnames;
+       uint32_t ttl;
+       uint16_t reply_id;
+       bool truncated;
+       if (not Resolver::DecodeReply(reply_packet, reply_packet_size, 
&hostnames, ip_addresses, &ttl,
+                                     &reply_id, &truncated)) // We received a 
garbled reply packet and will ignore it!
+               return false;
+       else if (unlikely(reply_id != expected_reply_id))     // We most likely 
received a reply to an earlier request
+               return false;                                 // and will 
ignore it!
+       else { // We're in luck.
+               if (not ip_addresses->empty()) {
+                       // Update the DNS cache:
+                       for (std::set<std::string>::const_iterator 
hostname(hostnames.begin()); hostname != hostnames.end(); ++hostname)
+                               dns_cache_.insert(*hostname, *ip_addresses, 
ttl);
+               }
+
+               return true;
+       }
+}

Added: csplugins/trunk/ucsd/ruschein/GSFS/src/ThreadSafeLogger.cc
===================================================================
--- csplugins/trunk/ucsd/ruschein/GSFS/src/ThreadSafeLogger.cc                  
        (rev 0)
+++ csplugins/trunk/ucsd/ruschein/GSFS/src/ThreadSafeLogger.cc  2011-09-20 
16:52:23 UTC (rev 26879)
@@ -0,0 +1,53 @@
+/** \file    ThreadSafeLogger.cc
+ *  \brief   Implementation of class ThreadSafeLogger.
+ *  \author  Dr. Johannes Ruscheinski
+ */
+
+/*
+ *  Copyright 2006 Project iVia.
+ *  Copyright 2006 The Regents of The University of California.
+ *
+ *  This file is part of the libiViaCore package.
+ *
+ *  The libiViaCore package is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License as 
published
+ *  by the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libiViaCore is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with libiViaCore; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <ThreadSafeLogger.h>
+#ifndef CERRNO
+#       include <cerrno>
+#       define CERRNO
+#endif
+#ifndef CSTRING
+#       include <cstring>
+#       define CSTRING
+#endif
+#ifndef UNISTD_H
+#       include <unistd.h>
+#       define UNISTD_H
+#endif
+#ifndef MSG_UTIL_H
+#       include <MsgUtil.h>
+#endif
+#ifndef TIME_UTIL_H
+#       include <TimeUtil.h>
+#endif
+
+
+void ThreadSafeLogger::writeLog(const std::string &message, const unsigned 
log_mask)
+{
+       ThreadUtil::MutexLocker mutex_locker(&mutex_);
+
+       Logger::writeLog(message, log_mask);
+}

Modified: csplugins/trunk/ucsd/ruschein/GSFS/src/WebUtil.cc
===================================================================
--- csplugins/trunk/ucsd/ruschein/GSFS/src/WebUtil.cc   2011-09-20 16:29:34 UTC 
(rev 26878)
+++ csplugins/trunk/ucsd/ruschein/GSFS/src/WebUtil.cc   2011-09-20 16:52:23 UTC 
(rev 26879)
@@ -1578,4 +1578,16 @@
 }
 
 
+// ExtractURLs -- extracts all links from "document_source" and returns them 
in "urls".  "root_url" is used to turn relative URLs into absolute URLs if
+//                requested.
+//
+void ExtractURLs(const std::string &/*document_source*/, const std::string 
&/*default_base_url*/,
+                const ExtractedUrlForm /*extracted_url_form*/, 
std::vector<UrlAndAnchorTexts> * const /*urls_and_anchor_texts*/,
+                const unsigned /*flags*/, const std::string 
&/*user_agent_string*/, const unsigned /*page_cacher_max_fanout*/,
+                const unsigned /*individual_page_timeout*/, unsigned long * 
const /*overall_timeout*/)
+{
+       throw std::runtime_error("WebUtil::ExtractURLs() has not been 
implemented!");
+}
+
+
 } // namespace WebUtil

-- 
You received this message because you are subscribed to the Google Groups 
"cytoscape-cvs" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/cytoscape-cvs?hl=en.

Reply via email to