source: npl/mailserver/dspam/dspam-3.10.2/src/client.c @ d36701a

gcc484perl-5.22
Last change on this file since d36701a 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: 22.8 KB
Line 
1/* $Id: client.c,v 1.691 2011/06/28 00:13:48 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 * client.c - client-based functions (for operating in client/daemon mode)c
24 *
25 * DESCRIPTION
26 *   Client-based functions are called when --client is specified on the
27 *   commandline or by dspamc (where --client is inferred). The client
28 *   functions connect to a DSPAM server for processing (rather than the
29 *   usual behavior which is to process the message itself). Client
30 *   functions are also used when delivering via LMTP or SMTP.
31 */
32
33#ifdef HAVE_CONFIG_H
34#include <auto-config.h>
35#endif
36
37#ifdef DAEMON
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/un.h>
47#include <sys/socket.h>
48#include <netdb.h>
49#include <netinet/in.h>
50#include <netinet/tcp.h>
51#include <arpa/inet.h>
52#ifndef _WIN32
53#include <unistd.h>
54#include <pwd.h>
55#endif
56#include <stdio.h>
57#include <stdlib.h>
58#include <sysexits.h>
59
60#include "client.h"
61#include "dspam.h"
62#include "config.h"
63#include "util.h"
64#include "language.h"
65#include "buffer.h"
66
67/*
68 * client_process(AGENT_CTX *, buffer *)
69 *
70 * DESCRIPTION
71 *   connect to a dspam daemon socket and attempt to process a message
72 *   this function is called by the dspam agent when --client is specified
73 *
74 * INPUT ARGUMENTS
75 *      ATX             agent context
76 *      message         message to be processed
77 *
78 * RETURN VALUES
79 *   returns 0 on success
80*/
81
82int client_process(AGENT_CTX *ATX, buffer *message) {
83  char buf[1024], err[256];
84  struct nt_node *node_nt;
85  struct nt_c c_nt;
86  int exitcode = 0, msglen;
87  THREAD_CTX TTX;
88  int i;
89
90  TTX.sockfd = client_connect(ATX, 0);
91  if (TTX.sockfd <0) {
92    LOG(LOG_WARNING, ERR_CLIENT_CONNECT);
93    STATUS(ERR_CLIENT_CONNECT);
94    return TTX.sockfd;
95  }
96
97  TTX.packet_buffer = buffer_create(NULL);
98  if (TTX.packet_buffer == NULL)
99    goto BAIL;
100
101  /* LHLO / MAIL FROM - Authenticate on the server */
102
103  if (client_authenticate(&TTX, ATX->client_args)<0) {
104    LOG(LOG_WARNING, ERR_CLIENT_AUTH_FAILED);
105    STATUS(ERR_CLIENT_AUTH_FAILED);
106    goto QUIT;
107  }
108
109  /* RCPT TO - Send recipient information */
110
111  strcpy(buf, "RCPT TO: ");
112  node_nt = c_nt_first(ATX->users, &c_nt);
113  while(node_nt != NULL) {
114    const char *ptr = (const char *) node_nt->ptr;
115    snprintf(buf, sizeof(buf), "RCPT TO: <%s>", ptr);
116    if (send_socket(&TTX, buf)<=0) {
117      STATUS(ERR_CLIENT_SEND_FAILED);
118      goto BAIL;
119    }
120
121    if (client_getcode(&TTX, err, sizeof(err))!=LMTP_OK) {
122      STATUS("%s", err);
123      goto QUIT;
124    }
125
126    node_nt = c_nt_next(ATX->users, &c_nt);
127  }
128
129  /* DATA - Send message */
130
131  if (send_socket(&TTX, "DATA")<=0)
132    goto BAIL;
133
134  if (client_getcode(&TTX, err, sizeof(err))!=LMTP_DATA) {
135    STATUS("%s", err);
136    goto QUIT;
137  }
138
139  i = 0;
140  msglen = strlen(message->data);
141  while(i<msglen) {
142    int r;
143    int t;
144    int buflen;
145
146    /*
147     * fill buf with partial msg, replacing \n with \r\n
148     * and do dot stuffing, if needed.
149     */
150    buflen = 0;
151    while ((size_t)buflen < (sizeof(buf) - 4) && i < msglen) {
152      if (i > 0) {
153        if (message->data[i] == '\n') {
154          /* only replace \n and not \r\n */
155          if (message->data[i - 1] != '\r') {
156            buf[buflen] = '\r';
157            buflen++;
158          }
159          /* take care of dot stuffing \n */
160          if (message->data[i + 1] && message->data[i + 1] == '.') {
161            buf[buflen] = '\n';
162            buflen++;
163            buf[buflen] = '.';
164            buflen++;
165            buf[buflen] = '.';
166            buflen++;
167            i += 2;
168            continue;
169          }
170        }
171      }
172
173      buf[buflen] = message->data[i];
174      buflen++;
175      i++;
176    }
177
178    /* send buf */
179    t = 0;
180    while (t < buflen) {
181      r = send(TTX.sockfd, buf+t, buflen - t, 0);
182      if (r <= 0) {
183        LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
184        STATUS(ERR_CLIENT_SEND_FAILED);
185        goto BAIL;
186      }
187      t += r;
188    }
189  }
190
191  if (message->data[msglen-1]!= '\n') {
192    if (send_socket(&TTX, "")<=0) {
193     STATUS(ERR_CLIENT_SEND_FAILED);
194     goto BAIL;
195    }
196  }
197
198  if (send_socket(&TTX, ".")<=0) {
199    STATUS(ERR_CLIENT_SEND_FAILED);
200    goto BAIL;
201  }
202
203  /* Server response */
204
205  if (ATX->flags & DAF_STDOUT || ATX->flags & DAF_SUMMARY ||
206      ATX->operating_mode == DSM_CLASSIFY)
207   {
208    char *line = NULL;
209    int head = !(ATX->flags & DAF_STDOUT);
210
211    if (ATX->flags & DAF_SUMMARY)
212      head = 1;
213
214    line = client_getline(&TTX, 300);
215
216    while(line != NULL && strcmp(line, ".")) {
217      chomp(line);
218      if (!head) {
219        head = 1;
220        if (!strncmp(line, "250 ", 4)) {
221          free(line);
222          goto QUIT;
223        }
224
225        if (!strcmp(line, "X-Daemon-Classification: SPAM") &&
226            _ds_match_attribute(agent_config, "Broken", "returnCodes"))
227        {
228          exitcode = 99;
229        }
230      } else {
231        /* remove dot stuffing, if needed */
232        if((line[0] && line[0]=='.') && (line[1] && line[1]=='.')) {
233          size_t i, len = strlen(line);
234          for(i=0;i<len;i++){
235            line[i]=line[i+1];
236          }
237          line[len-1]=0;
238        }
239        printf("%s\n", line);
240      }
241      free(line);
242      line = client_getline(&TTX, 300);
243      if (line) chomp(line);
244    }
245    free(line);
246  } else {
247    for(i=0;i<ATX->users->items;i++) {
248      char *input = client_getline(&TTX, 300);
249      char *x;
250      int code = 500;
251
252      if (!input) {
253        goto BAIL;
254      }
255      x = strtok(input, " ");
256      if (x) {
257        code = atoi(x);
258        if (code != LMTP_OK) {
259          if (exitcode > 0)
260            exitcode = 0;
261          exitcode--;
262        } else {
263          if (_ds_match_attribute(agent_config, "Broken", "returnCodes")) {
264            x = strtok(NULL, ":");
265            if (x)
266              x = strtok(NULL, ":");
267            if (x && strstr(x, "SPAM") && exitcode == 0)
268              exitcode = 99;
269          }
270        }
271      }
272    }
273  }
274
275  send_socket(&TTX, "QUIT");
276  client_getcode(&TTX, err, sizeof(err));
277  close(TTX.sockfd);
278  buffer_destroy(TTX.packet_buffer);
279  return exitcode;
280
281QUIT:
282  send_socket(&TTX, "QUIT");
283  client_getcode(&TTX, err, sizeof(err));
284
285BAIL:
286  exitcode = EFAILURE;
287  buffer_destroy(TTX.packet_buffer);
288  close(TTX.sockfd);
289  return exitcode;
290}
291
292/*
293 * client_connect(AGENT_CTX ATX, int flags)
294 *
295 * DESCRIPTION
296 *   establish a connection to a server
297 *
298 * INPUT ARGUMENTS
299 *      ATX             agent context
300 *      flags           connection flags
301 *
302 * FLAGS
303 *    CCF_PROCESS     Use ClientHost as destination
304 *    CCF_DELIVERY    Use DeliveryHost as destination
305 *
306 *  RETURN VALUES
307 *    returns 0 on success
308 */
309
310int client_connect(AGENT_CTX *ATX, int flags) {
311  struct sockaddr_in addr;
312  struct sockaddr_un saun;
313  int sockfd;
314  int yes = 1;
315  int port = 24;
316  int domain = 0;
317  int addr_len;
318  char *host;
319
320  if (flags & CCF_DELIVERY) {
321    host = _ds_read_attribute(agent_config, "DeliveryHost");
322
323    if (_ds_read_attribute(agent_config, "DeliveryPort"))
324      port = atoi(_ds_read_attribute(agent_config, "DeliveryPort"));
325
326    if (ATX->recipient && ATX->recipient[0]) {
327      char *domain = strchr(ATX->recipient, '@');
328      if (domain) {
329        char key[128];
330        char lcdomain[strlen(ATX->recipient)];
331        lc(lcdomain, domain+1);
332        snprintf(key, sizeof(key), "DeliveryHost.%s", lcdomain);
333        if (_ds_read_attribute(agent_config, key))
334          host = _ds_read_attribute(agent_config, key);
335        snprintf(key, sizeof(key), "DeliveryPort.%s", lcdomain);
336        if (_ds_read_attribute(agent_config, key))
337          port = atoi(_ds_read_attribute(agent_config, key));
338      }
339    }
340
341    if (host && host[0] == '/')
342      domain = 1;
343
344  } else {
345    host = _ds_read_attribute(agent_config, "ClientHost");
346
347    if (_ds_read_attribute(agent_config, "ClientPort"))
348      port = atoi(_ds_read_attribute(agent_config, "ClientPort"));
349
350    if (host && host[0] == '/')
351      domain = 1;
352  }
353
354  if (host == NULL) {
355    LOG(LOG_CRIT, ERR_CLIENT_INVALID_CONFIG);
356    STATUS(ERR_CLIENT_INVALID_CONFIG);
357    return EINVAL;
358  }
359
360  /* Connect (domain socket) */
361
362  if (domain) {
363    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
364    saun.sun_family = AF_UNIX;
365    strcpy(saun.sun_path, host);
366    addr_len = sizeof(saun.sun_family) + strlen(saun.sun_path) + 1;
367
368    LOGDEBUG(INFO_CLIENT_CONNECTING, host, 0);
369    if(connect(sockfd, (struct sockaddr *)&saun, addr_len)<0) {
370      LOG(LOG_ERR, ERR_CLIENT_CONNECT_SOCKET, host, strerror(errno));
371      STATUS("%s", strerror(errno));
372      close(sockfd);
373      return EFAILURE;
374    }
375
376  /* Connect (TCP socket) */
377
378  } else {
379    sockfd = socket(AF_INET, SOCK_STREAM, 0);
380    memset(&addr, 0, sizeof(struct sockaddr_in));
381    addr.sin_family = AF_INET;
382    addr.sin_addr.s_addr = inet_addr(host);
383    addr.sin_port = htons(port);
384    addr_len = sizeof(struct sockaddr_in);
385    LOGDEBUG(INFO_CLIENT_CONNECTING, host, port);
386    if(connect(sockfd, (struct sockaddr *)&addr, addr_len)<0) {
387      LOG(LOG_ERR, ERR_CLIENT_CONNECT_HOST, host, port, strerror(errno));
388      STATUS("%s", strerror(errno));
389      close(sockfd);
390      return EFAILURE;
391    }
392  }
393
394  LOGDEBUG(INFO_CLIENT_CONNECTED);
395  setsockopt(sockfd,SOL_SOCKET,TCP_NODELAY,&yes,sizeof(int));
396
397  return sockfd;
398}
399
400/*
401 * client_authenticate(AGENT_CTX *ATX, const char *mode)
402 *
403 * DESCRIPTION
404 *   greet and authenticate on a server
405 *
406 * INPUT ARGUMENTS
407 *      ATX             agent context
408 *      mode            processing mode
409 *
410 * NOTES
411 *   the process mode is passed using the DSPAMPROCESSMODE service tag
412 *
413 * RETURN VALUES
414 *   returns 0 on success
415 */
416
417int client_authenticate(THREAD_CTX *TTX, const char *mode) {
418  char *ident = _ds_read_attribute(agent_config, "ClientIdent");
419  char buf[1024], err[128];
420  char *ptr;
421  char pmode[1024];
422
423  pmode[0] = 0;
424  if (mode) {
425    int pos = 0, cpos = 0;
426    for(;mode[cpos]&&(size_t)pos<(sizeof(pmode)-1);cpos++) {
427      if (mode[cpos] == '"') {
428        pmode[pos] = '\\';
429        pos++;
430      }
431      pmode[pos] = mode[cpos];
432      pos++;
433    }
434    pmode[pos] = 0;
435  }
436
437  if (!ident || !strchr(ident, '@')) {
438    LOG(LOG_ERR, ERR_CLIENT_IDENT);
439    return EINVAL;
440  }
441
442  ptr = client_expect(TTX, LMTP_GREETING, err, sizeof(err));
443  if (ptr == NULL) {
444    LOG(LOG_ERR, ERR_CLIENT_WHILE_AUTH, err);
445    return EFAILURE;
446  }
447  free(ptr);
448
449  snprintf(buf, sizeof(buf), "LHLO %s", strchr(ident, '@')+1);
450  if (send_socket(TTX, buf)<=0)
451    return EFAILURE;
452
453  if (client_getcode(TTX, err, sizeof(err))!=LMTP_OK) {
454    return EFAILURE;
455  }
456
457  if (mode) {
458    snprintf(buf, sizeof(buf), "MAIL FROM: <%s> DSPAMPROCESSMODE=\"%s\"", ident, pmode);
459  } else {
460    snprintf(buf, sizeof(buf), "MAIL FROM: <%s>", ident);
461  }
462  if (send_socket(TTX, buf)<=0) {
463    return EFAILURE;
464  }
465
466  if (client_getcode(TTX, err, sizeof(err))!=LMTP_OK) {
467    LOG(LOG_ERR, ERR_CLIENT_AUTHENTICATE);
468    return EFAILURE;
469  }
470
471  return 0;
472}
473
474/*
475 * client_expect(THREAD_CTX *TTX, int code, char *err, size_t len)
476 *
477 * DESCRIPTION
478 *   wait for the appropriate return code, then return
479 *
480 * INPUT ARGUMENTS
481 *    ATX    agent context
482 *    code   return code to wait for
483 *    err    error buffer
484 *    len    buffer len
485 *
486 * RETURN VALUES
487 *   allocated pointer to acknowledgement line, NULL on error
488 *   err buffer is populated on error
489 */
490
491char * client_expect(THREAD_CTX *TTX, int code, char *err, size_t len) {
492  char *inp, *dup, *ptr, *ptrptr;
493  int recv_code;
494
495  inp = client_getline(TTX, 300);
496  while(inp != NULL) {
497    recv_code = 0;
498    dup = strdup(inp);
499    if (!dup) {
500      free(inp);
501      LOG(LOG_CRIT, ERR_MEM_ALLOC);
502      strlcpy(err, ERR_MEM_ALLOC, len);
503      return NULL;
504    }
505    if (strncmp(dup, "250-", 4)) {
506      ptr = strtok_r(dup, " ", &ptrptr);
507      if (ptr)
508        recv_code = atoi(ptr);
509      free(dup);
510      if (recv_code == code) {
511        err[0] = 0;
512        return inp;
513      }
514      LOG(LOG_WARNING, ERR_CLIENT_RESPONSE_CODE, code, inp);
515    }
516   
517    strlcpy(err, inp, len);
518    free(inp);
519    inp = client_getline(TTX, 300);
520  }
521
522  return NULL;
523}
524
525/*
526 * client_parsecode(const char *err)
527 *
528 * DESCRIPTION
529 *   parse response code from plain text
530 *
531 * INPUT ARGUMENTS
532 *      err    error message to parse
533 *
534 * RETURN VALUES
535 *   integer value of response code
536 */
537
538int client_parsecode(char *error) {
539  char code[4];
540  code[3] = 0;
541  strncpy(code, error, 3);
542  return atoi(code);
543}
544
545/*
546 * client_getcode(THREAD_CTX *TTX, char *err, size_t len)
547 *
548 * DESCRIPTION
549 *   retrieve a line of input and return response code
550 *
551 * INPUT ARGUMENTS 
552 *     TTX    thread context containing sockfd
553 *     err    error buffer
554 *     len    buffer len
555 *
556 * RETURN VALUES
557 *   integer value of response code
558 */
559
560int client_getcode(THREAD_CTX *TTX, char *err, size_t len) {
561  char *inp, *ptr, *ptrptr = NULL;
562  int i;
563
564  inp = client_getline(TTX, 300);
565  if (!inp)
566    return EFAILURE;
567
568  while(inp && !strncmp(inp, "250-", 4)) {
569    free(inp);
570    inp = client_getline(TTX, 300);
571  }
572
573  strlcpy(err, inp, len);
574  ptr = strtok_r(inp, " ", &ptrptr);
575  if (ptr == NULL)
576    return EFAILURE;
577  i = atoi(ptr);
578  free(inp);
579  return i;
580}
581
582/*
583 * client_getline(THREAD_CTX *TTX, int timeout)
584 *
585 * DESCRIPTION
586 *   read a complete line from a socket
587 *
588 * INPUT ARGUMENTS 
589 *      TTX        thread context containing sockfd
590 *      timeout    timeout (in seconds) to wait for input
591 *
592 * RETURN VALUES
593 *   allocated pointer to input
594 */
595
596char *client_getline(THREAD_CTX *TTX, int timeout) {
597  struct timeval tv;
598  long recv_len;
599  char buf[1024];
600  char *pop;
601  fd_set fds;
602  int i;
603
604  pop = pop_buffer(TTX);
605  while(!pop) {
606    tv.tv_sec = timeout;
607    tv.tv_usec = 0;
608    FD_ZERO(&fds);
609    FD_SET(TTX->sockfd, &fds);
610    i = select(TTX->sockfd+1, &fds, NULL, NULL, &tv);
611    if (i<=0)
612      return NULL;
613
614    recv_len = recv(TTX->sockfd, buf, sizeof(buf)-1, 0);
615    buf[recv_len] = 0;
616    if (recv_len == 0)
617      return NULL;
618    buffer_cat(TTX->packet_buffer, buf);
619    pop = pop_buffer(TTX);
620  }
621
622#ifdef VERBOSE
623  LOGDEBUG("RECV: %s", pop);
624#endif
625
626  return pop;
627}
628
629/*
630 * pop_buffer (THREAD_CTX *TTX)
631 *
632 * DESCRIPTION
633 *   pop a line off the packet buffer
634 *
635 * INPUT ARGUMENTS
636 *      TTX     thread context containing the packet buffer
637 *
638 * RETURN VALUES
639 *   allocated pointer to line, NULL if complete line isn't available
640 */
641
642char *pop_buffer(THREAD_CTX *TTX) {
643  char *buf, *eol;
644  long len;
645
646  if (!TTX || !TTX->packet_buffer || !TTX->packet_buffer->data)
647    return NULL;
648
649  eol = strchr(TTX->packet_buffer->data, 10);
650  if (!eol)
651    return NULL;
652  len = (eol - TTX->packet_buffer->data) + 1;
653  buf = calloc(1, len+1);
654  if (!buf) {
655    LOG(LOG_CRIT, ERR_MEM_ALLOC);
656    return NULL;
657  }
658  memcpy(buf, TTX->packet_buffer->data, len);
659  memmove(TTX->packet_buffer->data, eol+1, strlen(eol+1)+1);
660  TTX->packet_buffer->used -= len;
661  return buf;
662}
663
664/*
665 * send_socket(THREAD_CTX *TTX, const char *text)
666 *
667 * DESCRIPTION
668 *   send a line of text to a socket
669 *
670 * INPUT ARGUMENTS
671 *      TTX     thread context containing sockfd
672 *      text    text to send
673 *
674 * RETURN VALUES
675 *   number of bytes sent
676 */
677
678int send_socket(THREAD_CTX *TTX, const char *text) {
679  int i = 0, r, msglen;
680
681#ifdef VERBOSE
682  LOGDEBUG("SEND: %s", text);
683#endif
684
685  msglen = strlen(text);
686  while(i<msglen) {
687    r = send(TTX->sockfd, text+i, msglen-i, 0);
688    if (r <= 0) {
689      return r;
690    }
691    i += r;
692  }
693
694  r = send(TTX->sockfd, "\r\n", 2, 0);
695  if (r > 0) {
696    i += r;
697  }
698  return i;
699}
700
701/*
702 * deliver_socket(AGENT_CTX *ATX, const char *msg, int proto)
703 *
704 * DESCRIPTION
705 *   delivers message via LMTP or SMTP (instead of TrustedDeliveryAgent)
706 *
707 *   If LMTP/SMTP delivery was specified in dspam.conf, this function will be
708 *   called by deliver_message(). This function connects to and delivers the
709 *   message using standard LMTP or SMTP. Depending on how DSPAM was originally
710 *   called, either the address supplied with the incoming RCPT TO or the
711 *   address supplied on the commandline with --rcpt-to  will be used. If
712 *   neither are present, the username will be used.
713 *
714 * INPUT ARGUMENTS
715 *     ATX      agent context
716 *     msg      message to send
717 *     proto    protocol to use
718 *
719 * PROTOCOLS
720 *     DDP_LMTP    LMTP
721 *     DDP_SMTP    SMTP
722 *
723 * RETURN VALUES
724 *   returns 0 on success
725 */
726
727int deliver_socket(AGENT_CTX *ATX, const char *msg, int proto) {
728  THREAD_CTX TTX;
729  char buf[1024], err[256];
730  char *ident = _ds_read_attribute(agent_config, "DeliveryIdent");
731  int exitcode = EFAILURE;
732  int msglen, code;
733  int buflen;
734  char *inp;
735  int i;
736  int size_extension = 0;
737
738  err[0] = 0;
739
740  TTX.sockfd = client_connect(ATX, CCF_DELIVERY);
741  if (TTX.sockfd <0) {
742    STATUS(ERR_CLIENT_CONNECT);
743    LOG(LOG_ERR, ERR_CLIENT_CONNECT);
744    return TTX.sockfd;
745  }
746
747  TTX.packet_buffer = buffer_create(NULL);
748  if (TTX.packet_buffer == NULL) {
749    LOG(LOG_CRIT, ERR_MEM_ALLOC);
750    STATUS(ERR_MEM_ALLOC);
751    goto BAIL;
752  }
753
754  inp = client_expect(&TTX, LMTP_GREETING, err, sizeof(err));
755  if (inp == NULL) {
756    LOG(LOG_ERR, ERR_CLIENT_ON_GREETING, err);
757    STATUS("%s", err);
758    goto BAIL;
759  }
760  free(inp);
761
762  /* LHLO / HELO */
763
764  snprintf(buf, sizeof(buf), "%s %s", (proto == DDP_LMTP) ? "LHLO" : "HELO",
765           (ident) ? ident : "localhost");
766  if (send_socket(&TTX, buf)<=0) {
767    LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
768    STATUS(ERR_CLIENT_SEND_FAILED);
769    goto BAIL;
770  }
771
772  /* Check for SIZE extension */
773
774  if (proto == DDP_LMTP) {
775    char *dup, *ptr, *ptrptr;
776    inp = client_getline(&TTX, 300);
777    while(inp != NULL) {
778      code = 0;
779      dup = strdup(inp);
780      if (!dup) {
781        free(inp);
782        LOG(LOG_CRIT, ERR_MEM_ALLOC);
783        LOG(LOG_ERR, ERR_CLIENT_INVALID_RESPONSE, "LHLO", ERR_MEM_ALLOC);
784        STATUS("LHLO: %s", ERR_MEM_ALLOC);
785        goto QUIT;
786      }
787      if (!strcmp(dup, "250-SIZE") || (!strncmp(dup, "250-SIZE", 8) && strlen(dup)>=8 && isspace(dup[8]))) {
788        free(inp);
789        free(dup);
790        size_extension = 1;
791        inp = client_expect(&TTX, LMTP_OK, err, sizeof(err));
792        break;
793      } else if (strncmp(dup, "250-", 4)) {
794        ptr = strtok_r(dup, " ", &ptrptr);
795        if (ptr)
796          code = atoi(ptr);
797        if (code == LMTP_OK) {
798          ptr = strtok_r(NULL, " ", &ptrptr);
799          if (ptr && !strcmp(ptr, "SIZE"))
800            size_extension = 1;
801        }
802        free(dup);
803        if (code == LMTP_OK) {
804          err[0] = 0;
805          break;
806        }
807        LOG(LOG_WARNING, ERR_CLIENT_RESPONSE_CODE, code, inp);
808      }
809      strlcpy(err, inp, sizeof(err));
810      free(inp);
811      inp = client_getline(&TTX, 300);
812    }
813  } else {
814    inp = client_expect(&TTX, LMTP_OK, err, sizeof(err));
815  }
816  if (inp == NULL) {
817    LOG(LOG_ERR, ERR_CLIENT_INVALID_RESPONSE,
818      (proto == DDP_LMTP) ? "LHLO" : "HELO", err);
819    STATUS("%s: %s", (proto == DDP_LMTP) ? "LHLO" : "HELO", err);
820    goto QUIT;
821  }
822  free(inp);
823
824  /* MAIL FROM */
825
826  if (proto == DDP_LMTP && size_extension == 1) {
827    snprintf(buf, sizeof(buf), "MAIL FROM:<%s> SIZE=%ld",
828             ATX->mailfrom, (long) strlen(msg));
829  } else {
830    snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", ATX->mailfrom);
831  }
832
833  if (send_socket(&TTX, buf)<=0) {
834    LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
835    STATUS(ERR_CLIENT_SEND_FAILED);
836    goto BAIL;
837  }
838
839  code = client_getcode(&TTX, err, sizeof(err));
840
841  if (code!=LMTP_OK) {
842    LOG(LOG_ERR, ERR_CLIENT_RESPONSE, code, "MAIL FROM", err);
843    if (code >= 500)
844      exitcode = EINVAL;
845    chomp(err);
846    STATUS((code >= 500) ? "Fatal: %s" : "Deferred: %s", err);
847    goto QUIT;
848  }
849
850  /* RCPT TO */
851
852  snprintf(buf, sizeof(buf), "RCPT TO:<%s>", (ATX->recipient) ? ATX->recipient : "");
853  if (send_socket(&TTX, buf)<=0) {
854    LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
855    STATUS(ERR_CLIENT_SEND_FAILED);
856    goto BAIL;
857  }
858
859  code = client_getcode(&TTX, err, sizeof(err));
860
861  if (code!=LMTP_OK) {
862    LOG(LOG_ERR, ERR_CLIENT_RESPONSE, code, "RCPT TO", err);
863    if (code >= 500)
864      exitcode = EINVAL; 
865    chomp(err);
866    STATUS((code >= 500) ? "Fatal: %s" : "Deferred: %s", err);
867    goto QUIT;
868  }
869
870  /* DATA */
871
872  if (send_socket(&TTX, "DATA")<=0) {
873    LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
874    STATUS(ERR_CLIENT_SEND_FAILED);
875    goto BAIL;
876  }
877
878  code = client_getcode(&TTX, err, sizeof(err));
879  if (code!=LMTP_DATA) {
880    LOG(LOG_ERR, ERR_CLIENT_RESPONSE, code, "DATA", err);
881    if (code >= 500)
882      exitcode = EINVAL;
883    chomp(err);
884    STATUS((code >= 500) ? "Fatal: %s" : "Deferred: %s", err);
885    goto QUIT;
886  }
887
888  i = 0;
889  msglen = strlen(msg);
890  while(i<msglen) {
891    int r;
892    int t;
893
894    /* fill buf with partial msg, replacing \n with \r\n */
895    buflen = 0;
896    while ((size_t)buflen < (sizeof(buf) - 2) && i < msglen) {
897      /* only replace \n and not \r\n */
898      if (i > 0 && msg[i] == '\n' && msg[i - 1] != '\r') {
899        buf[buflen] = '\r';
900        buflen++;
901      }
902
903      /* escape dot if first character on line */
904      if (msg[i] == '.' && (i == 0 || msg[i - 1] == '\n')) {
905        buf[buflen] = '.';
906        buflen++;
907      }
908
909      buf[buflen] = msg[i];
910      buflen++;
911      i++;
912    }
913
914    /* send buf */
915    t = 0;
916    while (t < buflen) {
917      r = send(TTX.sockfd, buf+t, buflen - t, 0);
918      if (r <= 0) {
919        LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
920        STATUS(ERR_CLIENT_SEND_FAILED);
921        goto BAIL;
922      }
923      t += r;
924    }
925  }
926
927  if (msg[strlen(msg)-1]!= '\n') {
928    if (send_socket(&TTX, "")<=0) {
929      LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
930      STATUS(ERR_CLIENT_SEND_FAILED);
931      goto BAIL;
932    }
933  }
934
935  if (send_socket(&TTX, "\r\n.")<=0) {
936    LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
937    STATUS(ERR_CLIENT_SEND_FAILED);
938    goto BAIL;
939  }
940
941  /* server response */
942
943  code = client_getcode(&TTX, err, sizeof(err));
944  if (code < 200 || code >= 300) {
945    LOG(LOG_ERR, ERR_CLIENT_RESPONSE, code, "message data", err);
946    if (code >= 400 && code < 500)
947      exitcode = EX_TEMPFAIL;
948    else if (code >= 500)
949      exitcode = EINVAL;
950    chomp(err);
951    STATUS((code >= 500) ? "Fatal: %s" : "Deferred: %s", err);
952    goto QUIT;
953  }
954
955  send_socket(&TTX, "QUIT");
956  client_getcode(&TTX, err, sizeof(err));
957  close(TTX.sockfd);
958  buffer_destroy(TTX.packet_buffer);
959  return 0;
960
961QUIT:
962  send_socket(&TTX, "QUIT");
963  client_getcode(&TTX, err, sizeof(err));
964  buffer_destroy(TTX.packet_buffer);
965  close(TTX.sockfd);
966  return exitcode;
967
968BAIL:
969  LOG(LOG_ERR, ERR_CLIENT_DELIVERY_FAILED);
970  buffer_destroy(TTX.packet_buffer);
971  close(TTX.sockfd);
972  return exitcode;
973}
974
975#endif /* DAEMON */
Note: See TracBrowser for help on using the repository browser.