You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

237 lines
7.0 KiB

  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. #include "nod.hh"
  23. #include <fstream>
  24. #include "pdnsexception.hh"
  25. #include <iostream>
  26. #include <iomanip>
  27. #include <ctime>
  28. #include <thread>
  29. #include "threadname.hh"
  30. #include <unistd.h>
  31. #include <boost/filesystem.hpp>
  32. #include "logger.hh"
  33. #include "misc.hh"
  34. using namespace nod;
  35. using namespace boost::filesystem;
  36. // PersistentSBF Implementation
  37. std::mutex PersistentSBF::d_cachedir_mutex;
  38. // This looks for an old (per-thread) snapshot. The first one it finds,
  39. // it restores from that. Then immediately snapshots with the current thread id,// before removing the old snapshot
  40. // In this way, we can have per-thread SBFs, but still snapshot and restore.
  41. // The mutex has to be static because we can't have multiple (i.e. per-thread)
  42. // instances iterating and writing to the cache dir at the same time
  43. bool PersistentSBF::init(bool ignore_pid) {
  44. if (d_init)
  45. return false;
  46. std::lock_guard<std::mutex> lock(d_cachedir_mutex);
  47. if (d_cachedir.length()) {
  48. path p(d_cachedir);
  49. try {
  50. if (exists(p) && is_directory(p)) {
  51. path newest_file;
  52. std::time_t newest_time=time(nullptr);
  53. Regex file_regex(d_prefix + ".*\\." + bf_suffix + "$");
  54. for (directory_iterator i(p); i!=directory_iterator(); ++i) {
  55. if (is_regular_file(i->path()) &&
  56. file_regex.match(i->path().filename().string())) {
  57. if (ignore_pid ||
  58. (i->path().filename().string().find(std::to_string(getpid())) == std::string::npos)) {
  59. // look for the newest file matching the regex
  60. if ((last_write_time(i->path()) < newest_time) ||
  61. newest_file.empty()) {
  62. newest_time = last_write_time(i->path());
  63. newest_file = i->path();
  64. }
  65. }
  66. }
  67. }
  68. if (exists(newest_file)) {
  69. std::string filename = newest_file.string();
  70. std::ifstream infile;
  71. try {
  72. infile.open(filename, std::ios::in | std::ios::binary);
  73. g_log << Logger::Warning << "Found SBF file " << filename << endl;
  74. // read the file into the sbf
  75. d_sbf.restore(infile);
  76. infile.close();
  77. // now dump it out again with new thread id & process id
  78. snapshotCurrent(std::this_thread::get_id());
  79. // Remove the old file we just read to stop proliferation
  80. remove(newest_file);
  81. }
  82. catch (const std::runtime_error& e) {
  83. g_log<<Logger::Warning<<"NODDB init: Cannot parse file: " << filename << endl;
  84. }
  85. }
  86. }
  87. }
  88. catch (const filesystem_error& e) {
  89. g_log<<Logger::Warning<<"NODDB init failed:: " << e.what() << endl;
  90. return false;
  91. }
  92. }
  93. d_init = true;
  94. return true;
  95. }
  96. void PersistentSBF::setCacheDir(const std::string& cachedir)
  97. {
  98. if (!d_init) {
  99. path p(cachedir);
  100. if (!exists(p))
  101. throw PDNSException("NODDB setCacheDir specified non-existent directory: " + cachedir);
  102. else if (!is_directory(p))
  103. throw PDNSException("NODDB setCacheDir specified a file not a directory: " + cachedir);
  104. d_cachedir = cachedir;
  105. }
  106. }
  107. // Dump the SBF to a file
  108. // To spend the least amount of time inside the mutex, we dump to an
  109. // intermediate stringstream, otherwise the lock would be waiting for
  110. // file IO to complete
  111. bool PersistentSBF::snapshotCurrent(std::thread::id tid)
  112. {
  113. if (d_cachedir.length()) {
  114. path p(d_cachedir);
  115. path f(d_cachedir);
  116. std::stringstream ss;
  117. ss << d_prefix << "_" << tid;
  118. f /= ss.str() + "_" + std::to_string(getpid()) + "." + bf_suffix;
  119. if (exists(p) && is_directory(p)) {
  120. try {
  121. std::ofstream ofile;
  122. std::stringstream iss;
  123. ofile.open(f.string(), std::ios::out | std::ios::binary);
  124. {
  125. // only lock while dumping to a stringstream
  126. std::lock_guard<std::mutex> lock(d_sbf_mutex);
  127. d_sbf.dump(iss);
  128. }
  129. // Now write it out to the file
  130. ofile << iss.str();
  131. if (ofile.fail())
  132. throw std::runtime_error("Failed to write to file:" + f.string());
  133. return true;
  134. }
  135. catch (const std::runtime_error& e) {
  136. g_log<<Logger::Warning<<"NODDB snapshot: Cannot write file: " << e.what() << endl;
  137. }
  138. }
  139. else {
  140. g_log<<Logger::Warning<<"NODDB snapshot: Cannot write file: " << f.string() << endl;
  141. }
  142. }
  143. return false;
  144. }
  145. // NODDB Implementation
  146. void NODDB::housekeepingThread(std::thread::id tid)
  147. {
  148. setThreadName("pdns-r/NOD-hk");
  149. for (;;) {
  150. sleep(d_snapshot_interval);
  151. {
  152. snapshotCurrent(tid);
  153. }
  154. }
  155. }
  156. bool NODDB::isNewDomain(const std::string& domain)
  157. {
  158. DNSName dname(domain);
  159. return isNewDomain(dname);
  160. }
  161. bool NODDB::isNewDomain(const DNSName& dname)
  162. {
  163. std::string dname_lc = dname.toDNSStringLC();
  164. // The only time this should block is when snapshotting from the
  165. // housekeeping thread
  166. // the result is always the inverse of what is returned by the SBF
  167. return !d_psbf.testAndAdd(dname_lc);
  168. }
  169. bool NODDB::isNewDomainWithParent(const std::string& domain, std::string& observed)
  170. {
  171. DNSName dname(domain);
  172. return isNewDomainWithParent(dname, observed);
  173. }
  174. bool NODDB::isNewDomainWithParent(const DNSName& dname, std::string& observed)
  175. {
  176. bool ret = isNewDomain(dname);
  177. if (ret == true) {
  178. DNSName mdname = dname;
  179. while (mdname.chopOff()) {
  180. if (!isNewDomain(mdname)) {
  181. observed = mdname.toString();
  182. break;
  183. }
  184. }
  185. }
  186. return ret;
  187. }
  188. void NODDB::addDomain(const DNSName& dname)
  189. {
  190. std::string native_domain = dname.toDNSStringLC();
  191. d_psbf.add(native_domain);
  192. }
  193. void NODDB::addDomain(const std::string& domain)
  194. {
  195. DNSName dname(domain);
  196. addDomain(dname);
  197. }
  198. // UniqueResponseDB Implementation
  199. bool UniqueResponseDB::isUniqueResponse(const std::string& response)
  200. {
  201. return !d_psbf.testAndAdd(response);
  202. }
  203. void UniqueResponseDB::addResponse(const std::string& response)
  204. {
  205. d_psbf.add(response);
  206. }
  207. void UniqueResponseDB::housekeepingThread(std::thread::id tid)
  208. {
  209. setThreadName("pdns-r/UDR-hk");
  210. for (;;) {
  211. sleep(d_snapshot_interval);
  212. {
  213. snapshotCurrent(tid);
  214. }
  215. }
  216. }