source: npl/mailserver/dspam/dspam-3.10.2/src/daemon.c @ c5c522c

gcc484ntopperl-5.22
Last change on this file since c5c522c was c5c522c, checked in by Edwin Eefting <edwin@datux.nl>, 8 years ago

initial commit, transferred from cleaned syn3 svn tree

  • Property mode set to 100644
File size: 31.1 KB
Line 
1/* $Id: daemon.c,v 1.23 2012/02/09 22:05:07 sbajic Exp $ */
2
3/*
4 DSPAM
5 COPYRIGHT (C) 2002-2012 DSPAM PROJECT
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Affero General Public License as
9 published by the Free Software Foundation, either version 3 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20*/
21
22/*
23 * daemon.c - server daemon codebase (for operating in client/daemon mode)
24 *
25 * DESCRIPTION
26 *   The code in this section is responsible for managing the DSPAM agent as
27 *   a server-side process when --daemon is called. It ties in with many
28 *   standard agent processing functions..
29 */
30
31#ifdef HAVE_CONFIG_H
32#include <auto-config.h>
33#endif
34
35#ifdef DAEMON
36
37#define RSET(A) ( A && !strcmp(A, "RSET") )
38
39#include <string.h>
40#include <stdlib.h>
41#include <ctype.h>
42#include <errno.h>
43#include <error.h>
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <sys/socket.h>
47#include <sys/resource.h>
48#include <sys/un.h>
49#include <netdb.h>
50#include <netinet/in.h>
51#include <netinet/tcp.h>
52#include <arpa/inet.h>
53#include <signal.h>
54
55#ifndef _WIN32
56#include <unistd.h>
57#include <pwd.h>
58#endif
59
60#include <stdio.h>
61#include <fcntl.h>
62
63#include "daemon.h"
64#include "client.h"
65#include "dspam.h"
66#include "libdspam.h"
67#include "config.h"
68#include "util.h"
69#include "buffer.h"
70#include "language.h"
71
72/*
73 * daemon_listen(DRIVER_CTX *DTX)
74 *
75 * DESCRIPTION
76 *   primary daemon loop
77 *
78 *   this function is called by the agent when --daemon is specified on the
79 *   commandline, and is responsible for innitializing and managing core daemon
80 *   services. these include listening for and accepting incoming connections
81 *   and spawning new protocol handler threads.
82 *
83 * INPUT ARGUMENTS
84 *     DTX    driver context (containing cached database connections)
85 *
86 * RETURN VALUES
87 *   returns 0 on success
88 */
89
90int daemon_listen(DRIVER_CTX *DTX) {
91  struct sockaddr_in local_addr, remote_addr;
92  THREAD_CTX *TTX = NULL;
93  fd_set master, read_fds;
94  pthread_attr_t attr;
95  struct timeval tv;
96  int fdmax, yes = 1;
97  int domain = 0;               /* listening on domain socket? */
98  int listener;                 /* listener fd */
99  int i;
100  int port = 24, queue = 32;    /* default port and queue size */
101
102  signal(SIGPIPE, SIG_IGN);
103  signal(SIGINT,  process_signal);
104  signal(SIGTERM, process_signal);
105  signal(SIGHUP,  process_signal);
106
107  if (_ds_read_attribute(agent_config, "ServerPort"))
108    port = atoi(_ds_read_attribute(agent_config, "ServerPort"));
109
110  if (_ds_read_attribute(agent_config, "ServerQueueSize"))
111    queue = atoi(_ds_read_attribute(agent_config, "ServerQueueSize"));
112
113  if (_ds_read_attribute(agent_config, "ServerDomainSocketPath"))
114    domain = 1;
115
116  /* initialize */
117
118  FD_ZERO(&master);
119  FD_ZERO(&read_fds);
120
121  pthread_attr_init(&attr);
122  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
123
124  /* Bind (domain socket) */
125
126  if (domain) {
127    struct sockaddr_un saun;
128    char *address = _ds_read_attribute(agent_config, "ServerDomainSocketPath");
129    mode_t mask;
130    int len;
131
132    mask = umask (000);
133
134    listener = socket(AF_UNIX, SOCK_STREAM, 0);
135    if (listener == -1) {
136      LOG(LOG_CRIT, ERR_DAEMON_SOCKET, strerror(errno));
137      umask (mask);
138      return(EFAILURE);
139    }
140
141    memset(&saun, 0, sizeof(struct sockaddr_un));
142    saun.sun_family = AF_UNIX;
143    strcpy(saun.sun_path, address);
144
145    unlink(address);
146    len = sizeof(saun.sun_family) + strlen(saun.sun_path) + 1;
147
148    LOGDEBUG(INFO_DAEMON_DOMAINSOCK, address);
149 
150    if (bind(listener, (struct sockaddr *) &saun, len)<0) {
151      close(listener);
152      LOG(LOG_CRIT, INFO_DAEMON_DOMAINSOCK, address, strerror(errno));
153      umask (mask);
154      return EFAILURE;
155    }   
156
157    umask (mask);
158
159  /* Bind to a TCP socket */
160
161  } else {
162    listener = socket(AF_INET, SOCK_STREAM, 0);
163    if (listener == -1) {
164      LOG(LOG_CRIT, ERR_DAEMON_SOCKET, strerror(errno));
165      return(EFAILURE);
166    }
167
168    if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
169      close(listener);
170      LOG(LOG_CRIT, ERR_DAEMON_SOCKOPT, "SO_REUSEADDR", strerror(errno));
171      return(EFAILURE);
172    }
173
174    memset(&local_addr, 0, sizeof(struct sockaddr_in));
175    local_addr.sin_family = AF_INET;
176    local_addr.sin_port = htons(port);
177    if (_ds_read_attribute(agent_config, "ServerHost")) {
178      char *host = _ds_read_attribute(agent_config, "ServerHost");
179      local_addr.sin_addr.s_addr = inet_addr(host);
180      LOGDEBUG(INFO_DAEMON_BIND, host, port);
181    } else {
182      local_addr.sin_addr.s_addr = INADDR_ANY;
183      LOGDEBUG(INFO_DAEMON_BIND, "*", port);
184    }
185
186    if (bind(listener, (struct sockaddr *)&local_addr,
187             sizeof(struct sockaddr)) == -1)
188    {
189      close(listener);
190      LOG(LOG_CRIT, ERR_DAEMON_BIND, port, strerror(errno));
191      return(EFAILURE);
192    }
193  }
194
195  /* Listen */
196
197  if (listen(listener, queue) == -1) {
198    close(listener);
199    LOG(LOG_CRIT, ERR_DAEMON_LISTEN, strerror(errno));
200    return(EFAILURE);
201  }
202
203  FD_SET(listener, &master);
204  fdmax = listener;
205
206  /* Process new connections (until death or reload) */
207
208  for(;;) {
209    read_fds = master;
210    tv.tv_sec = 2;
211    tv.tv_usec = 0;
212
213    if (__daemon_run == 0) {
214      close(listener);
215
216      if (_ds_read_attribute(agent_config, "ServerDomainSocketPath"))
217        unlink (_ds_read_attribute(agent_config, "ServerDomainSocketPath"));
218     
219      return 0;
220    }
221
222    if (select(fdmax+1, &read_fds, NULL, NULL, &tv)>0) {
223
224      /* Process read-ready connections */
225
226      for(i=0;i<=fdmax;i++) {
227        if (FD_ISSET(i, &read_fds)) {
228
229          /* Accept new connections */
230
231          if (i == listener) {
232            int newfd;
233            int addrlen = sizeof(remote_addr);
234
235            if ((newfd = accept(listener,
236                                (struct sockaddr *)&remote_addr,
237                                (socklen_t *) &addrlen)) == -1)
238            {
239              LOG(LOG_WARNING, ERR_DAEMON_ACCEPT, strerror(errno));
240              continue;
241#ifdef DEBUG
242            } else if (!domain) {
243              char buff[32];
244              LOGDEBUG("connection id %d from %s.", newfd,
245#ifdef HAVE_INET_NTOA_R_2
246                       inet_ntoa_r(remote_addr.sin_addr, buff)
247#else
248                       inet_ntoa_r(remote_addr.sin_addr, buff, sizeof(buff))
249#endif
250                      );
251#endif
252            }
253            fcntl(newfd, F_SETFL, O_RDWR);
254            setsockopt(newfd,SOL_SOCKET,TCP_NODELAY,&yes,sizeof(int));
255
256            /*
257             * Since processing time varies, each new connection gets its own
258             * thread, so we create a new thread context and send it on its way
259             *
260             */
261
262            TTX = calloc(1, sizeof(THREAD_CTX));
263            if (TTX == NULL) {
264              LOG(LOG_CRIT, ERR_MEM_ALLOC);
265              close(newfd);
266              continue;
267            } else {
268              TTX->sockfd = newfd;
269              TTX->DTX = DTX;
270              memcpy(&TTX->remote_addr, &remote_addr, sizeof(remote_addr));
271
272              increment_thread_count();
273              if (pthread_create(&TTX->thread,
274                                 &attr, process_connection, (void *) TTX))
275              {
276                decrement_thread_count();
277                LOG(LOG_CRIT, ERR_DAEMON_THREAD, strerror(errno));
278                close(TTX->sockfd);
279                free(TTX);
280                continue;
281              }
282            }
283          } /* if i == listener */
284        } /* if FD_SET else */
285      } /* for(i.. */
286    }  /* if (select)... */
287  } /* for(;;) */
288
289  /* Shutdown - we should never get here, but who knows */
290
291  close(listener);
292  pthread_attr_destroy(&attr);
293  return 0;
294}
295
296/*
297 * process_connection(void *ptr)
298 *
299 * DESCRIPTION
300 *   process a connection after establishment
301 *
302 *  this function instantiates for each thread at the beginning of a connection
303 *  and is the hub for a connection's processing.
304 *
305 * INPUT ARGUMENTS
306 *      ptr    thread context (TTX)
307 *
308 * RETURN VALUES
309 *   returns NULL on success
310 *
311 */
312
313void *process_connection(void *ptr) {
314  char *server_ident = _ds_read_attribute(agent_config, "ServerIdent");
315  THREAD_CTX *TTX = (THREAD_CTX *) ptr;
316  AGENT_CTX *ATX = NULL;
317  char *input, *cmdline = NULL, *token, *ptrptr;
318  buffer *message = NULL;
319  char *parms=NULL, *p=NULL;
320  int i, locked = -1, invalid = 0;
321  int server_mode = SSM_DSPAM;
322  char *argv[64];
323  char buf[1024];
324  int tries = 0;
325  int argc = 0;
326  FILE *fd = 0;
327
328  if (_ds_read_attribute(agent_config, "ServerMode") &&
329      !strcasecmp(_ds_read_attribute(agent_config, "ServerMode"), "standard"))
330  {
331    server_mode = SSM_STANDARD;
332  }
333
334  if (_ds_read_attribute(agent_config, "ServerMode") &&
335      !strcasecmp(_ds_read_attribute(agent_config, "ServerMode"), "auto"))
336  {
337    server_mode = SSM_AUTO;
338  }
339
340  /* Initialize a file descriptor hook for dspam to use as stdout */
341
342  fd = fdopen(TTX->sockfd, "w");
343  if (!fd) {
344    close(TTX->sockfd);
345    goto CLOSE;
346  }
347  setbuf(fd, NULL);
348
349  TTX->packet_buffer = buffer_create(NULL);
350  if (TTX->packet_buffer == NULL)
351    goto CLOSE;
352
353  /*
354   *  Send greeting banner
355   *  in auto mode, we want to look like a regular LMTP server so we don't
356   *  cause any compatibility problems. in dspam mode, we can change this. 
357   */
358
359  snprintf(buf, sizeof(buf), "%d DSPAM %sLMTP %s %s",
360    LMTP_GREETING,
361    (server_mode == SSM_DSPAM) ? "D" : "",
362    VERSION,
363    (server_mode == SSM_DSPAM) ? "Authentication Required" : "Ready");
364  if (send_socket(TTX, buf)<=0)
365    goto CLOSE;
366
367  TTX->authenticated = 0;
368
369  /* LHLO */
370
371  input = daemon_expect(TTX, "LHLO");
372  if (input == NULL)
373    goto CLOSE;
374  if (server_mode == SSM_AUTO && input[4]) {
375    char buff[128];
376
377    /*
378     *  Auto-detect the server mode based on whether or not the ident is
379     *  assigned a password in dspam.conf
380     */
381
382    snprintf(buff, sizeof(buff), "ServerPass.%s", input + 5);
383    chomp(buff);
384    if (_ds_read_attribute(agent_config, buff))
385      server_mode = SSM_DSPAM;
386    else
387      server_mode = SSM_STANDARD;
388  }
389  free(input);
390
391  /* Advertise extensions */
392
393  if (daemon_extension(TTX, (server_ident) ? server_ident :
394      "localhost.localdomain")<=0)
395    goto CLOSE;
396
397  if (daemon_extension(TTX, "PIPELINING")<=0)
398    goto CLOSE;
399
400  if (daemon_extension(TTX, "ENHANCEDSTATUSCODES")<=0)
401    goto CLOSE;
402
403  if (server_mode == SSM_DSPAM)
404    if (daemon_extension(TTX, "DSPAMPROCESSMODE")<=0)
405      goto CLOSE;
406
407  if (daemon_extension(TTX, "8BITMIME")<=0)
408    goto CLOSE;
409
410  if (daemon_reply(TTX, LMTP_OK, "", "SIZE")<=0)
411    goto CLOSE;
412
413  /* Main protocol loop */
414
415  while(1) {
416    char processmode[256];
417    parms = NULL;
418
419    /* Configure a new agent context for each pass */
420
421    ATX = calloc(1, sizeof(AGENT_CTX));
422    if (ATX == NULL) {
423      LOG(LOG_CRIT, ERR_MEM_ALLOC);
424      daemon_reply(TTX, LMTP_TEMP_FAIL, "4.3.0", ERR_MEM_ALLOC);
425      goto CLOSE;
426    }
427
428    if (initialize_atx(ATX)) {
429      LOG(LOG_ERR, ERR_AGENT_INIT_ATX);
430      daemon_reply(TTX, LMTP_BAD_CMD, "5.3.0", ERR_AGENT_INIT_ATX);
431      goto CLOSE;
432    }
433
434    /* MAIL FROM (and authentication, if SSM_DSPAM) */
435
436    processmode[0] = 0;
437    while(!TTX->authenticated) {
438      input = daemon_expect(TTX, "MAIL FROM");
439
440      if (RSET(input))
441        goto RSET;
442
443      if (input == NULL)
444        goto CLOSE;
445      else {
446        char *pass, *ident;
447        chomp(input);
448
449        if (server_mode == SSM_STANDARD) {
450          TTX->authenticated = 1;
451
452          ATX->mailfrom[0] = 0;
453          _ds_extract_address(ATX->mailfrom, input, sizeof(ATX->mailfrom));
454
455          if (daemon_reply(TTX, LMTP_OK, "2.1.0", "OK")<=0) {
456            free(input);
457            goto CLOSE;
458          }
459        } else {
460          char id[256];
461          pass = ident = NULL;
462          id[0] = 0;
463          if (!_ds_extract_address(id, input, sizeof(id))) {
464            pass = strtok_r(id, "@", &ptrptr);
465            ident = strtok_r(NULL, "@", &ptrptr);
466          }
467
468          if (pass && ident) {
469            char *serverpass;
470            char *ptr, *ptr2, *ptr3;
471 
472            snprintf(buf, sizeof(buf), "ServerPass.%s", ident);
473            serverpass = _ds_read_attribute(agent_config, buf);
474 
475            snprintf(buf, sizeof(buf), "ServerPass.%s", ident);
476            if (serverpass && !strcmp(pass, serverpass)) {
477              TTX->authenticated = 1;
478
479              /* Parse PROCESSMODE service tag */
480 
481              ptr = strstr(input, "DSPAMPROCESSMODE=\"");
482              if (ptr) {
483                char *mode;
484                int i;
485                ptr2 = strchr(ptr, '"')+1;
486                mode = ptr2;
487                while((ptr3 = strstr(ptr2, "\\\"")))
488                  ptr2 = ptr3+2;
489                ptr3 = strchr(ptr2+2, '"');
490                if (ptr3)
491                  ptr3[0] = 0;
492                strlcpy(processmode, mode, sizeof(processmode));
493               
494                ptr = processmode;
495                for(i=0;ptr[i];i++) {
496                  if (ptr[i] == '\\' && ptr[i+1] == '"') {
497                    strcpy(ptr+i, ptr+i+1);
498                  }
499                }
500                LOGDEBUG("process mode: '%s'", processmode);
501              }
502               
503              if (daemon_reply(TTX, LMTP_OK, "2.1.0", "OK")<=0) {
504                free(input);
505                goto CLOSE;
506              }
507            }
508          }
509        }
510
511        free(input);
512        if (!TTX->authenticated) {
513          LOGDEBUG("fd %d authentication failure.", TTX->sockfd);
514
515          if (daemon_reply(TTX, LMTP_AUTH_ERROR, "5.1.0",
516              "Authentication Required")<=0)
517          {
518            free(input);
519            goto CLOSE;
520          }
521         
522          tries++;
523          if (tries>=3) {
524            struct timeval tv;
525            tv.tv_sec = 5;
526            tv.tv_usec = 0;
527            select(0, NULL, NULL, NULL, &tv);
528            goto CLOSE;
529          }
530        }
531      }
532    }
533 
534    /* MAIL FROM response */
535
536    snprintf(buf, sizeof(buf), "%d OK", LMTP_OK);
537
538    argc = 1;
539    argv[0] = "dspam";
540    argv[1] = 0;
541
542    /* Load open-LMTP configuration parameters */
543
544    if (server_mode == SSM_STANDARD) {
545      parms = _ds_read_attribute(agent_config, "ServerParameters");
546      if (parms) {
547        p = strdup(parms);
548        if (p) {
549          token = strtok_r(p, " ", &ptrptr);
550          while(token != NULL && argc<63) {
551            argv[argc] = token;
552            argc++;
553            argv[argc] = 0;
554            token = strtok_r(NULL, " ", &ptrptr);
555          }
556        }
557      }
558    }
559
560    /* RCPT TO */
561
562    while(ATX->users->items == 0 || invalid) {
563      free(cmdline);
564      cmdline = daemon_getline(TTX, 300);
565 
566      while(cmdline &&
567            (!strncasecmp(cmdline, "RCPT TO:", 8) ||
568             !strncasecmp(cmdline, "RSET", 4)))
569      {
570        char username[256];
571        char *at = NULL;
572
573        if (!strncasecmp(cmdline, "RSET", 4)) {
574          snprintf(buf, sizeof(buf), "%d OK", LMTP_OK);
575          if (send_socket(TTX, buf)<=0)
576            goto CLOSE;
577          goto RSET;
578        }
579
580        if (_ds_extract_address(username, cmdline, sizeof(username)) ||
581            username[0] == 0 || username[0] == '-' || username[0] == '@')
582        {
583          if ((server_mode == SSM_DSPAM) || (server_mode == SSM_STANDARD && _ds_validate_address(username) == 0)) {
584            daemon_reply(TTX, LMTP_BAD_CMD, "5.1.2", ERR_LMTP_BAD_RCPT);
585            goto GETCMD;
586          }
587        }
588
589        if (_ds_match_attribute(agent_config, "Broken", "case"))
590          lc(username, username);
591
592        /* Chop of @.* from the recipient */
593        if (_ds_match_attribute(agent_config, "StripRcptDomain", "on")) {
594          at = strchr(username, '@');
595          if (at != NULL)
596            *at = '\0';
597        }
598
599        if (server_mode == SSM_DSPAM) {
600          nt_add(ATX->users, username);
601        }
602        else {
603          if (!parms || !strstr(parms, "--user "))
604            nt_add(ATX->users, username);
605
606          if (!ATX->recipients) {
607            ATX->recipients = nt_create(NT_CHAR);
608            if (ATX->recipients == NULL) {
609              LOG(LOG_CRIT, ERR_MEM_ALLOC);
610              goto CLOSE;
611            }
612          }
613          if (at != NULL)
614            *at = '@'; /* always add complete address (user@domain) to recipient list */
615          nt_add(ATX->recipients, username);
616        }
617
618        if (daemon_reply(TTX, LMTP_OK, "2.1.5", "OK")<=0)
619          goto CLOSE;
620
621GETCMD:
622        free(cmdline);
623        cmdline = daemon_getline(TTX, 300);
624      }
625
626      if (cmdline == NULL)
627        goto CLOSE;
628
629      if (!strncasecmp(cmdline, "RSET", 4)) {
630        snprintf(buf, sizeof(buf), "%d OK", LMTP_OK);
631        if (send_socket(TTX, buf)<=0)
632          goto CLOSE;
633        goto RSET;
634      }
635
636      if (!strncasecmp(cmdline, "quit", 4)) {
637        daemon_reply(TTX, LMTP_OK, "2.0.0", "OK");
638        goto CLOSE;
639      }
640
641      /* Parse DSPAMPROCESSMODE input and set up process arguments */
642 
643      if (server_mode == SSM_DSPAM && processmode[0] != 0) {
644        token = strtok_r(processmode, " ", &ptrptr);
645        while(token != NULL && argc<63) {
646          argv[argc] = token;
647          argc++;
648          argv[argc] = 0;
649          token = strtok_r(NULL, " ", &ptrptr);
650        }
651      }
652
653      invalid = 0;
654      if (process_arguments(ATX, argc, argv) || apply_defaults(ATX))
655      {
656        LOG(LOG_ERR, ERR_AGENT_INIT_ATX);
657        daemon_reply(TTX, LMTP_NO_RCPT, "5.1.0", ERR_AGENT_INIT_ATX);
658        invalid = 1;
659      } else if (ATX->users->items == 0) {
660        daemon_reply(TTX, LMTP_NO_RCPT, "5.1.1", ERR_AGENT_USER_UNDEFINED);
661      }
662    }
663
664    ATX->sockfd = fd;
665    ATX->sockfd_output = 0;
666
667    /* Something's terribly misconfigured */
668
669    if (check_configuration(ATX)) {
670      LOG(LOG_ERR, ERR_AGENT_MISCONFIGURED);
671      daemon_reply(TTX, LMTP_BAD_CMD, "5.3.5", ERR_AGENT_MISCONFIGURED);
672      goto CLOSE;
673    }
674
675    /* DATA */
676
677    if (cmdline != NULL) {
678      if (strncasecmp(cmdline, "DATA", 4)) {
679        if (daemon_reply(TTX, LMTP_BAD_CMD, "5.0.0", "Need DATA Here")<0)
680          goto CLOSE;
681        input = daemon_expect(TTX, "DATA");
682        if (input == NULL)
683          goto CLOSE;
684        if (RSET(input))
685          goto RSET;
686      }
687    }
688
689    if (daemon_reply(TTX, LMTP_DATA, "", INFO_LMTP_DATA)<=0)
690      goto CLOSE;
691 
692    /*
693     *  Read in the message from a DATA. I personally like to just hang up on
694     *  a client stupid enough to pass in a NULL message for DATA, but you're
695     *  welcome to do whatever you want.
696     */
697
698    message = read_sock(TTX, ATX);
699    if (message == NULL || message->data == NULL || message->used == 0) {
700      daemon_reply(TTX, LMTP_FAILURE, "5.2.0", ERR_LMTP_MSG_NULL);
701      goto CLOSE;
702    }
703
704    /*
705     *  Lock a database handle. We currently use the modulus of the socket
706     *  id against the number of database connections in the cache. This
707     *  seems to work rather well, as we would need to lock up the entire
708     *  cache to wrap back around. And if we do wrap back around, that means
709     *  we're busy enough to justify spinning on the current lock (vs. seeking
710     *  out a free handle, which there likely are none).
711     */
712
713    i = (TTX->sockfd % TTX->DTX->connection_cache);
714    LOGDEBUG("using database handle id %d", i);
715    if (TTX->DTX->flags & DRF_RWLOCK) {
716      if (ATX->operating_mode == DSM_CLASSIFY ||
717          ATX->training_mode == DST_NOTRAIN   ||
718          (ATX->training_mode == DST_TOE && ATX->classification == DSR_NONE))
719      {
720        pthread_rwlock_rdlock(&TTX->DTX->connections[i]->rwlock);
721      } else {
722        pthread_rwlock_wrlock(&TTX->DTX->connections[i]->rwlock);
723      }
724    } else {
725      pthread_mutex_lock(&TTX->DTX->connections[i]->lock);
726    }
727    LOGDEBUG("handle locked");
728    ATX->dbh = TTX->DTX->connections[i]->dbh;
729    locked = i;
730
731    /* Process the message by tying back into the agent functions */
732
733    ATX->results = nt_create(NT_PTR);
734    if (ATX->results == NULL) {
735      LOG(LOG_CRIT, ERR_MEM_ALLOC);
736      goto CLOSE;
737    }
738    /* FIXME: this sends the message back to the client, but doesnt do the appropiate dot stuffing. */
739    process_users(ATX, message);
740 
741    /*
742     *  Unlock the database handle as soon as we're done. We also need to
743     *  refresh our handle index with a new handle if for some reason we
744     *  had to re-establish a dewedged connection.
745     */
746
747    if (TTX->DTX->connections[locked]->dbh != ATX->dbh)
748      TTX->DTX->connections[locked]->dbh = ATX->dbh;
749
750    if (TTX->DTX->flags & DRF_RWLOCK) {
751      pthread_rwlock_unlock(&TTX->DTX->connections[locked]->rwlock);
752    } else {
753      pthread_mutex_unlock(&TTX->DTX->connections[locked]->lock);
754    }
755    locked = -1;
756
757    /* Send a terminating '.' if --stdout in 'dspam' mode */
758
759    if (ATX->sockfd_output) {
760      if (send_socket(TTX, ".")<=0)
761        goto CLOSE;
762
763    /* Otherwise, produce standard delivery results */
764
765    } else {
766      struct nt_node *node_nt, *node_res = NULL;
767      struct nt_c c_nt;
768      if (ATX->recipients)
769        node_nt = c_nt_first(ATX->recipients, &c_nt);
770      else
771        node_nt = c_nt_first(ATX->users, &c_nt);
772
773      if (ATX->results)
774        node_res = ATX->results->first;
775
776      while(node_res && node_nt != NULL) {
777        agent_result_t result = (agent_result_t) node_res->ptr;
778
779        if (result != NULL && result->exitcode == ERC_SUCCESS)
780        {
781          if (server_mode == SSM_DSPAM) {
782            snprintf(buf, sizeof(buf),
783                    "%d 2.6.0 <%s> Message accepted for delivery: %s",
784                    LMTP_OK, (char *) node_nt->ptr,
785              (result->classification == DSR_ISSPAM) ? "SPAM" : "INNOCENT");
786          } else {
787            snprintf(buf, sizeof(buf),
788                    "%d 2.6.0 <%s> Message accepted for delivery",
789                    LMTP_OK, (char *) node_nt->ptr);
790          }
791        }
792        else
793        {
794          if (result != NULL && result->exitcode == ERC_PERMANENT_DELIVERY) {
795            snprintf(buf, sizeof(buf), "%d 5.3.0 <%s> %s",
796                 LMTP_FAILURE, (char *) node_nt->ptr,
797                 (result->text[0]) ? result->text : "Permanent error occured");
798          } else {
799            if (result != NULL && result->text[0]) {
800              snprintf(buf, sizeof(buf),
801                       "%d 4.3.0 <%s> %s",
802                       LMTP_TEMP_FAIL, (char *) node_nt->ptr, result->text);
803            } else {
804              snprintf(buf, sizeof(buf),
805                       "%d 4.3.0 <%s> Error occured during %s",
806                       LMTP_TEMP_FAIL, (char *) node_nt->ptr,
807                (result != NULL && result->exitcode == ERC_DELIVERY) ? "delivery" : "processing");
808            }
809          }
810        }
811
812        if (send_socket(TTX, buf)<=0)
813          goto CLOSE;
814        if (ATX->recipients)
815          node_nt = c_nt_next(ATX->recipients, &c_nt);
816        else
817          node_nt = c_nt_next(ATX->users, &c_nt);
818        if (node_res)
819          node_res = node_res->next;
820      }
821    }
822
823    /* Cleanup and get ready for another message */
824
825RSET:
826    fflush(fd);
827
828    buffer_destroy(message);
829    message = NULL;
830    if (ATX != NULL) {
831      nt_destroy(ATX->users);
832      nt_destroy(ATX->recipients);
833      nt_destroy(ATX->results);
834      free(ATX);
835      ATX = NULL;
836      free(cmdline);
837      cmdline = NULL;
838      TTX->authenticated = 0;
839      /* argc = 0; */
840    }
841
842    free(p);
843    p = NULL;
844
845  } /* while(1) */
846
847  /* Close connection and return */
848
849CLOSE:
850  if (locked>=0)
851    pthread_mutex_unlock(&TTX->DTX->connections[locked]->lock);
852  if (fd)
853    fclose(fd);
854  buffer_destroy(TTX->packet_buffer);
855  if (message)
856    buffer_destroy(message);
857  if (ATX != NULL) {
858    nt_destroy(ATX->users);
859    nt_destroy(ATX->recipients);
860    nt_destroy(ATX->results);
861  }
862  free(ATX);
863  free(cmdline);
864  free(TTX);
865  decrement_thread_count();
866  pthread_exit(0);
867  return 0;
868}
869
870/*
871 * read_sock(THREAD_CTX *TTX, AGENT_CTX *ATX)
872 *
873 * DESCRIPTION
874 *   read in a message via socket and perform standard parseto services
875 *   this is a daemonized version of read_stdin, adding in timeouts and
876 *   termination via '.'
877 *
878 * INPUT ARGUMENTS
879 *      TTX    thread context
880 *      ATX    agent context
881 *
882 * RETURN VALUES
883 *   pointer to allocated buffer containing the message
884 */
885
886buffer * read_sock(THREAD_CTX *TTX, AGENT_CTX *ATX) {
887  buffer *message;
888  int body = 0, line = 1;
889  char *buf;
890  int strip = _ds_match_attribute(agent_config, "Broken", "lineStripping");
891  int parseto = _ds_match_attribute(agent_config, "ParseToHeaders", "on");
892
893  message = buffer_create(NULL);
894  if (message == NULL) {
895    LOG(LOG_CRIT, ERR_MEM_ALLOC);
896    return NULL;
897  }
898
899  if (_ds_match_attribute(agent_config, "DataSource", "document")) {
900    buffer_cat(message, ".\n\n");
901    body = 1;
902  }
903
904  while ((buf = daemon_getline(TTX, 300))!=NULL) {
905    chomp(buf);
906
907    if (!strcmp(buf, ".")) {
908      free(buf);
909      return message;
910    }
911
912    if (strip) {
913      size_t len = strlen(buf);
914 
915      while (len>1 && buf[len-2]=='\n') {
916        buf[len-2] = buf[len-1];
917        buf[len-1] = 0;
918        len--;
919      }
920    }
921 
922    if (line > 1 || strncmp (buf, "From QUARANTINE", 15))
923    {
924      if (parseto) {
925        if (buf[0] == 0)
926          body = 1;
927        if (!body && !strncasecmp(buf, "To: ", 4))
928          process_parseto(ATX, buf);
929      }
930
931      /* remove dot stuffing, if needed */
932      /* FIXME: we SHOULD remove dot stuffing, but because i couldn't find a simple way to re-add dot stuffing in the return message. However this currently leads to truncated messages which is very bad.
933         As a workaround i disabled the dot-stuffing routing below, resulting in dspam seeing and trarining the extra dots in some messages.
934         Edwin Eefting - edwin@datux.nl
935         
936      if((buf[0] && buf[0]=='.') && (buf[1] && buf[1]=='.')) {
937        size_t i, len = strlen(buf);
938        for(i=0;i<len;i++){
939          buf[i]=buf[i+1];
940        }
941        buf[len-1]=0;
942      }
943      */
944
945      if (buffer_cat (message, buf) || buffer_cat(message, "\n"))
946      {
947        LOG (LOG_CRIT, ERR_MEM_ALLOC);
948        goto bail;
949      }
950    }
951
952    /* Use the original user id if we are reversing a false positive */
953    if (ATX->source         == DSS_ERROR      &&
954        ATX->classification == DSR_ISINNOCENT &&
955        ATX->operating_mode == DSM_PROCESS    &&
956        !strncasecmp (buf, "X-DSPAM-User: ", 14))
957    {
958      char user[MAX_USERNAME_LENGTH];
959      strlcpy (user, buf + 14, sizeof (user));
960      chomp (user);
961      nt_destroy (ATX->users);
962      ATX->users = nt_create (NT_CHAR);
963      if (ATX->users == NULL)
964      {
965        LOG(LOG_CRIT, ERR_MEM_ALLOC);
966        goto bail;
967      }
968      nt_add (ATX->users, user);
969    }
970
971    free(buf);
972    line++;
973  }
974
975  return NULL;
976
977bail:
978  buffer_destroy(message);
979  return NULL;
980}
981
982/*
983 * daemon_expect(THREAD_CTX *TTX, const char *command) {
984 *
985 * DESCRIPTION
986 *   wait for the right command ot be issued by the client
987 *   if any other command is issued, give them an error
988 *
989 * INPUT ARGUMENTS
990 *      TTX        thread context
991 *      command    command to wait for
992 *
993 * RETURN VALUES
994 *   pointer to allocated character array containing the input line
995 */
996
997char *daemon_expect(THREAD_CTX *TTX, const char *command) {
998  char buf[128];
999  char *cmd;
1000
1001  cmd = daemon_getline(TTX, 300);
1002  if (cmd == NULL)
1003    return NULL;
1004
1005  while(strncasecmp(cmd, command, strlen(command))) {
1006    if (!strncasecmp(cmd, "QUIT", 4)) {
1007      free(cmd);
1008      daemon_reply(TTX, LMTP_QUIT, "2.0.0", "OK");
1009      return NULL;
1010    } else if (!strncasecmp(cmd, "RSET", 4)) {
1011      snprintf(buf, sizeof(buf), "%d OK", LMTP_OK);
1012      if (send_socket(TTX, buf)<=0)
1013        return NULL;
1014      free(cmd);
1015      if (!strncasecmp(command, "LHLO", 4)) {
1016        cmd = daemon_getline(TTX, 300);
1017        if (cmd == NULL)
1018          return NULL;
1019      } else {
1020        return "RSET";
1021      }
1022    } else {
1023      snprintf(buf, sizeof(buf), "%d 5.0.0 Need %s here.", LMTP_BAD_CMD, command);
1024      if (send_socket(TTX, buf)<=0)
1025        return NULL;
1026      free(cmd);
1027      cmd = daemon_getline(TTX, 300);
1028      if (cmd == NULL)
1029        return NULL;
1030    }
1031  }
1032  return cmd;
1033}
1034
1035/*
1036 * daemon_reply(THREAD_CTX *TTX, int reply, const char *ecode, const char *text)
1037 *
1038 * DESCRIPTION
1039 *   send a formatted response to the client
1040 *
1041 * INPUT ARGUMENTS
1042 *      TTX      thread context
1043 *      reply    numeric response code
1044 *      ecode    enhanced status code
1045 *      text     info text to send
1046 *
1047 * RETURN VALUES
1048 *    returns 0 on success
1049 */
1050
1051int daemon_reply(
1052  THREAD_CTX *TTX,
1053  int reply,
1054  const char *ecode,
1055  const char *text)
1056{
1057  char buf[128];
1058  snprintf(buf, sizeof(buf), "%d %s%s%s",
1059           reply, ecode, (ecode[0]) ? " " : "", text);
1060  return send_socket(TTX, buf);
1061}
1062
1063
1064/*
1065 * daemon_extension(THREAD_CTX *TTX, const char *extension)
1066 *
1067 * DESCRIPTION
1068 *   advertise a supported LMTP extension
1069 *
1070 * INPUT ARGUMENTS
1071 *       TTX          thread context
1072 *       extension    extension name
1073 *
1074 * RETURN VALUES
1075 *   returns 0 on success
1076 */
1077
1078int daemon_extension(THREAD_CTX *TTX, const char *extension) {
1079  char buf[128];
1080  snprintf(buf, sizeof(buf), "%d-%s", LMTP_OK, extension);
1081  return send_socket(TTX, buf);
1082}
1083
1084/*
1085 * process_signal(int sig)
1086 *
1087 *  DESCRIPTION
1088 *    terminate daemon or perform a reload (signal handler)
1089 *
1090 *  INPUT ARGUMENTS
1091 *      sig    signal code
1092 *
1093 */
1094
1095void process_signal(int sig) {
1096  __daemon_run = 0;
1097  if (sig == SIGHUP) {
1098    LOG(LOG_INFO, INFO_DAEMON_RELOAD);
1099    __hup = 1;
1100  } else {
1101    LOG(LOG_WARNING, ERR_DAEMON_TERMINATE, sig);
1102    __hup = 0;
1103  }
1104
1105  return;
1106}
1107
1108/*
1109 *  daemon_getline(THREAD_CTX *TTX, int timeout)
1110 *
1111 * DESCRIPTION
1112 *   retrieves a full line of text from a socket
1113 *
1114 * INPUT ARGUMENTS
1115 *      TTX        thread context
1116 *      timeout    timeout to enforce in waiting for complete line
1117 *
1118 * RETURN VALUES
1119 *   pointer to allocated character array containing line of input
1120 */
1121
1122char *daemon_getline(THREAD_CTX *TTX, int timeout) {
1123  struct timeval tv;
1124  char *p, *q, *pop;
1125  char buf[1024];
1126  int total_wait = 0;
1127  long recv_len;
1128  fd_set fds;
1129  int i;
1130
1131  pop = pop_buffer(TTX);
1132  while(!pop && total_wait<timeout) {
1133    if (__daemon_run == 0)
1134      return NULL;
1135    total_wait++;
1136    tv.tv_sec = 1;
1137    tv.tv_usec = 0;
1138    FD_ZERO(&fds);
1139    FD_SET(TTX->sockfd, &fds);
1140    i = select(TTX->sockfd+1, &fds, NULL, NULL, &tv);
1141    if (i<=0)
1142      continue;
1143
1144    recv_len = recv(TTX->sockfd, buf, sizeof(buf)-1, 0);
1145    buf[recv_len] = 0;
1146    if (recv_len == 0)
1147      return NULL;
1148    for(p=q=buf,i=0;i<recv_len;i++) {
1149      if (*q) {
1150        *p = *q;
1151        p++;
1152      }
1153      q++;
1154    }
1155    *p = 0;
1156    buffer_cat(TTX->packet_buffer, buf);
1157    pop = pop_buffer(TTX);
1158  }
1159
1160#ifdef VERBOSE
1161  LOGDEBUG("SRECV: %s", pop);
1162#endif
1163
1164  return pop;
1165}
1166
1167/*
1168 * {increment,decrement}_thread_count
1169 *
1170 * DESCRIPTION
1171 *   keep track of running thread count
1172 *
1173 *  in order to reload or terminate, all threads must complete and exit.
1174 *  these functions are called whenever a thread spawns or ends and bumps the
1175 *  thread counter in the appropriate direction
1176 *
1177 * RETURN VALUES
1178 *   pointer to allocated character array containing line of input
1179 */
1180
1181void increment_thread_count(void) {
1182  pthread_mutex_lock(&__lock);
1183  __num_threads++;
1184  pthread_mutex_unlock(&__lock);
1185}
1186
1187void decrement_thread_count(void) {
1188  pthread_mutex_lock(&__lock);
1189  __num_threads--;
1190  pthread_mutex_unlock(&__lock);
1191}
1192
1193#endif
Note: See TracBrowser for help on using the repository browser.