Shadowrun: Awakened 29 September 2011 - Build 871
DNSClient.hpp
Go to the documentation of this file.
00001 /*
00002     Copyright (c) 2009-2010 Christopher A. Taylor.  All rights reserved.
00003 
00004     Redistribution and use in source and binary forms, with or without
00005     modification, are permitted provided that the following conditions are met:
00006 
00007     * Redistributions of source code must retain the above copyright notice,
00008       this list of conditions and the following disclaimer.
00009     * Redistributions in binary form must reproduce the above copyright notice,
00010       this list of conditions and the following disclaimer in the documentation
00011       and/or other materials provided with the distribution.
00012     * Neither the name of LibCat nor the names of its contributors may be used
00013       to endorse or promote products derived from this software without
00014       specific prior written permission.
00015 
00016     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00017     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00018     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00019     ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00020     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00021     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00022     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00023     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00024     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00025     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00026     POSSIBILITY OF SUCH DAMAGE.
00027 */
00028 
00029 // TODO: React to timeouts from DNS server by switching to a backup
00030 // TODO: Use TTL from DNS record instead of fixed constant
00031 // TODO: If the DNS resolution load is high, it would make sense to put
00032 //       multiple requests in the same DNS packet
00033 // TODO: Retransmissions could also be grouped into the same DNS packets
00034 // TODO: The locks held in DNSClient are fairly coarse and could be broken up
00035 
00036 #ifndef CAT_DNS_CLIENT_HPP
00037 #define CAT_DNS_CLIENT_HPP
00038 
00039 #include <cat/net/ThreadPoolSockets.hpp>
00040 #include <cat/threads/Thread.hpp>
00041 #include <cat/threads/WaitableFlag.hpp>
00042 #include <cat/crypt/rand/Fortuna.hpp>
00043 #include <cat/port/FastDelegate.h>
00044 
00045 namespace cat {
00046 
00047 
00048 static const int HOSTNAME_MAXLEN = 63; // Max characters in a hostname request
00049 static const int DNSREQ_TIMEOUT = 3000; // DNS request timeout interval
00050 static const int DNSREQ_REPOST_TIME = 300; // Number of milliseconds between retries
00051 static const int DNSREQ_MAX_SIMUL = 2048; // Maximum number of simultaneous DNS requests
00052 static const int DNSCACHE_MAX_REQS = 8; // Maximum number of requests to cache
00053 static const int DNSCACHE_MAX_RESP = 8; // Maximum number of responses to cache
00054 static const int DNSCACHE_TIMEOUT = 60000; // Time until a cached response is dropped
00055 
00056 // Prototype: bool MyResultCallback(const char *, const NetAddr*, int);
00057 typedef fastdelegate::FastDelegate3<const char *, const NetAddr*, int, bool> DNSResultCallback;
00058 
00059 
00061 
00062 struct DNSCallback
00063 {
00064     DNSCallback *next;
00065 
00066     DNSResultCallback cb;
00067     ThreadRefObject *ref;
00068 };
00069 
00070 struct DNSRequest
00071 {
00072     DNSRequest *last, *next;
00073 
00074     u32 first_post_time; // Timestamp for first post, for timeout
00075     u32 last_post_time; // Timestamp for last post, for retries and cache
00076 
00077     // Our copy of the hostname string
00078     char hostname[HOSTNAME_MAXLEN+1];
00079     u16 id; // Random request ID
00080     DNSCallback callback_head;
00081 
00082     // For caching purposes
00083     NetAddr responses[DNSCACHE_MAX_RESP];
00084     int num_responses;
00085 };
00086 
00087 
00089 
00090 class DNSClient : Thread, public UDPEndpoint, public Singleton<DNSClient>
00091 {
00092     static const int TICK_RATE = 200; // milliseconds
00093     static const int DNS_THREAD_KILL_TIMEOUT = 10000; // 10 seconds
00094 
00095     CAT_SINGLETON(DNSClient)
00096         : UDPEndpoint(REFOBJ_PRIO_0+5)
00097     {
00098         _server_addr.Invalidate();
00099         _dns_unavailable = true;
00100 
00101         _cache_head = _cache_tail = 0;
00102         _cache_size = 0;
00103 
00104         _request_head = _request_tail = 0;
00105         _request_queue_size = 0;
00106     }
00107 
00108 public:
00109     virtual ~DNSClient();
00110 
00111     /*
00112         In your startup code, call Initialize() and check the return value.
00113         In your shutdown code, call Shutdown().  This will delete the DNSClient object.
00114     */
00115     bool Initialize();
00116     void Shutdown();
00117 
00118     /*
00119         If hostname is numeric or in the cache, the callback function will be invoked
00120         immediately from the requesting thread, rather than from another thread.
00121 
00122         First attempts numerical resolve of hostname, then queries the DNS server.
00123 
00124         Hostname string length limited to HOSTNAME_MAXLEN characters.
00125         Caches the most recent DNSCACHE_MAX_REQS requests.
00126         Returns up to DNSCACHE_MAX_RESP addresses per resolve request.
00127         Performs DNS lookup on a cached request after DNSCACHE_TIMEOUT.
00128         Gives up on DNS lookup after DNSREQ_TIMEOUT.
00129 
00130         If holdRef is valid, the reference will be held until the callback completes.
00131 
00132         If no results were found, array_length == 0.
00133         If the callback returns false, the result will not be entered into the cache.
00134 
00135         The resolved addresses may need to be promoted to IPv6.
00136 
00137         If Resolve() returns false, no callback will be generated.
00138     */
00139     bool Resolve(const char *hostname, DNSResultCallback, ThreadRefObject *holdRef = 0);
00140 
00141 private:
00142     NetAddr _server_addr;
00143     volatile bool _dns_unavailable;
00144     FortunaOutput *_csprng;
00145 
00146 private:
00147     Mutex _request_lock;
00148     DNSRequest *_request_head;
00149     DNSRequest *_request_tail;
00150     int _request_queue_size;
00151 
00152     bool GetUnusedID(u16 &id); // not thread-safe, caller must lock
00153     bool IsValidHostname(const char *hostname);
00154     DNSRequest *PullRequest(u16 id); // not thread-safe, caller must lock
00155 
00156 private:
00157     Mutex _cache_lock;
00158     DNSRequest *_cache_head;
00159     DNSRequest *_cache_tail;
00160     int _cache_size;
00161 
00162     // These functions do not lock, caller must lock:
00163     void CacheAdd(DNSRequest *req); // Assumes not already in cache
00164     DNSRequest *CacheGet(const char *hostname); // Case-insensitive
00165     void CacheKill(DNSRequest *req); // Assumes already in cache
00166 
00167 private:
00168     bool GetServerAddr();
00169     bool BindToRandomPort(bool ignoreUnreachable);
00170     bool PostDNSPacket(DNSRequest *req, u32 now);
00171     bool PerformLookup(DNSRequest *req); // not thread-safe, caller must lock
00172 
00173 protected:
00174     virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &src, u8 *data, u32 bytes);
00175     virtual void OnClose();
00176     virtual void OnUnreachable(const NetAddr &src);
00177 
00178 protected:
00179     void ProcessDNSResponse(DNSRequest *req, int qdcount, int ancount, u8 *data, u32 bytes);
00180     void NotifyRequesters(DNSRequest *req);
00181 
00182 private:
00183     WaitableFlag _kill_flag;
00184 
00185     bool ThreadFunction(void *param);
00186 };
00187 
00188 
00189 } // namespace cat
00190 
00191 #endif // CAT_DNS_CLIENT_HPP

Copyright © 2007-2010 by The Shadowrun: Awakened Team. This work is licensed under the GNU Lesser General Public License 3.

GNU Lesser General Public License 3 Sourceforge.net