"Fossies" - the Fresh Open Source Software Archive

Member "hermes-1.9/src/hermes.cpp" (5 Sep 2014, 11643 Bytes) of package /linux/privat/hermes-1.9.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "hermes.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.8_vs_1.9.

    1 /**
    2  * hermes antispam proxy
    3  * Copyright (C) 2006, 2007 Juan José Gutiérrez de Quevedo <juanjo@gutierrezdequevedo.com>
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of the GNU General Public License as published by
    7  * the Free Software Foundation; version 2 of the License
    8  *
    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.
   13  *
   14  * You should have received a copy of the GNU General Public License along
   15  * with this program; if not, write to the Free Software Foundation, Inc.,
   16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   17  *
   18  * @author Juan José Gutiérrez de Quevedo <juanjo@gutierrezdequevedo.com>
   19  */
   20 #include <iostream>
   21 #include <list>
   22 #include <stack>
   23 #include <pthread.h>
   24 #include <signal.h>
   25 #include <time.h>
   26 #ifdef HAVE_SSL
   27 #include <openssl/crypto.h>
   28 #endif //HAVE_SSL
   29 #ifndef WIN32
   30 #include <grp.h>
   31 #endif //WIN32
   32 
   33 #include "Proxy.h"
   34 #include "Socket.h"
   35 #include "ServerSocket.h"
   36 #include "Configfile.h"
   37 #include "Utils.h"
   38 #include "Logger.h"
   39 
   40 using namespace std;
   41 
   42 void *thread_main(void *);
   43 void *cleaner_thread_run(void *);
   44 void exit_requested(int);
   45 
   46 //global var to know when we have to exit
   47 bool quit=false;
   48 
   49 //mutexes
   50 pthread_mutex_t childrenlist_mutex=PTHREAD_MUTEX_INITIALIZER;
   51 pthread_mutex_t info_stack_mutex=PTHREAD_MUTEX_INITIALIZER;
   52 
   53 //our config
   54 Configfile cfg;
   55 
   56 //our logger
   57 LOGGER_CLASS hermes_log;
   58 
   59 //this variable is thread-local to allow having a unique id per-thread which we can
   60 //print at the start of log messages
   61 __thread unsigned long connection_id;
   62 
   63 list<unsigned long> children;
   64 
   65 #ifdef HAVE_SSL
   66 pthread_mutex_t ssl_locks[CRYPTO_NUM_LOCKS]={PTHREAD_MUTEX_INITIALIZER};
   67 
   68 void ssl_locking_function(int mode,int n,const char *file,int line)
   69 {
   70   if(n>CRYPTO_NUM_LOCKS)
   71     throw Exception(_("Error, "+Utils::inttostr(n)+" is bigger than CRYPTO_NUM_LOCKS("+Utils::inttostr(CRYPTO_NUM_LOCKS)+")"),__FILE__,__LINE__);
   72   if(mode&CRYPTO_LOCK)
   73     pthread_mutex_lock(&ssl_locks[n]);
   74   else
   75     pthread_mutex_unlock(&ssl_locks[n]);
   76 }
   77 #endif //HAVE_SSL
   78 
   79 int
   80 #ifdef WIN32_SERVICE
   81 hermes_main
   82 #else
   83 main
   84 #endif //WIN32_SERVICE
   85 (int argc,char *argv[])
   86 {
   87   /* TODO:think of this again
   88   if(argc>2)
   89   {
   90     for(unsigned i=1;i<argc;i++)
   91     {
   92       argv++
   93   }
   94   */
   95 
   96   #ifdef HAVE_SSL
   97     CRYPTO_set_locking_callback(ssl_locking_function);
   98     #ifndef WIN32 //getpid() returns different values for threads on windows, therefor this is not needed
   99     CRYPTO_set_id_callback(pthread_self);
  100     #endif //WIN32
  101   #endif //HAVE_SSL
  102   try
  103   {
  104     if(2==argc)
  105     {
  106       if(!Utils::file_exists(argv[1]))
  107         throw Exception(string(_("Config file "))+argv[1]+_(" doesn't exist or is not readable."),__FILE__,__LINE__);
  108       cfg.parse(argv[1]);
  109     }
  110     else
  111       throw Exception(_("Config file not specified"), __FILE__, __LINE__);
  112     cfg.validateConfig();
  113   }
  114   catch(Exception &e)
  115   {
  116     LERR(e);
  117     return -1;
  118   }
  119 
  120   unsigned long nconns=0;
  121 
  122   signal(SIGTERM,exit_requested);
  123   signal(SIGINT,exit_requested);
  124   #ifndef WIN32
  125   signal(SIGCHLD,SIG_IGN);
  126   signal(SIGPIPE,SIG_IGN);
  127   #endif //WIN32
  128 
  129   //we have to create the server socket BEFORE chrooting, because if we don't,
  130   //SSL cannot initialize because it's missing libz
  131   ServerSocket server;
  132   pthread_t cleaner_thread;
  133   string peer_address;
  134 
  135   #ifndef WIN32
  136     if(cfg.getBackground())
  137     {
  138       int retval;
  139 
  140       retval=fork();
  141       if(retval>0)
  142         exit(0); //succesful fork
  143 
  144       if(retval<0)
  145       {
  146         LERR(_("Error forking into the background") + Utils::errnotostrerror(errno));
  147         return -1;
  148       }
  149     }
  150     
  151     if(cfg.getPidFile()!="")
  152     {
  153       try
  154       {
  155         Utils::write_pid(cfg.getPidFile(),getpid());
  156       }
  157       catch(Exception &e)
  158       {
  159         LERR(e);
  160       }
  161     }
  162 
  163     if(cfg.getChroot()!="")
  164     {
  165       //this is needed to get hermes to load the dns resolver BEFORE chrooting
  166       (void)gethostbyname("hermes-project.com");
  167       if(-1 == chdir(cfg.getChroot().c_str()))
  168       {
  169         LERR(_("Couldn't chdir into ") + cfg.getChroot() + " " + Utils::errnotostrerror(errno) );
  170         return -1;
  171       }
  172       if(-1==chroot(cfg.getChroot().c_str()))
  173       {
  174         LERR(_("Couldn't chroot ") + Utils::errnotostrerror(errno));
  175         return -1;
  176       }
  177       if(-1 == chdir("/"))
  178       {
  179         LERR(_("Couldn't chdir into /, this shouldn't happen: " + Utils::errnotostrerror(errno)) );
  180         return -1;
  181       }
  182     }
  183   #endif //WIN32
  184 
  185   LINF("Starting hermes with pid "+Utils::inttostr(getpid()));
  186   try
  187   {
  188     server.init();
  189     server.setPort(cfg.getListeningPort());
  190     server.listen(cfg.getListeningPort(),cfg.getBindTo());
  191   }
  192   catch(Exception &e)
  193   {
  194     LERR(e);
  195     return -1; //couldn't bind, exit
  196   }
  197 
  198   #ifndef WIN32
  199   if(cfg.getDropPrivileges())
  200   {
  201     //drop privileges once we have opened the listening port
  202     if(-1 == setgroups(0,NULL))
  203     {
  204       LERR(_("Error dropping priviledges " + Utils::errnotostrerror(errno)) );
  205       return -1;
  206     }
  207     if(-1 == setgid(cfg.getGid()))
  208     {
  209       LERR(_("Error setting gid " + Utils::inttostr(cfg.getGid()) + " " + Utils::errnotostrerror(errno)) );
  210       return -1;
  211     }
  212     if(-1 == setuid(cfg.getUid()))
  213     {
  214       LERR(_("Error setting uid " + Utils::inttostr(cfg.getUid()) + " " + Utils::errnotostrerror(errno)) );
  215       return -1;
  216     }
  217     if(-1 == setuid(cfg.getUid()))
  218     {
  219       LERR(_("Error setting uid " + Utils::inttostr(cfg.getUid()) + " " + Utils::errnotostrerror(errno)) );
  220       return -1;
  221     }
  222   }
  223   #endif //WIN32
  224 
  225   /* start our cleaner thread */
  226   if(cfg.getCleanDb())
  227     pthread_create(&cleaner_thread,NULL,cleaner_thread_run,NULL);
  228 
  229   new_conn_info info;
  230   stack<new_conn_info> info_stack;
  231   while(!quit)
  232   {
  233     if(server.canRead(1)) //wait one second for incoming connections, if none then loop again(allows us to check for SIGTERM and SIGINT)
  234     {
  235       pthread_t thread;
  236       pthread_attr_t thread_attr;
  237       pthread_attr_init(&thread_attr);
  238       pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
  239 
  240       int retval;
  241       int fd=server.accept(&peer_address);
  242       info.new_fd=fd;
  243       info.peer_address=peer_address;
  244       info.connection_id=++nconns;
  245       pthread_mutex_lock(&info_stack_mutex);
  246       info_stack.push(info);
  247       pthread_mutex_unlock(&info_stack_mutex);
  248       retval=pthread_create(&thread,&thread_attr,thread_main,(void *)&info_stack);
  249       if(retval)
  250       {
  251         LERR(_("Error creating thread: ") + Utils::errnotostrerror(retval) + _(". Sleeping 5 seconds before continuing..."));
  252         sleep(5);
  253       }
  254       else
  255       {
  256         #ifdef WIN32
  257         LDEB("New thread created [" + Utils::ulongtostr(nconns) + "] thread_id: " + Utils::ulongtostr((unsigned long)thread.p) + ":" + Utils::ulongtostr(thread.x));
  258         #else
  259         LDEB("New thread created [" + Utils::ulongtostr(nconns) + "] thread_id: " + Utils::ulongtostr(thread));
  260         #endif //WIN32
  261         pthread_mutex_lock(&childrenlist_mutex);
  262         children.push_back(nconns);
  263         pthread_mutex_unlock(&childrenlist_mutex);
  264       }
  265     }
  266   }
  267 
  268   //close connection so that the port is no longer usable
  269   server.close();
  270 
  271   // wait for all threads to finish
  272   LINF("Waiting for threads to finish");
  273   #ifndef WIN32
  274   while(children.size())
  275   {
  276     if(false==cfg.getBackground())
  277     {
  278       cout << "Threads active:" << children.size() << (char)13;
  279       fflush(stdout);
  280     }
  281     sleep(1);
  282   }
  283   #endif //WIN32
  284   if(cfg.getCleanDb())
  285     pthread_join(cleaner_thread,NULL);
  286 
  287   #ifndef WIN32
  288   if(false==cfg.getBackground())
  289     cout << endl;
  290   #endif //WIN32
  291 
  292   #ifdef HAVE_SPF
  293   Spf::deinitialize();
  294   #endif //HAVE_SPF
  295   return 0;
  296 }
  297 
  298 /**
  299  * this threads cleans the database once each hour, deleting
  300  * the records on the database that have an expire time < now
  301  *
  302  */
  303 void *cleaner_thread_run(void *)
  304 {
  305   try
  306   {
  307     Database db;
  308     time_t next_run=time(NULL)+3600;
  309 
  310     db.setDatabaseFile(cfg.getDatabaseFile());
  311 
  312     db.open();
  313     while(!quit)
  314     {
  315       time_t now=time(NULL);
  316       sched_yield();
  317       if(now>next_run)
  318       {
  319         unsigned long spamcount=0;
  320 
  321         next_run=now+3600; //if we just add 3600 like before, then if 
  322         //time changes during execution of hermes this will run
  323         //every few seconds instead of every hour
  324 
  325         try
  326         {
  327           spamcount=db.cleanDB();
  328           LDEB("Cleaning database, cleaning "+Utils::inttostr(spamcount)+" blocked spams.");
  329         }
  330         catch(Exception &e)
  331         {
  332           LERR("Error cleaning the database: " + string(e));
  333         }
  334         if(spamcount>0&&cfg.getSubmitStats())
  335         {
  336           try
  337           {
  338             Socket s;
  339             string server_response;
  340 
  341             s.init();
  342             s.connect("stats.hermes-project.com",11125);
  343             #ifdef HAVE_SSL
  344             if(cfg.getSubmitStatsSsl())
  345             {
  346               s.writeLine("ssl");
  347               s.prepareSSL(false);
  348               s.startSSL(false);
  349             }
  350             else
  351             #endif //HAVE_SSL
  352               s.writeLine("non-ssl");
  353             s.writeLine(cfg.getSubmitStatsUsername());
  354             s.writeLine(cfg.getSubmitStatsPassword());
  355             s.writeLine(Utils::inttostr(spamcount));
  356             server_response=s.readLine();
  357             s.close();
  358             if("OK"!=server_response)
  359               throw Exception(server_response,__FILE__,__LINE__);
  360           }
  361           catch(Exception &e)
  362           {
  363             LDEB("Exception sending stats: "+string(e));
  364           }
  365         }
  366       }
  367       #ifndef WIN32
  368       if(false==cfg.getBackground())
  369       {
  370         if(!(now%10)) //echo info each 10 seconds
  371         {
  372           stringstream ss;
  373 
  374           pthread_mutex_lock(&childrenlist_mutex);
  375           ss << children.size() << " threads running: ";
  376           for(list<unsigned long>::iterator i=children.begin();i!=children.end();i++)
  377             ss << "[ " << *i << " ] ";
  378           pthread_mutex_unlock(&childrenlist_mutex);
  379           ss << endl;
  380           cout << ss.str();
  381         }
  382       }
  383       #endif //WIN32
  384       sleep(1);
  385     }
  386     db.close();
  387   }
  388   catch(Exception &e)
  389   {
  390     LERR(e);
  391   }
  392   return NULL;
  393 }
  394 
  395 void remove_child_from_childlist(unsigned long child_id)
  396 {
  397   pthread_mutex_lock(&childrenlist_mutex);
  398   children.remove(child_id);
  399   pthread_mutex_unlock(&childrenlist_mutex);
  400 }
  401 
  402 void *thread_main(void *info_stack)
  403 {
  404   try
  405   {
  406     Socket client; //for the input connection from the client
  407     Proxy p;
  408     new_conn_info peerinfo;
  409 
  410     //read a new peerinfo from the stack
  411     pthread_mutex_lock(&info_stack_mutex);
  412     peerinfo=((stack<new_conn_info>*)info_stack)->top();
  413     ((stack<new_conn_info>*)info_stack)->pop();
  414     pthread_mutex_unlock(&info_stack_mutex);
  415 
  416     connection_id=peerinfo.connection_id;
  417     client.setFD(peerinfo.new_fd);
  418     p.setOutside(client);
  419     p.run(peerinfo.peer_address);
  420     remove_child_from_childlist(connection_id);
  421   }
  422   catch(Exception &e)
  423   {
  424     LDEB(e);
  425   }
  426   return NULL;
  427 }
  428 
  429 void exit_requested(int)
  430 {
  431   if(!quit)
  432   {
  433     quit=true;
  434     #ifndef WIN32
  435     if(false==cfg.getBackground())
  436       cout << "Hit control+c again to force-quit" << endl;
  437     #endif //WIN32
  438   }
  439   else
  440     exit(-1);
  441 }
  442 
  443 #ifdef WIN32
  444 //pthreads on win32 doesn't provide an operator== for pthread_t
  445 //and it's also an struct, not an int, so supply one operator== here
  446 bool operator==(pthread_t t1,pthread_t t2)
  447 {
  448   return t1.p==t2.p&&t1.x==t2.x;
  449 }
  450 #endif //WIN32