]>
Commit | Line | Data |
---|---|---|
12c86877 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
3 | Copyright (C) 2002 PowerDNS.COM BV | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; if not, write to the Free Software | |
17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | */ | |
19 | #ifndef PACKETCACHE_HH | |
20 | #define PACKETCACHE_HH | |
21 | ||
22 | #include <string> | |
23 | #include <utility> | |
24 | #include <map> | |
25 | ||
26 | #ifndef WIN32 | |
27 | # if __GNUC__ >= 3 | |
28 | # include <ext/hash_map> | |
29 | using namespace __gnu_cxx; | |
30 | # else | |
31 | # include <hash_map> | |
32 | # endif // __GNUC__ | |
33 | ||
34 | #else | |
35 | # include <map> | |
36 | ||
37 | #endif // WIN32 | |
38 | ||
39 | using namespace std; | |
40 | ||
41 | #include "dnspacket.hh" | |
42 | #include "lock.hh" | |
43 | #include "statbag.hh" | |
44 | ||
45 | /** This class performs 'whole packet caching'. Feed it a question packet and it will | |
46 | try to find an answer. If you have an answer, insert it to have it cached for later use. | |
47 | Take care not to replace existing cache entries. While this works, it is wasteful. Only | |
48 | insert packets that where not found by get() | |
49 | ||
50 | Locking! | |
51 | ||
52 | The cache itself is protected by a read/write lock. Because deleting is a two step process, which | |
53 | first marks and then sweeps, a second lock is present to prevent simultaneous inserts and deletes. | |
54 | ||
55 | Overloading! | |
56 | ||
57 | The packet cache contains packets but also negative UeberBackend queries. Those last are recognized | |
58 | because they start with a | and have empty content. One day, this content may also contain queries. | |
59 | ||
60 | */ | |
61 | class PacketCache | |
62 | { | |
63 | public: | |
64 | PacketCache(); | |
65 | void insert(DNSPacket *q, DNSPacket *r); //!< We copy the contents of *p into our cache. Do not needlessly call this to insert questions already in the cache as it wastes resources | |
66 | void PacketCache::insert(const char *packet, int length); | |
67 | ||
68 | inline int get(DNSPacket *p, DNSPacket *q); //!< We return a dynamically allocated copy out of our cache. You need to delete it. You also need to spoof in the right ID with the DNSPacket.spoofID() method. | |
69 | bool getKey(const string &key, string &content); | |
70 | int size(); //!< number of entries in the cache | |
71 | void cleanup(); //!< force the cache to preen itself from expired packets | |
72 | int purge(const string &prefix=""); | |
73 | void insert(const string &key, const string &packet, unsigned int ttl); | |
74 | map<char,int> getCounts(); | |
75 | private: | |
76 | typedef string ckey_t; | |
77 | ||
78 | class CacheContent | |
79 | { | |
80 | public: | |
81 | time_t ttd; | |
82 | string value; | |
83 | }; | |
84 | ||
85 | typedef CacheContent cvalue_t; | |
86 | void getTTLS(); | |
87 | #ifndef WIN32 | |
88 | ||
89 | struct compare_string | |
90 | { | |
91 | bool operator()(const string& s1, const string& s2) const | |
92 | { | |
93 | return s1 == s2; | |
94 | } | |
95 | }; | |
96 | ||
97 | struct hash_string | |
98 | { | |
99 | size_t operator()(const string& s) const | |
100 | { | |
101 | return __stl_hash_string(s.c_str()); | |
102 | } | |
103 | }; | |
104 | ||
105 | typedef hash_map<ckey_t,cvalue_t, hash_string, compare_string > cmap_t; | |
106 | ||
107 | #else | |
108 | typedef map< ckey_t, cvalue_t > cmap_t; | |
109 | ||
110 | #endif // WIN32 | |
111 | ||
112 | cmap_t d_map; | |
113 | ||
114 | pthread_rwlock_t d_mut; | |
115 | pthread_mutex_t d_dellock; | |
116 | ||
117 | int d_hit; | |
118 | int d_miss; | |
119 | int d_ttl; | |
120 | int d_recursivettl; | |
121 | bool d_doRecursion; | |
122 | int *statnumhit; | |
123 | int *statnummiss; | |
124 | int *statnumentries; | |
125 | }; | |
126 | ||
127 | inline int PacketCache::get(DNSPacket *p, DNSPacket *cached) | |
128 | { | |
129 | extern StatBag S; | |
130 | if(!((d_hit+d_miss)%5000)) { | |
131 | cleanup(); | |
132 | } | |
133 | ||
134 | if(d_ttl<0) | |
135 | getTTLS(); | |
136 | ||
137 | if(d_doRecursion && p->d.rd) { // wants recursion | |
138 | if(!d_recursivettl) { | |
139 | (*statnummiss)++; | |
140 | d_miss++; | |
141 | return 0; | |
142 | } | |
143 | } | |
144 | else { // does not | |
145 | if(!d_ttl) { | |
146 | (*statnummiss)++; | |
147 | d_miss++; | |
148 | return 0; | |
149 | } | |
150 | } | |
151 | ||
152 | bool packetMeritsRecursion=d_doRecursion && p->d.rd; | |
153 | char ckey[512]; | |
154 | int len=p->qdomain.length(); | |
155 | memcpy(ckey,p->qdomain.c_str(),len); // add TOLOWER HERE FIXME XXX | |
156 | ckey[len]='|'; | |
157 | ckey[len+1]=packetMeritsRecursion ? 'r' : 'n'; | |
158 | ckey[len+2]=(p->qtype.getCode()>>8)&0xff; | |
159 | ckey[len+3]=(p->qtype.getCode())&0xff; | |
160 | string key; | |
161 | ||
162 | key.assign(ckey,p->qdomain.length()+4); | |
163 | // cout<<"key lookup: '"<<key<<"'"<<endl; | |
164 | // string key=toLower(p->qdomain+"|"+(packetMeritsRecursion ? "R" : "N")+ "|"+p->qtype.getName()); | |
165 | ||
166 | if(ntohs(p->d.qdcount)!=1) // we get confused by packets with more than one question | |
167 | return 0; | |
168 | ||
169 | { | |
170 | TryReadLock l(&d_mut); // take a readlock here | |
171 | if(!l.gotIt()) { | |
172 | S.inc("deferred-cache-lookup"); | |
173 | return 0; | |
174 | } | |
175 | ||
176 | if(!((d_hit+d_miss)%1000)) { | |
177 | *statnumentries=d_map.size(); // needs lock | |
178 | } | |
179 | cmap_t::const_iterator i; | |
180 | if((i=d_map.find(key))!=d_map.end()) { // HIT! | |
181 | ||
182 | if(i->second.ttd>time(0)) { // it is still fresh | |
183 | (*statnumhit)++; | |
184 | d_hit++; | |
185 | cached->parse(i->second.value.c_str(),i->second.value.size()); | |
186 | cached->spoofQuestion(p->qdomain); // for correct case | |
187 | return 1; | |
188 | } | |
189 | } | |
190 | } | |
191 | (*statnummiss)++; | |
192 | d_miss++; | |
193 | return 0; // bummer | |
194 | } | |
195 | ||
196 | ||
197 | #endif /* PACKETCACHE_HH */ | |
198 |