]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/nod.cc
Wishy-washy
[thirdparty/pdns.git] / pdns / nod.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include "nod.hh"
24 #include <fstream>
25 #include "pdnsexception.hh"
26 #include <iostream>
27 #include <iomanip>
28 #include <ctime>
29 #include <thread>
30 #include "threadname.hh"
31 #include <unistd.h>
32 #include <boost/filesystem.hpp>
33 #include "logger.hh"
34 #include "misc.hh"
35
36 using namespace nod;
37 using namespace boost::filesystem;
38
39 // PersistentSBF Implementation
40
41 std::mutex PersistentSBF::d_cachedir_mutex;
42
43 // This looks for an old (per-thread) snapshot. The first one it finds,
44 // it restores from that. Then immediately snapshots with the current thread id,// before removing the old snapshot
45 // In this way, we can have per-thread SBFs, but still snapshot and restore.
46 // The mutex has to be static because we can't have multiple (i.e. per-thread)
47 // instances iterating and writing to the cache dir at the same time
48 bool PersistentSBF::init(bool ignore_pid) {
49 if (d_init)
50 return false;
51
52 std::lock_guard<std::mutex> lock(d_cachedir_mutex);
53 if (d_cachedir.length()) {
54 path p(d_cachedir);
55 try {
56 if (exists(p) && is_directory(p)) {
57 path newest_file;
58 std::time_t newest_time=time(nullptr);
59 Regex file_regex(d_prefix + ".*\\." + bf_suffix + "$");
60 for (directory_iterator i(p); i!=directory_iterator(); ++i) {
61 if (is_regular_file(i->path()) &&
62 file_regex.match(i->path().filename().string())) {
63 if (ignore_pid ||
64 (i->path().filename().string().find(std::to_string(getpid())) == std::string::npos)) {
65 // look for the newest file matching the regex
66 if ((last_write_time(i->path()) < newest_time) ||
67 newest_file.empty()) {
68 newest_time = last_write_time(i->path());
69 newest_file = i->path();
70 }
71 }
72 }
73 }
74 if (exists(newest_file)) {
75 std::string filename = newest_file.string();
76 std::ifstream infile;
77 try {
78 infile.open(filename, std::ios::in | std::ios::binary);
79 g_log << Logger::Warning << "Found SBF file " << filename << endl;
80 // read the file into the sbf
81 d_sbf.restore(infile);
82 infile.close();
83 // now dump it out again with new thread id & process id
84 snapshotCurrent(std::this_thread::get_id());
85 // Remove the old file we just read to stop proliferation
86 remove(newest_file);
87 }
88 catch (const std::runtime_error& e) {
89 g_log<<Logger::Warning<<"NODDB init: Cannot parse file: " << filename << endl;
90 }
91 }
92 }
93 }
94 catch (const filesystem_error& e) {
95 g_log<<Logger::Warning<<"NODDB init failed:: " << e.what() << endl;
96 return false;
97 }
98 }
99 d_init = true;
100 return true;
101 }
102
103 void PersistentSBF::setCacheDir(const std::string& cachedir)
104 {
105 if (!d_init) {
106 path p(cachedir);
107 if (!exists(p))
108 throw PDNSException("NODDB setCacheDir specified non-existent directory: " + cachedir);
109 else if (!is_directory(p))
110 throw PDNSException("NODDB setCacheDir specified a file not a directory: " + cachedir);
111 d_cachedir = cachedir;
112 }
113 }
114
115 // Dump the SBF to a file
116 // To spend the least amount of time inside the mutex, we dump to an
117 // intermediate stringstream, otherwise the lock would be waiting for
118 // file IO to complete
119 bool PersistentSBF::snapshotCurrent(std::thread::id tid)
120 {
121 if (d_cachedir.length()) {
122 path p(d_cachedir);
123 path f(d_cachedir);
124 std::stringstream ss;
125 ss << d_prefix << "_" << tid;
126 f /= ss.str() + "_" + std::to_string(getpid()) + "." + bf_suffix;
127 if (exists(p) && is_directory(p)) {
128 try {
129 std::ofstream ofile;
130 std::stringstream iss;
131 ofile.open(f.string(), std::ios::out | std::ios::binary);
132 {
133 // only lock while dumping to a stringstream
134 std::lock_guard<std::mutex> lock(d_sbf_mutex);
135 d_sbf.dump(iss);
136 }
137 // Now write it out to the file
138 ofile << iss.str();
139
140 if (ofile.fail())
141 throw std::runtime_error("Failed to write to file:" + f.string());
142 return true;
143 }
144 catch (const std::runtime_error& e) {
145 g_log<<Logger::Warning<<"NODDB snapshot: Cannot write file: " << e.what() << endl;
146 }
147 }
148 else {
149 g_log<<Logger::Warning<<"NODDB snapshot: Cannot write file: " << f.string() << endl;
150 }
151 }
152 return false;
153 }
154
155 // NODDB Implementation
156
157 void NODDB::housekeepingThread(std::thread::id tid)
158 {
159 setThreadName("pdns-r/NOD-hk");
160 for (;;) {
161 sleep(d_snapshot_interval);
162 {
163 snapshotCurrent(tid);
164 }
165 }
166 }
167
168 bool NODDB::isNewDomain(const std::string& domain)
169 {
170 DNSName dname(domain);
171 return isNewDomain(dname);
172 }
173
174 bool NODDB::isNewDomain(const DNSName& dname)
175 {
176 std::string dname_lc = dname.toDNSStringLC();
177 // The only time this should block is when snapshotting from the
178 // housekeeping thread
179 // the result is always the inverse of what is returned by the SBF
180 return !d_psbf.testAndAdd(dname_lc);
181 }
182
183 bool NODDB::isNewDomainWithParent(const std::string& domain, std::string& observed)
184 {
185 DNSName dname(domain);
186 return isNewDomainWithParent(dname, observed);
187 }
188
189 bool NODDB::isNewDomainWithParent(const DNSName& dname, std::string& observed)
190 {
191 bool ret = isNewDomain(dname);
192 if (ret == true) {
193 DNSName mdname = dname;
194 while (mdname.chopOff()) {
195 if (!isNewDomain(mdname)) {
196 observed = mdname.toString();
197 break;
198 }
199 }
200 }
201 return ret;
202 }
203
204 void NODDB::addDomain(const DNSName& dname)
205 {
206 std::string native_domain = dname.toDNSStringLC();
207 d_psbf.add(native_domain);
208 }
209
210 void NODDB::addDomain(const std::string& domain)
211 {
212 DNSName dname(domain);
213 addDomain(dname);
214 }
215
216 // UniqueResponseDB Implementation
217 bool UniqueResponseDB::isUniqueResponse(const std::string& response)
218 {
219 return !d_psbf.testAndAdd(response);
220 }
221
222 void UniqueResponseDB::addResponse(const std::string& response)
223 {
224 d_psbf.add(response);
225 }
226
227 void UniqueResponseDB::housekeepingThread(std::thread::id tid)
228 {
229 setThreadName("pdns-r/UDR-hk");
230 for (;;) {
231 sleep(d_snapshot_interval);
232 {
233 snapshotCurrent(tid);
234 }
235 }
236 }