"Fossies" - the Fresh Open Source Software Archive

Member "postal-0.76/bhm.cpp" (30 Jun 2016, 11319 Bytes) of package /linux/privat/postal-0.76.tgz:


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 "bhm.cpp" see the Fossies "Dox" file reference documentation.

    1 #ifndef _GNU_SOURCE
    2 #define _GNU_SOURCE
    3 #endif
    4 
    5 #include "bhmusers.h"
    6 #include <errno.h>
    7 #include <ctype.h>
    8 #include <unistd.h>
    9 #include <sys/wait.h>
   10 #include <signal.h>
   11 #include <stdio.h>
   12 #include <sys/poll.h>
   13 #include <sys/types.h>
   14 #include <sys/socket.h>
   15 #include <arpa/inet.h>
   16 #include <netinet/ip.h>
   17 
   18 #include "postal.h"
   19 #include "logit.h"
   20 #include "results.h"
   21 #include "basictcp.h"
   22 #ifdef USE_GNUTLS
   23 #include <errno.h>
   24 #if GNUTLS_VERSION_NUMBER <= 0x020b00
   25 #include <gcrypt.h>
   26 GCRY_THREAD_OPTION_PTHREAD_IMPL;
   27 #endif /* GNUTLS_VERSION_NUMBER */
   28 #endif
   29 
   30 int processes = 0;
   31 int *thread_status;
   32 
   33 void usage(CPCCHAR msg = NULL)
   34 {
   35   if(msg)
   36     printf("Error: %s\n\n", msg);
   37 
   38   printf("Usage: bhm [-m maximum-message-size] [-t threads] [-p port]"
   39 #ifdef USE_SSL
   40          " [-s]"
   41 #endif
   42          "\n"
   43          "              [-a] [-(z|Z) debug-file] [-r reverse-dns] [-b bind address ]\n"
   44          "              user-list-filename\n"
   45          "\n"
   46          "Postal Version: " VER_STR "\n");
   47   close(1);
   48   exit(eParam);
   49 }
   50 
   51 int maxMsgSize = 10240;
   52 results res;
   53 Logit *log;
   54 
   55 int exitCount = 0;
   56 
   57 void endit(int)
   58 {
   59   exitCount++;
   60   if(exitCount > 2)
   61     exit(1);
   62 }
   63 
   64 typedef struct
   65 {
   66   int threadNum;
   67   int fd;
   68   struct sockaddr_in addr;
   69   int ssl;
   70   Logit *debug;
   71 } thread_data;
   72 
   73 enum { eFree = 0, eUsed, eFinished };
   74 
   75 int check_sender(CPCCHAR addr)
   76 {
   77   return 0;
   78 }
   79 
   80 int readCommand(base_tcp &t, char *buf, int bufSize, bool stripCR, int timeout = 60);
   81 
   82 // return 1 for successfully accepting an address, 0 for quit, <0 for error
   83 // return 2 for data
   84 int recv_addr(base_tcp &t, char *buf, const size_t buf_len, CPCCHAR msg, string &addr, bool do_data)
   85 {
   86   while(1)
   87   {
   88     char *tmp;
   89     bool syntax_warn = false;
   90     int len = readCommand(t, buf, buf_len, true);
   91     if(len < 0)
   92       return len;
   93     if(!strncasecmp(buf, "quit", 4) && (isblank(buf[4]) || buf[4] == '\0'))
   94       return 0;
   95     if(do_data && !strncasecmp(buf, "data", 4) && (len == 4 || (isblank(buf[4])  && len == 5)))
   96       return 2;
   97     if(!strncasecmp(buf, msg, strlen(msg)))
   98     {
   99       CPCCHAR bad_sender_msg = "501 Bad address syntax\r\n";
  100       if(buf[len - 1] == '\r')
  101       {
  102         len--;
  103         buf[len] = '\0';
  104       }
  105       tmp = buf + strlen(msg);
  106       while(isblank(*tmp))
  107         tmp++;
  108       if(*tmp != '<' && buf[len - 1] != '>')
  109       {
  110         syntax_warn = true;
  111       }
  112       else if(*tmp != '<' || buf[len - 1] != '>')
  113       {
  114         t.sendCstr(bad_sender_msg);
  115         res.error();
  116         continue;
  117       }
  118       else
  119       {
  120         tmp++;
  121         len--;
  122         buf[len] = '\0';
  123       }
  124       if(check_sender(tmp))
  125       {
  126         t.sendCstr(bad_sender_msg);
  127         res.error();
  128         continue;
  129       }
  130       addr = tmp;
  131       if(syntax_warn)
  132         t.sendCstr("250 Ok but you should use <addr>\r\n");
  133       else
  134         t.sendCstr("250 Ok\r\n");
  135       break;
  136     }
  137     res.error();
  138     int rc = t.sendCstr("502 Error: Command not recognised or not appropriate at this time\r\n");
  139     if(rc < 0)
  140       return rc;
  141   }
  142   return 1;
  143 }
  144 
  145 // return 1 for successfully accepting a message, 0 for quit, -1 for error
  146 int recv_msg(base_tcp &t, char *buf, const size_t buf_len)
  147 {
  148   string sender;
  149   string recipient;
  150   int rc = recv_addr(t, buf, buf_len, "mail from:", sender, false);
  151   if(rc != 1)
  152     return rc;
  153 
  154   int recipient_count = 0;
  155   while( (rc = recv_addr(t, buf, buf_len, "rcpt to:", recipient, recipient_count)) == 1)
  156   {
  157     recipient_count++;
  158   }
  159   if(rc <= 0)
  160     return rc;
  161   t.sendCstr("354 End data with <CR><LF>.<CR><LF>\r\n");
  162   while( (rc = readCommand(t, buf, buf_len, true)) >= 0)
  163   {
  164     if(!strcmp(".", buf))
  165       break;
  166   }
  167   if(rc < 0)
  168   {
  169     res.error();
  170     return rc;
  171   }
  172   struct timespec req;
  173   req.tv_sec = 0;
  174   req.tv_nsec = 1000000;
  175   nanosleep(&req, NULL);
  176   t.sendCstr("250 Ok: queued on /dev/null\r\n");
  177   res.message();
  178   return 1;
  179 }
  180 
  181 const char *hostname = "bhm.coker.com.au";
  182 
  183 int do_helo(base_tcp &t, char *buf, const size_t buf_len)
  184 {
  185   int len;
  186   while(1)
  187   {
  188     int rc;
  189     if( (len = t.readLine(buf, buf_len, true)) < 0)
  190     {
  191       res.error();
  192       return -1;
  193     }
  194     if(buf[len - 1] == '\r')
  195     {
  196       len--;
  197       buf[len] = '\0';
  198     }
  199     bool basicHello = false;
  200     if(!strncasecmp("quit", buf, 4))
  201     {
  202       t.sendCstr("221 Bye\r\n");
  203       return -2;
  204     }
  205     if(!strncasecmp("helo", buf, 4))
  206     {
  207       basicHello = true;
  208     }
  209     else if(strncasecmp("ehlo", buf, 4))
  210     {
  211       res.error();
  212       if(t.sendCstr("502 Error: command not recognized\r\n") < 0)
  213         return -3;
  214       continue;
  215     }
  216     if(len < 6 || !isblank(buf[4]))
  217     {
  218       res.error();
  219       if(t.sendCstr("501 Syntax: EHLO hostname or HELO hostname\r\n") < 0)
  220         return -3;
  221       continue;
  222     }
  223     string remoteName(buf + 5);
  224     if(basicHello)
  225       rc = t.printf("250 %s\r\n", hostname);
  226     else
  227       rc = t.printf("250-%s\r\n250-PIPELINING\r\n"
  228 #ifdef USE_SSL
  229                     "250-STARTTLS\r\n"
  230 #endif
  231                     "250 SIZE %d\r\n", hostname, maxMsgSize * 1024);
  232     if(rc < 0)
  233     {
  234       res.error();
  235       return -1;
  236     }
  237     break;
  238   }
  239   return 0;
  240 }
  241 
  242 int readCommand(base_tcp &t, char *buf, int bufSize, bool stripCR, int timeout)
  243 {
  244 #ifdef USE_SSL
  245   if(!t.isTLS())
  246   {
  247     int rc = t.readLine(buf, bufSize, stripCR, timeout);
  248     if(rc < 0 || strcasecmp(buf, "starttls"))
  249       return rc;
  250     t.printf("220 2.0.0 Ready to start TLS\r\n");
  251     if(t.ConnectTLS())
  252       return -1;
  253     res.connect_ssl();
  254     rc = do_helo(t, buf, bufSize);
  255     if(rc < 0)
  256       return rc; // need to make sure values are consistent
  257     rc = t.readLine(buf, bufSize, stripCR, timeout);
  258     return rc;
  259   }
  260   else
  261 #endif
  262   return t.readLine(buf, bufSize, stripCR, timeout);
  263 }
  264 
  265 void do_work(thread_data *td)
  266 {
  267   base_tcp t(td->fd, log, td->debug, &res
  268 #ifdef USE_SSL
  269     , td->ssl
  270 #endif
  271   );
  272   struct sockaddr_in name;
  273   socklen_t namelen = sizeof(name);
  274   char buf[1024];
  275 
  276   int rc = getsockname(td->fd, (sockaddr *)&name, &namelen);
  277 
  278   if(rc)
  279   {
  280     fprintf(stderr, "Error getting socket name.\n");
  281     return;
  282   }
  283 /*  if(!inet_ntop(name.sin_family, &name.sin_addr, buf, sizeof(buf)))
  284     printf("error decoding\n");
  285   else
  286     printf("Addr:%s\n", buf);
  287 */
  288   t.printf("220 %s ESMTP BHM\r\n", hostname);
  289   do_helo(t, buf, sizeof(buf));
  290   while( (rc = recv_msg(t, buf, sizeof(buf))) == 1)
  291     { }
  292   if(rc == eTimeout)
  293     t.printf("421 %s Error: timeout exceeded\r\n", hostname);
  294   if(rc < 0)
  295   {
  296     res.error();
  297     return;
  298   }
  299   if(rc == 0)
  300     t.sendCstr("221 2.0.0 Bye\r\n");
  301 }
  302 
  303 PVOID smtp_worker(PVOID param)
  304 {
  305   thread_data *td = (thread_data *)param;
  306   do_work(td);
  307   if(td->debug)
  308     delete td->debug;
  309   thread_status[td->threadNum] = eFinished;
  310   close(td->fd);
  311   free(td);
  312   return 0;
  313 }
  314 
  315 int main(int argc, char **argv)
  316 {
  317   int maxThreads = 100;
  318   bool allLog = false;
  319   PCCHAR debugName = NULL;
  320   bool debugMultipleFiles = false;
  321 #ifdef USE_SSL
  322   int use_ssl = false;
  323 #endif
  324   unsigned short port = 25;
  325   struct in_addr sin_addr;
  326   sin_addr.s_addr = INADDR_ANY;
  327 #ifdef USE_GNUTLS
  328 #if GNUTLS_VERSION_NUMBER <= 0x020b00
  329   gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
  330 #endif
  331   gnutls_global_init();
  332   if(!gnutls_check_version(GNUTLS_VER))
  333   {
  334     fprintf(stderr, "Needs version " GNUTLS_VER " of GNUTLS\n");
  335     exit(1);
  336   }
  337 #endif
  338 
  339   int c;
  340   while(-1 != (c = getopt(argc, argv, "b:m:p:st:z:Z:")) )
  341   {
  342     switch(char(c))
  343     {
  344       case '?':
  345       case ':':
  346         usage();
  347       break;
  348       case 'a':
  349         allLog = true;
  350       break;
  351       case 'b':
  352         if(!inet_aton(optarg, &sin_addr))
  353         {
  354           fprintf(stderr, "Invalid bind address \"%s\"\n", optarg);
  355           return 1;
  356         }
  357       break;
  358       case 'm':
  359         maxMsgSize = atoi(optarg);
  360       break;
  361       case 'p':
  362         port = atoi(optarg);
  363       break;
  364       case 's':
  365 #ifdef USE_SSL
  366         use_ssl = true;
  367 #else
  368         usage("SSL not supported.\n");
  369 #endif
  370       break;
  371       case 't':
  372         maxThreads = atoi(optarg);
  373       break;
  374       case 'Z':
  375         debugMultipleFiles = true;
  376       case 'z':
  377         debugName = optarg;
  378       break;
  379     }
  380   }
  381   if(maxMsgSize < 0 || maxMsgSize > MAX_MSG_SIZE)
  382     usage("Bad maximum message size.\n");
  383 
  384   if(maxThreads < 0 || maxThreads > MAX_PROCESSES)
  385     usage("Bad maximum process limit.\n");
  386 
  387   if(optind + 1 != argc)
  388     usage();
  389 
  390   BHMUsers user_list(argv[optind]);
  391 
  392   struct sigaction sa;
  393   memset(&sa, 0, sizeof(sa));
  394   sa.sa_sigaction = NULL;
  395   sa.sa_handler = SIG_IGN;
  396   sa.sa_flags = 0;
  397   if(sigaction(SIGPIPE, &sa, NULL))
  398   {
  399     printf("Can't block SIGPIPE.\n");
  400     return eSystem;
  401   }
  402 
  403   sa.sa_flags = SA_SIGINFO;
  404   sa.sa_handler = &endit;
  405   if(sigaction(SIGINT, &sa, NULL))
  406   {
  407     printf("Can't handle SIGINT.\n");
  408     return eSystem;
  409   }
  410 
  411   printf("time,messages,data(K),errors,connections"
  412 #ifdef USE_SSL
  413     ",SSL connections"
  414 #endif
  415     "\n");
  416 
  417   log = new Logit("bhm.log", allLog, false, 0);
  418   Logit *debug = NULL;
  419 
  420   if(debugName)
  421     debug = new Logit(debugName, false, debugMultipleFiles, 0);
  422 
  423   int listen_fd = socket(PF_INET, SOCK_STREAM, 0);
  424   struct sockaddr_in in;
  425   in.sin_family = AF_INET;
  426   in.sin_port = htons(port);
  427   memcpy(&in.sin_addr, &sin_addr, sizeof(sin_addr));
  428   if(listen_fd == -1 || bind(listen_fd, (sockaddr *)&in, sizeof(in))
  429    || listen(listen_fd, 10))
  430   {
  431     fprintf(stderr, "Can't bind to port.\n");
  432     return 1;
  433   }
  434   struct pollfd pfd;
  435   pfd.fd = listen_fd;
  436   pfd.events = POLLIN;
  437   pthread_t *thread_info = (pthread_t *)calloc(sizeof(thread_info), maxThreads);
  438   thread_status = (int *)calloc(sizeof(int), maxThreads);
  439   time_t nextPrint = time(NULL)/60*60+60;
  440   while(1)
  441   {
  442     time_t t = time(NULL);
  443     if(t >= nextPrint)
  444     {
  445       res.print();
  446       nextPrint += 60;
  447     }
  448     int timeout = (nextPrint - t) * 1000;
  449     int rc = poll(&pfd, 1, timeout);
  450     if(rc < 0)
  451     {
  452       if(errno == EINTR)
  453         continue;
  454       fprintf(stderr, "Poll error on accept.\n");
  455       return 1;
  456     }
  457     int fd;
  458     struct sockaddr_in addr;
  459     socklen_t addrlen = sizeof(addr);
  460     if(rc == 1)
  461     {
  462       fd = accept(listen_fd, (sockaddr*)&addr, &addrlen);
  463       if(fd == -1)
  464       {
  465         res.error();
  466         continue;
  467       }
  468       res.connection();
  469       processes++;
  470       int i;
  471       for(i = 0; i < maxThreads && thread_status[i] == eUsed; i++)
  472         ;
  473       if(thread_status[i] == eFinished)
  474       {
  475         void *value_ptr;
  476         pthread_join(thread_info[i], &value_ptr);
  477         processes--;
  478       }
  479 
  480       thread_status[i] = eUsed;
  481 
  482       pthread_attr_t attr;
  483       if(pthread_attr_init(&attr)
  484        || pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)
  485        || pthread_attr_setstacksize(&attr, 32*1024))
  486         fprintf(stderr, "Can't set thread attributes.\n");
  487       thread_data *td = (thread_data *)malloc(sizeof(thread_data));
  488       td->threadNum = i;
  489       td->fd = fd;
  490       memcpy(&td->addr, &addr, sizeof(addr));
  491       td->debug = debug ? new Logit(*debug, i) : NULL;
  492 #ifdef USE_SSL
  493       td->ssl = use_ssl;
  494 #endif
  495       int p = pthread_create(&thread_info[i], &attr, smtp_worker, PVOID(td));
  496       pthread_attr_destroy(&attr);
  497       if(p)
  498       {
  499         fprintf(stderr, "Can't create a thread.\n");
  500         return 1;
  501         if(debug)
  502           delete debug;
  503       }
  504     }
  505   }
  506   if(debug)
  507     delete debug;
  508   return 0;
  509 }
  510