The Poor Man's (or Woman's) Intrusion Detection System
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.
 
 
 
 

267 lines
7.6 KiB

  1. //////////////////////////////////////////////////////////////////////
  2. // Traffic Montor Control HTTP server
  3. // Written by Jonathan A. Foster <jon@jfpossibilities.com>
  4. // Started August 13th, 2021
  5. // Copyright JF Possibilities, Inc. All rights reserved.
  6. //
  7. // Provide a control panel to manage which domains we want to watch,
  8. // ignore and block.
  9. //////////////////////////////////////////////////////////////////////
  10. // TODO: put the note fields to use (dns,dns_wild)
  11. // TODO: Any purpose in wild-card selection "accepted" host names?
  12. #include <stdlib.h>
  13. #include <string>
  14. #include <stdexcept>
  15. #include <iostream>
  16. /// C++CMS/DB ///
  17. #include <cppcms/service.h>
  18. #include <cppcms/applications_pool.h>
  19. #include <cppcms/mount_point.h>
  20. #include <cppcms/url_dispatcher.h>
  21. #include <cppcms/url_mapper.h>
  22. #include <cppcms/http_request.h>
  23. #include <cppcms/http_response.h>
  24. #include <cppdb/frontend.h>
  25. /// Our app ///
  26. #include "../strutil.h"
  27. #include "data.h"
  28. /// Build flags ///
  29. // TODO: better application of C++CMS booster logging
  30. //#define DEBUGGIN
  31. //////////////////////////////////////////////////////////////////////
  32. // DNS wild card matcher
  33. //////////////////////////////////////////////////////////////////////
  34. bool dns_wild_match(const StringList &wilds, const std::string &host) {
  35. for(StringList::const_iterator i=wilds.begin(); i!=wilds.end(); i++) {
  36. if(host.length()<i->length()) continue;
  37. if(host==*i) return true;
  38. if(host.substr(host.length()-i->length()+1)=="."+*i) return true;
  39. }
  40. return false;
  41. }
  42. //////////////////////////////////////////////////////////////////////
  43. // C++CMS App to manage the state of all known domain names.
  44. //////////////////////////////////////////////////////////////////////
  45. const std::string filter_titles[] = { "Undecided", "Accepted", "Blocked" };
  46. const std::string actions[] = { "Reset", "Accept", "Block" };
  47. std::string root_uri = "";
  48. struct app: public cppcms::application {
  49. std::auto_ptr<cppdb::session> sql;
  50. int items_per_page;
  51. std::string numlist(const std::string &field) {
  52. std::string r;
  53. const cppcms::http::request::form_type &POST = request().post();
  54. for(
  55. cppcms::http::request::form_type::const_iterator it = POST.find(field);
  56. it!=POST.end() && it->first==field;
  57. it++
  58. ) {
  59. if(it->second!="") {
  60. if(r!="") r+=",";
  61. r+="'"+sql->escape(it->second)+"'";
  62. }
  63. }
  64. return r;
  65. }
  66. /// This looks at new log entries and creates missing DNS entries.
  67. /// This should be run periodically. Right now we run just prior
  68. /// displaying a "list" page. This could be used in a CRON job...
  69. void catchup() {
  70. std::string s;
  71. StringList list;
  72. StringList wild;
  73. /// Read list of wild-card blocks ///
  74. cppdb::result r = *sql
  75. << "SELECT name FROM dns_wild WHERE status=?"
  76. << Domain::blocked;
  77. while(r.next()) {
  78. r >> s;
  79. wild.push_back(s);
  80. }
  81. /// Auto-add unknown domains to DNS list ///
  82. // Find all connections not recorded in the DNS table
  83. r = *sql <<
  84. "SELECT c.them_name "
  85. "FROM connections c LEFT OUTER JOIN dns ON c.them_name=dns.name "
  86. "WHERE c.them_name<>'' AND dns.name IS NULL "
  87. "GROUP BY c.them_name";
  88. while(r.next()) {
  89. r >> s;
  90. list.push_back(s);
  91. }
  92. // add them
  93. if(!list.empty()) {
  94. cppdb::statement q = *sql << "INSERT INTO dns (name, status) VALUES (?,?)";
  95. for(StringList::iterator i=list.begin(); i!=list.end(); i++) {
  96. q.reset();
  97. // If blocked by wild card add it to the blocked list otherwise its
  98. // undecided.
  99. q << *i << (Domain::blocked*dns_wild_match(wild, *i)) << cppdb::exec;
  100. }
  101. }
  102. }
  103. void list(Domain::STATUS sid) {
  104. int i;
  105. std::string s;
  106. DomainList c;
  107. cppdb::result r;
  108. // TODO: put this someplace else?
  109. /// Form processing ///
  110. if(request().request_method()=="POST") {
  111. std::string op = request().post("op");
  112. // nothing to do without a valid "op"
  113. if(op=="0" || op=="1" || op=="2") {
  114. // eliminate NOP busywork
  115. if((s=numlist("id"))!="" && sid!=atoi(op.c_str())) {
  116. *sql << "UPDATE dns SET status="+op+" WHERE name IN ("+s+")"
  117. << cppdb::exec;
  118. }
  119. if(s=="*" || s=="*.") {
  120. c.error = "'*' and '*.' are not acceptable.";
  121. } else if((s=request().post("domain"))!="") {
  122. // wild card block handling
  123. if(s.substr(0,2)=="*.") {
  124. s = s.substr(2);
  125. if(op=="2")
  126. *sql << "INSERT INTO dns_wild (name,status) VALUES (?,?)"
  127. << s << op
  128. << cppdb::exec;
  129. else
  130. c.error = "Wild cards can only be used to <b>block</b> domains. "
  131. "This has been treated as regular domain prefix "
  132. "search.";
  133. }
  134. // regardless move all existing matches to the specified status.
  135. *sql << "UPDATE dns SET status=? WHERE name=? OR name LIKE ?"
  136. << op << s << ("%."+s)
  137. << cppdb::exec;
  138. }
  139. }
  140. }
  141. /// Update DB with new log data ///
  142. catchup();
  143. /// Produce list of names of the desired STATUS ///
  144. c.filter = filter_titles[sid];
  145. s = request().get("pg");
  146. if(s=="")
  147. c.page = 1;
  148. else
  149. c.page = atoi(s.c_str());
  150. if(c.page < 1 || c.page > 999999) c.page = 1;
  151. r = *sql << "SELECT name, decided, status FROM dns WHERE status=? "
  152. "LIMIT "+str((c.page-1)*items_per_page)+","+str(items_per_page)
  153. << sid;
  154. for(i = c.list.size(); r.next(); i++) {
  155. c.list.resize(i+1);
  156. r >> c.list[i].name >> c.list[i].decided >> c.list[i].status;
  157. }
  158. r = *sql << "SELECT count(*) FROM dns WHERE status=?" << sid << cppdb::row;
  159. r >> c.count;
  160. c.page_size = items_per_page;
  161. c.pages = (c.count+items_per_page-1)/items_per_page;
  162. render("mainskin", "domain_list", c);
  163. }
  164. void undecided() { list(Domain::undecided); }
  165. void accepted() { list(Domain::accepted ); }
  166. void blocked() { list(Domain::blocked ); }
  167. app(cppcms::service &s): cppcms::application(s), items_per_page(50) {
  168. #ifdef DEBUGGIN
  169. std::cerr << "spawning app object" << std::endl;
  170. #endif
  171. sql.reset(new cppdb::session());
  172. sql->open(settings().get<std::string>("trafficctrl.db"));
  173. mapper().root(root_uri);
  174. mapper().assign("blocked", "/blocked");
  175. dispatcher().assign("/blocked/?", &app::blocked, this);
  176. mapper().assign("accepted", "/accepted");
  177. dispatcher().assign("/accepted/?", &app::accepted, this);
  178. mapper().assign("");
  179. dispatcher().assign("/?", &app::undecided, this);
  180. }
  181. // logging
  182. void main(const std::string url) {
  183. #ifdef DEBUGGIN
  184. std::cerr << "request: " << url << '\n'
  185. << " INFO: " << request().path_info() << '\n'
  186. << " SCRIPT: " << request().script_name() << '\n'
  187. << std::endl;
  188. #endif
  189. cppcms::application::main(url);
  190. }
  191. };
  192. //////////////////////////////////////////////////////////////////////
  193. // main() - Launch C++CMS with my test app on "/"
  194. //////////////////////////////////////////////////////////////////////
  195. int main(int argc, char **args) {
  196. // create server object
  197. cppcms::service srv(argc, args);
  198. // Get root URI from configuration file.
  199. root_uri = srv.settings().get("trafficctrl.root_uri", "");
  200. // Mount our app in the server
  201. srv.applications_pool().mount(
  202. cppcms::create_pool<app>(),
  203. cppcms::mount_point(root_uri)
  204. );
  205. // Serve it!
  206. // TODO: log crashes.
  207. #ifdef DEBUGGIN
  208. std::cerr << "Launching" << std::endl;
  209. #endif
  210. srv.run();
  211. return 0;
  212. }