![]() |
Shadowrun: Awakened 29 September 2011 - Build 871
|
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.