]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/packetcache.cc
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 - 2008 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 #include "packetcache.hh"
21 #include "arguments.hh"
24 #include <boost/algorithm/string.hpp>
28 PacketCache :: PacketCache ()
30 pthread_rwlock_init (& d_mut
, 0 );
36 S
. declare ( "packetcache-hit" );
37 S
. declare ( "packetcache-miss" );
38 S
. declare ( "packetcache-size" );
40 d_statnumhit
= S
. getPointer ( "packetcache-hit" );
41 d_statnummiss
= S
. getPointer ( "packetcache-miss" );
42 d_statnumentries
= S
. getPointer ( "packetcache-size" );
45 PacketCache ::~ PacketCache ()
50 int PacketCache :: get ( DNSPacket
* p
, DNSPacket
* cached
)
57 if (!(( d_ops
++) % 300000 )) {
62 if ( d_doRecursion
&& p
-> d
. rd
) { // wants recursion
75 bool packetMeritsRecursion
= d_doRecursion
&& p
-> d
. rd
;
76 if ( ntohs ( p
-> d
. qdcount
)!= 1 ) // we get confused by packets with more than one question
82 TryReadLock
l (& d_mut
); // take a readlock here
84 S
. inc ( "deferred-cache-lookup" );
88 haveSomething
= getEntryLocked ( p
-> qdomain
, p
-> qtype
, PacketCache :: PACKETCACHE
, value
, - 1 , packetMeritsRecursion
);
92 if ( cached
-> noparse ( value
. c_str (), value
. size ()) < 0 ) {
95 cached
-> spoofQuestion ( p
-> qdomain
); // for correct case
99 // cerr<<"Packet cache miss for '"<<p->qdomain<<"', merits: "<<packetMeritsRecursion<<endl;
104 void PacketCache :: getTTLS ()
106 d_ttl
=:: arg (). asNum ( "cache-ttl" );
107 d_recursivettl
=:: arg (). asNum ( "recursive-cache-ttl" );
109 d_doRecursion
=:: arg (). mustDo ( "recursor" );
113 void PacketCache :: insert ( DNSPacket
* q
, DNSPacket
* r
)
118 if ( ntohs ( q
-> d
. qdcount
)!= 1 ) {
119 return ; // do not try to cache packets with multiple questions
122 bool packetMeritsRecursion
= d_doRecursion
&& q
-> d
. rd
;
124 insert ( q
-> qdomain
, q
-> qtype
, PacketCache :: PACKETCACHE
, r
-> getString (), packetMeritsRecursion
? d_recursivettl
: d_ttl
, - 1 , packetMeritsRecursion
);
127 // universal key appears to be: qname, qtype, kind (packet, query cache), optionally zoneid, meritsRecursion
128 void PacketCache :: insert ( const string
& qname
, const QType
& qtype
, CacheEntryType cet
, const string
& value
, unsigned int ttl
, int zoneID
, bool meritsRecursion
)
130 if (!(( d_ops
++) % 300000 )) {
137 // cerr<<"Inserting qname '"<<qname<<"', cet: "<<(int)cet<<", value: '"<< (cet ? value : "PACKET") <<"', qtype: "<<qtype.getName()<<", ttl: "<<ttl<<endl;
141 val
. qtype
= qtype
. getCode ();
144 val
. meritsRecursion
= meritsRecursion
;
146 TryWriteLock
l (& d_mut
);
149 cmap_t :: iterator place
;
150 tie ( place
, success
)= d_map
. insert ( val
);
151 // cerr<<"Insert succeeded: "<<success<<endl;
153 d_map
. replace ( place
, val
);
157 S
. inc ( "deferred-cache-inserts" );
160 /** purges entries from the packetcache. If match ends on a $, it is treated as a suffix */
161 int PacketCache :: purge ( const vector
< string
> & matches
)
166 if ( matches
. empty ()) {
167 delcount
= d_map
. size ();
173 /* ok, the suffix delete plan. We want to be able to delete everything that
174 pertains 'www.powerdns.com' but we also want to be able to delete everything
175 in the powerdns.com zone, so: 'powerdns.com' and '*.powerdns.com'.
177 However, we do NOT want to delete 'usepowerdns.com!, nor 'powerdnsiscool.com'
179 So, at first shot, store in reverse label order:
183 'com.powerdns.images'
186 'com.usepowerdns.www'
188 If we get a request to remove 'everything above powerdns.com', we do a search for 'com.powerdns' which is guaranteed to come first (it is shortest!)
189 Then we delete everything that is either equal to 'com.powerdns' or begins with 'com.powerdns.' This trailing dot saves us
190 from deleting 'com.powerdnsiscool'.
192 We can stop the process once we reach something that doesn't match.
194 Ok - fine so far, except it doesn't work! Let's say there *is* no 'com.powerdns' in cache!
196 In that case our request doesn't find anything.. now what.
197 lower_bound to the rescue! It finds the place where 'com.powerdns' *would* be.
199 Ok - next step, can we get away with simply reversing the string?
202 'moc.sndrewop.segami'
205 'moc.dnsrewopesu.www'
207 Ok - next step, can we get away with only reversing the comparison?
210 'images.powerdns.com'
213 'www.userpowerdns.com'
216 for ( vector
< string
>:: const_iterator match
= ++ matches
. begin (); match
!= matches
. end () ; ++ match
) {
217 if ( ends_with (* match
, "$" )) {
218 string
suffix (* match
);
219 suffix
. resize ( suffix
. size ()- 1 );
221 // cerr<<"Begin dump!"<<endl;
222 cmap_t :: const_iterator iter
= d_map
. lower_bound ( tie ( suffix
));
223 cmap_t :: const_iterator start
= iter
;
224 string dotsuffix
= "." + suffix
;
226 for (; iter
!= d_map
. end (); ++ iter
) {
227 if (! pdns_iequals ( iter
-> qname
, suffix
) && ! iends_with ( iter
-> qname
, dotsuffix
)) {
228 // cerr<<"Stopping!"<<endl;
231 // cerr<<"Will erase '"<<iter->qname<<"'\n";
235 // cerr<<"End dump!"<<endl;
236 d_map
. erase ( start
, iter
);
239 delcount
= d_map
. count ( tie (* match
));
240 pair
< cmap_t :: iterator
, cmap_t :: iterator
> range
= d_map
. equal_range ( tie (* match
));
241 d_map
. erase ( range
. first
, range
. second
);
244 * d_statnumentries
= d_map
. size ();
248 bool PacketCache :: getEntry ( const string
& qname
, const QType
& qtype
, CacheEntryType cet
, string
& value
, int zoneID
, bool meritsRecursion
)
253 if (!(( d_ops
++) % 300000 )) {
257 TryReadLock
l (& d_mut
); // take a readlock here
259 S
. inc ( "deferred-cache-lookup" );
262 return getEntryLocked ( qname
, qtype
, cet
, value
, zoneID
, meritsRecursion
);
265 bool PacketCache :: getEntryLocked ( const string
& qname
, const QType
& qtype
, CacheEntryType cet
, string
& value
, int zoneID
, bool meritsRecursion
)
268 uint16_t qt
= qtype
. getCode ();
269 cmap_t :: const_iterator i
= d_map
. find ( tie ( qname
, qt
, cet
, zoneID
, meritsRecursion
));
271 bool ret
=( i
!= d_map
. end () && i
-> ttd
> now
);
278 map
< char , int > PacketCache :: getCounts ()
283 int recursivePackets
= 0 , nonRecursivePackets
= 0 , queryCacheEntries
= 0 , negQueryCacheEntries
= 0 ;
285 for ( cmap_t :: const_iterator iter
= d_map
. begin () ; iter
!= d_map
. end (); ++ iter
) {
286 if ( iter
-> ctype
== PACKETCACHE
)
287 if ( iter
-> meritsRecursion
)
290 nonRecursivePackets
++;
291 else if ( iter
-> ctype
== QUERYCACHE
) {
292 if ( iter
-> value
. empty ())
293 negQueryCacheEntries
++;
298 ret
[ '!' ]= negQueryCacheEntries
;
299 ret
[ 'Q' ]= queryCacheEntries
;
300 ret
[ 'n' ]= nonRecursivePackets
;
301 ret
[ 'r' ]= recursivePackets
;
305 int PacketCache :: size ()
311 /** readlock for figuring out which iterators to delete, upgrade to writelock when actually cleaning */
312 void PacketCache :: cleanup ()
316 * d_statnumentries
= d_map
. size ();
318 unsigned int maxCached
=:: arg (). asNum ( "max-cache-entries" );
319 unsigned int toTrim
= 0 ;
321 unsigned int cacheSize
=* d_statnumentries
;
323 if ( maxCached
&& cacheSize
> maxCached
) {
324 toTrim
= cacheSize
- maxCached
;
327 unsigned int lookAt
= 0 ;
328 // two modes - if toTrim is 0, just look through 10% of the cache and nuke everything that is expired
329 // otherwise, scan first 5*toTrim records, and stop once we've nuked enough
335 // cerr<<"cacheSize: "<<cacheSize<<", lookAt: "<<lookAt<<", toTrim: "<<toTrim<<endl;
338 DLOG ( L
<< "Starting cache clean" << endl
);
342 typedef cmap_t :: nth_index
< 1 >:: type sequence_t
;
343 sequence_t
& sidx
= d_map
. get
< 1 >();
344 unsigned int erased
= 0 , lookedAt
= 0 ;
345 for ( sequence_t :: iterator i
= sidx
. begin (); i
!= sidx
. end (); lookedAt
++) {
353 if ( toTrim
&& erased
> toTrim
)
356 if ( lookedAt
> lookAt
)
359 // cerr<<"erased: "<<erased<<endl;
360 * d_statnumentries
= d_map
. size ();
361 DLOG ( L
<< "Done with cache clean" << endl
);