- //////////////////////////////////////////////////////////////////////
- // Traffic Montor Control HTTP server
- // Written by Jonathan A. Foster <jon@jfpossibilities.com>
- // Started August 13th, 2021
- // Copyright JF Possibilities, Inc. All rights reserved.
- //
- // Provide a control panel to manage which domains we want to watch,
- // ignore and block.
- //////////////////////////////////////////////////////////////////////
- // TODO: put the note fields to use (dns,dns_wild)
- // TODO: Any purpose in wild-card selection "accepted" host names?
- #include <stdlib.h>
- #include <string>
- #include <stdexcept>
- #include <iostream>
-
- /// C++CMS/DB ///
-
- #include <cppcms/service.h>
- #include <cppcms/applications_pool.h>
- #include <cppcms/mount_point.h>
- #include <cppcms/url_dispatcher.h>
- #include <cppcms/url_mapper.h>
- #include <cppcms/http_request.h>
- #include <cppcms/http_response.h>
- #include <cppdb/frontend.h>
-
- /// Our app ///
-
- #include "../strutil.h"
- #include "data.h"
-
- /// Build flags ///
-
- // TODO: better application of C++CMS booster logging
- //#define DEBUGGIN
-
-
-
- //////////////////////////////////////////////////////////////////////
- // DNS wild card matcher
- //////////////////////////////////////////////////////////////////////
-
- bool dns_wild_match(const StringList &wilds, const std::string &host) {
- for(StringList::const_iterator i=wilds.begin(); i!=wilds.end(); i++) {
- if(host.length()<i->length()) continue;
- if(host==*i) return true;
- if(host.substr(host.length()-i->length()+1)=="."+*i) return true;
- }
- return false;
- }
-
-
-
- //////////////////////////////////////////////////////////////////////
- // C++CMS App to manage the state of all known domain names.
- //////////////////////////////////////////////////////////////////////
-
- const std::string filter_titles[] = { "Undecided", "Accepted", "Blocked" };
- const std::string actions[] = { "Reset", "Accept", "Block" };
- std::string root_uri = "";
-
- struct app: public cppcms::application {
- std::auto_ptr<cppdb::session> sql;
- int items_per_page;
-
-
-
- std::string numlist(const std::string &field) {
- std::string r;
- const cppcms::http::request::form_type &POST = request().post();
- for(
- cppcms::http::request::form_type::const_iterator it = POST.find(field);
- it!=POST.end() && it->first==field;
- it++
- ) {
- if(it->second!="") {
- if(r!="") r+=",";
- r+="'"+sql->escape(it->second)+"'";
- }
- }
- return r;
- }
-
-
-
- /// This looks at new log entries and creates missing DNS entries.
- /// This should be run periodically. Right now we run just prior
- /// displaying a "list" page. This could be used in a CRON job...
-
- void catchup() {
- std::string s;
- StringList list;
- StringList wild;
-
- /// Read list of wild-card blocks ///
-
- cppdb::result r = *sql
- << "SELECT name FROM dns_wild WHERE status=?"
- << Domain::blocked;
- while(r.next()) {
- r >> s;
- wild.push_back(s);
- }
-
-
- /// Auto-add unknown domains to DNS list ///
-
- // Find all connections not recorded in the DNS table
- r = *sql <<
- "SELECT c.them_name "
- "FROM connections c LEFT OUTER JOIN dns ON c.them_name=dns.name "
- "WHERE c.them_name<>'' AND dns.name IS NULL "
- "GROUP BY c.them_name";
- while(r.next()) {
- r >> s;
- list.push_back(s);
- }
-
- // add them
- if(!list.empty()) {
- cppdb::statement q = *sql << "INSERT INTO dns (name, status) VALUES (?,?)";
- for(StringList::iterator i=list.begin(); i!=list.end(); i++) {
- q.reset();
- // If blocked by wild card add it to the blocked list otherwise its
- // undecided.
- q << *i << (Domain::blocked*dns_wild_match(wild, *i)) << cppdb::exec;
- }
- }
- }
-
-
-
- void list(Domain::STATUS sid) {
- int i;
- std::string s;
- DomainList c;
- cppdb::result r;
-
- // TODO: put this someplace else?
-
- /// Form processing ///
-
- if(request().request_method()=="POST") {
- std::string op = request().post("op");
- // nothing to do without a valid "op"
- if(op=="0" || op=="1" || op=="2") {
- // eliminate NOP busywork
- if((s=numlist("id"))!="" && sid!=atoi(op.c_str())) {
- *sql << "UPDATE dns SET status="+op+" WHERE name IN ("+s+")"
- << cppdb::exec;
- }
- if(s=="*" || s=="*.") {
- c.error = "'*' and '*.' are not acceptable.";
- } else if((s=request().post("domain"))!="") {
- // wild card block handling
- if(s.substr(0,2)=="*.") {
- s = s.substr(2);
- if(op=="2")
- *sql << "INSERT INTO dns_wild (name,status) VALUES (?,?)"
- << s << op
- << cppdb::exec;
- else
- c.error = "Wild cards can only be used to <b>block</b> domains. "
- "This has been treated as regular domain prefix "
- "search.";
- }
- // regardless move all existing matches to the specified status.
- *sql << "UPDATE dns SET status=? WHERE name=? OR name LIKE ?"
- << op << s << ("%."+s)
- << cppdb::exec;
- }
- }
- }
-
- /// Update DB with new log data ///
-
- catchup();
-
- /// Produce list of names of the desired STATUS ///
-
- c.filter = filter_titles[sid];
- s = request().get("pg");
- if(s=="")
- c.page = 1;
- else
- c.page = atoi(s.c_str());
- if(c.page < 1 || c.page > 999999) c.page = 1;
- r = *sql << "SELECT name, decided, status FROM dns WHERE status=? "
- "LIMIT "+str((c.page-1)*items_per_page)+","+str(items_per_page)
- << sid;
- for(i = c.list.size(); r.next(); i++) {
- c.list.resize(i+1);
- r >> c.list[i].name >> c.list[i].decided >> c.list[i].status;
- }
- r = *sql << "SELECT count(*) FROM dns WHERE status=?" << sid << cppdb::row;
- r >> c.count;
- c.page_size = items_per_page;
- c.pages = (c.count+items_per_page-1)/items_per_page;
- render("mainskin", "domain_list", c);
- }
-
-
-
- void undecided() { list(Domain::undecided); }
- void accepted() { list(Domain::accepted ); }
- void blocked() { list(Domain::blocked ); }
-
-
-
- app(cppcms::service &s): cppcms::application(s), items_per_page(50) {
- #ifdef DEBUGGIN
- std::cerr << "spawning app object" << std::endl;
- #endif
- sql.reset(new cppdb::session());
- sql->open(settings().get<std::string>("trafficctrl.db"));
-
- mapper().root(root_uri);
- mapper().assign("blocked", "/blocked");
- dispatcher().assign("/blocked/?", &app::blocked, this);
- mapper().assign("accepted", "/accepted");
- dispatcher().assign("/accepted/?", &app::accepted, this);
- mapper().assign("");
- dispatcher().assign("/?", &app::undecided, this);
-
- }
-
- // logging
- void main(const std::string url) {
- #ifdef DEBUGGIN
- std::cerr << "request: " << url << '\n'
- << " INFO: " << request().path_info() << '\n'
- << " SCRIPT: " << request().script_name() << '\n'
- << std::endl;
- #endif
- cppcms::application::main(url);
- }
-
- };
-
-
-
- //////////////////////////////////////////////////////////////////////
- // main() - Launch C++CMS with my test app on "/"
- //////////////////////////////////////////////////////////////////////
-
- int main(int argc, char **args) {
- // create server object
- cppcms::service srv(argc, args);
- // Get root URI from configuration file.
- root_uri = srv.settings().get("trafficctrl.root_uri", "");
- // Mount our app in the server
- srv.applications_pool().mount(
- cppcms::create_pool<app>(),
- cppcms::mount_point(root_uri)
- );
- // Serve it!
- // TODO: log crashes.
- #ifdef DEBUGGIN
- std::cerr << "Launching" << std::endl;
- #endif
- srv.run();
-
- return 0;
- }
-
|