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 
client_process(AGENT_CTX * ATX,buffer * message)82 int 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         printf("%s\n", line);
232       }
233       free(line);
234       line = client_getline(&TTX, 300);
235       if (line) chomp(line);
236     }
237     free(line);
238   } else {
239     for(i=0;i<ATX->users->items;i++) {
240       char *input = client_getline(&TTX, 300);
241       char *x;
242       int code = 500;
243 
244       if (!input) {
245         goto BAIL;
246       }
247       x = strtok(input, " ");
248       if (x) {
249         code = atoi(x);
250         if (code != LMTP_OK) {
251           if (exitcode > 0)
252             exitcode = 0;
253           exitcode--;
254         } else {
255           if (_ds_match_attribute(agent_config, "Broken", "returnCodes")) {
256             x = strtok(NULL, ":");
257             if (x)
258               x = strtok(NULL, ":");
259             if (x && strstr(x, "SPAM") && exitcode == 0)
260               exitcode = 99;
261           }
262         }
263       }
264     }
265   }
266 
267   send_socket(&TTX, "QUIT");
268   client_getcode(&TTX, err, sizeof(err));
269   close(TTX.sockfd);
270   buffer_destroy(TTX.packet_buffer);
271   return exitcode;
272 
273 QUIT:
274   send_socket(&TTX, "QUIT");
275   client_getcode(&TTX, err, sizeof(err));
276 
277 BAIL:
278   exitcode = EFAILURE;
279   buffer_destroy(TTX.packet_buffer);
280   close(TTX.sockfd);
281   return exitcode;
282 }
283 
284 /*
285  * client_connect(AGENT_CTX ATX, int flags)
286  *
287  * DESCRIPTION
288  *   establish a connection to a server
289  *
290  * INPUT ARGUMENTS
291  *      ATX             agent context
292  *      flags		connection flags
293  *
294  * FLAGS
295  *    CCF_PROCESS     Use ClientHost as destination
296  *    CCF_DELIVERY    Use DeliveryHost as destination
297  *
298  *  RETURN VALUES
299  *    returns 0 on success
300  */
301 
client_connect(AGENT_CTX * ATX,int flags)302 int client_connect(AGENT_CTX *ATX, int flags) {
303   struct sockaddr_in addr;
304   struct sockaddr_un saun;
305   int sockfd;
306   int yes = 1;
307   int port = 2424;
308   int domain = 0;
309   int addr_len;
310   char *host;
311 
312   if (flags & CCF_DELIVERY) {
313     host = _ds_read_attribute(agent_config, "DeliveryHost");
314 
315     if (_ds_read_attribute(agent_config, "DeliveryPort"))
316       port = atoi(_ds_read_attribute(agent_config, "DeliveryPort"));
317 
318     if (ATX->recipient && ATX->recipient[0]) {
319       char *domain = strchr(ATX->recipient, '@');
320       if (domain) {
321         char key[128];
322         char lcdomain[strlen(ATX->recipient)];
323         lc(lcdomain, domain+1);
324         snprintf(key, sizeof(key), "DeliveryHost.%s", lcdomain);
325         if (_ds_read_attribute(agent_config, key))
326           host = _ds_read_attribute(agent_config, key);
327         snprintf(key, sizeof(key), "DeliveryPort.%s", lcdomain);
328         if (_ds_read_attribute(agent_config, key))
329           port = atoi(_ds_read_attribute(agent_config, key));
330       }
331     }
332 
333     if (host && host[0] == '/')
334       domain = 1;
335 
336   } else {
337     host = _ds_read_attribute(agent_config, "ClientHost");
338 
339     if (_ds_read_attribute(agent_config, "ClientPort"))
340       port = atoi(_ds_read_attribute(agent_config, "ClientPort"));
341 
342     if (host && host[0] == '/')
343       domain = 1;
344   }
345 
346   if (host == NULL) {
347     LOG(LOG_CRIT, ERR_CLIENT_INVALID_CONFIG);
348     STATUS(ERR_CLIENT_INVALID_CONFIG);
349     return EINVAL;
350   }
351 
352   /* Connect (domain socket) */
353 
354   if (domain) {
355     sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
356     saun.sun_family = AF_UNIX;
357     strcpy(saun.sun_path, host);
358     addr_len = sizeof(saun.sun_family) + strlen(saun.sun_path) + 1;
359 
360     LOGDEBUG(INFO_CLIENT_CONNECTING, host, 0);
361     if(connect(sockfd, (struct sockaddr *)&saun, addr_len)<0) {
362       LOG(LOG_ERR, ERR_CLIENT_CONNECT_SOCKET, host, strerror(errno));
363       STATUS("%s", strerror(errno));
364       close(sockfd);
365       return EFAILURE;
366     }
367 
368   /* Connect (TCP socket) */
369 
370   } else {
371     sockfd = socket(AF_INET, SOCK_STREAM, 0);
372     memset(&addr, 0, sizeof(struct sockaddr_in));
373     addr.sin_family = AF_INET;
374     addr.sin_addr.s_addr = inet_addr(host);
375     addr.sin_port = htons(port);
376     addr_len = sizeof(struct sockaddr_in);
377     LOGDEBUG(INFO_CLIENT_CONNECTING, host, port);
378     if(connect(sockfd, (struct sockaddr *)&addr, addr_len)<0) {
379       LOG(LOG_ERR, ERR_CLIENT_CONNECT_HOST, host, port, strerror(errno));
380       STATUS("%s", strerror(errno));
381       close(sockfd);
382       return EFAILURE;
383     }
384   }
385 
386   LOGDEBUG(INFO_CLIENT_CONNECTED);
387   setsockopt(sockfd,SOL_SOCKET,TCP_NODELAY,&yes,sizeof(int));
388 
389   return sockfd;
390 }
391 
392 /*
393  * client_authenticate(AGENT_CTX *ATX, const char *mode)
394  *
395  * DESCRIPTION
396  *   greet and authenticate on a server
397  *
398  * INPUT ARGUMENTS
399  *      ATX             agent context
400  *      mode		processing mode
401  *
402  * NOTES
403  *   the process mode is passed using the DSPAMPROCESSMODE service tag
404  *
405  * RETURN VALUES
406  *   returns 0 on success
407  */
408 
client_authenticate(THREAD_CTX * TTX,const char * mode)409 int client_authenticate(THREAD_CTX *TTX, const char *mode) {
410   char *ident = _ds_read_attribute(agent_config, "ClientIdent");
411   char buf[1024], err[128];
412   char *ptr;
413   char pmode[1024];
414 
415   pmode[0] = 0;
416   if (mode) {
417     int pos = 0, cpos = 0;
418     for(;mode[cpos]&&(size_t)pos<(sizeof(pmode)-1);cpos++) {
419       if (mode[cpos] == '"') {
420         pmode[pos] = '\\';
421         pos++;
422       }
423       pmode[pos] = mode[cpos];
424       pos++;
425     }
426     pmode[pos] = 0;
427   }
428 
429   if (!ident || !strchr(ident, '@')) {
430     LOG(LOG_ERR, ERR_CLIENT_IDENT);
431     return EINVAL;
432   }
433 
434   ptr = client_expect(TTX, LMTP_GREETING, err, sizeof(err));
435   if (ptr == NULL) {
436     LOG(LOG_ERR, ERR_CLIENT_WHILE_AUTH, err);
437     return EFAILURE;
438   }
439   free(ptr);
440 
441   snprintf(buf, sizeof(buf), "LHLO %s", strchr(ident, '@')+1);
442   if (send_socket(TTX, buf)<=0)
443     return EFAILURE;
444 
445   if (client_getcode(TTX, err, sizeof(err))!=LMTP_OK) {
446     return EFAILURE;
447   }
448 
449   if (mode) {
450     snprintf(buf, sizeof(buf), "MAIL FROM: <%s> DSPAMPROCESSMODE=\"%s\"", ident, pmode);
451   } else {
452     snprintf(buf, sizeof(buf), "MAIL FROM: <%s>", ident);
453   }
454   if (send_socket(TTX, buf)<=0) {
455     return EFAILURE;
456   }
457 
458   if (client_getcode(TTX, err, sizeof(err))!=LMTP_OK) {
459     LOG(LOG_ERR, ERR_CLIENT_AUTHENTICATE);
460     return EFAILURE;
461   }
462 
463   return 0;
464 }
465 
466 /*
467  * client_expect(THREAD_CTX *TTX, int code, char *err, size_t len)
468  *
469  * DESCRIPTION
470  *   wait for the appropriate return code, then return
471  *
472  * INPUT ARGUMENTS
473  *    ATX    agent context
474  *    code   return code to wait for
475  *    err    error buffer
476  *    len    buffer len
477  *
478  * RETURN VALUES
479  *   allocated pointer to acknowledgement line, NULL on error
480  *   err buffer is populated on error
481  */
482 
client_expect(THREAD_CTX * TTX,int code,char * err,size_t len)483 char * client_expect(THREAD_CTX *TTX, int code, char *err, size_t len) {
484   char *inp, *dup, *ptr, *ptrptr;
485   int recv_code;
486 
487   inp = client_getline(TTX, 300);
488   while(inp != NULL) {
489     recv_code = 0;
490     dup = strdup(inp);
491     if (!dup) {
492       free(inp);
493       LOG(LOG_CRIT, ERR_MEM_ALLOC);
494       strlcpy(err, ERR_MEM_ALLOC, len);
495       return NULL;
496     }
497     if (strncmp(dup, "250-", 4)) {
498       ptr = strtok_r(dup, " ", &ptrptr);
499       if (ptr)
500         recv_code = atoi(ptr);
501       free(dup);
502       if (recv_code == code) {
503         err[0] = 0;
504         return inp;
505       }
506       LOG(LOG_WARNING, ERR_CLIENT_RESPONSE_CODE, code, inp);
507     }
508 
509     strlcpy(err, inp, len);
510     free(inp);
511     inp = client_getline(TTX, 300);
512   }
513 
514   return NULL;
515 }
516 
517 /*
518  * client_parsecode(const char *err)
519  *
520  * DESCRIPTION
521  *   parse response code from plain text
522  *
523  * INPUT ARGUMENTS
524  *      err    error message to parse
525  *
526  * RETURN VALUES
527  *   integer value of response code
528  */
529 
client_parsecode(char * error)530 int client_parsecode(char *error) {
531   char code[4];
532   code[3] = 0;
533   strncpy(code, error, 3);
534   return atoi(code);
535 }
536 
537 /*
538  * client_getcode(THREAD_CTX *TTX, char *err, size_t len)
539  *
540  * DESCRIPTION
541  *   retrieve a line of input and return response code
542  *
543  * INPUT ARGUMENTS
544  *     TTX    thread context containing sockfd
545  *     err    error buffer
546  *     len    buffer len
547  *
548  * RETURN VALUES
549  *   integer value of response code
550  */
551 
client_getcode(THREAD_CTX * TTX,char * err,size_t len)552 int client_getcode(THREAD_CTX *TTX, char *err, size_t len) {
553   char *inp, *ptr, *ptrptr = NULL;
554   int i;
555 
556   inp = client_getline(TTX, 300);
557   if (!inp)
558     return EFAILURE;
559 
560   while(inp && !strncmp(inp, "250-", 4)) {
561     free(inp);
562     inp = client_getline(TTX, 300);
563   }
564 
565   strlcpy(err, inp, len);
566   ptr = strtok_r(inp, " ", &ptrptr);
567   if (ptr == NULL)
568     return EFAILURE;
569   i = atoi(ptr);
570   free(inp);
571   return i;
572 }
573 
574 /*
575  * client_getline(THREAD_CTX *TTX, int timeout)
576  *
577  * DESCRIPTION
578  *   read a complete line from a socket
579  *
580  * INPUT ARGUMENTS
581  *      TTX        thread context containing sockfd
582  *      timeout    timeout (in seconds) to wait for input
583  *
584  * RETURN VALUES
585  *   allocated pointer to input
586  */
587 
client_getline(THREAD_CTX * TTX,int timeout)588 char *client_getline(THREAD_CTX *TTX, int timeout) {
589   struct timeval tv;
590   long recv_len;
591   char buf[1024];
592   char *pop;
593   fd_set fds;
594   int i;
595 
596   pop = pop_buffer(TTX);
597   while(!pop) {
598     tv.tv_sec = timeout;
599     tv.tv_usec = 0;
600     FD_ZERO(&fds);
601     FD_SET(TTX->sockfd, &fds);
602     i = select(TTX->sockfd+1, &fds, NULL, NULL, &tv);
603     if (i<=0)
604       return NULL;
605 
606     recv_len = recv(TTX->sockfd, buf, sizeof(buf)-1, 0);
607     buf[recv_len] = 0;
608     if (recv_len == 0)
609       return NULL;
610     buffer_cat(TTX->packet_buffer, buf);
611     pop = pop_buffer(TTX);
612   }
613 
614 #ifdef VERBOSE
615   LOGDEBUG("RECV: %s", pop);
616 #endif
617 
618   return pop;
619 }
620 
621 /*
622  * pop_buffer (THREAD_CTX *TTX)
623  *
624  * DESCRIPTION
625  *   pop a line off the packet buffer
626  *
627  * INPUT ARGUMENTS
628  *      TTX     thread context containing the packet buffer
629  *
630  * RETURN VALUES
631  *   allocated pointer to line, NULL if complete line isn't available
632  */
633 
pop_buffer(THREAD_CTX * TTX)634 char *pop_buffer(THREAD_CTX *TTX) {
635   char *buf, *eol;
636   long len;
637 
638   if (!TTX || !TTX->packet_buffer || !TTX->packet_buffer->data)
639     return NULL;
640 
641   eol = strchr(TTX->packet_buffer->data, 10);
642   if (!eol)
643     return NULL;
644   len = (eol - TTX->packet_buffer->data) + 1;
645   buf = calloc(1, len+1);
646   if (!buf) {
647     LOG(LOG_CRIT, ERR_MEM_ALLOC);
648     return NULL;
649   }
650   memcpy(buf, TTX->packet_buffer->data, len);
651   memmove(TTX->packet_buffer->data, eol+1, strlen(eol+1)+1);
652   TTX->packet_buffer->used -= len;
653   return buf;
654 }
655 
656 /*
657  * send_socket(THREAD_CTX *TTX, const char *text)
658  *
659  * DESCRIPTION
660  *   send a line of text to a socket
661  *
662  * INPUT ARGUMENTS
663  *      TTX     thread context containing sockfd
664  *      text    text to send
665  *
666  * RETURN VALUES
667  *   number of bytes sent
668  */
669 
send_socket(THREAD_CTX * TTX,const char * text)670 int send_socket(THREAD_CTX *TTX, const char *text) {
671   int i = 0, r, msglen;
672 
673 #ifdef VERBOSE
674   LOGDEBUG("SEND: %s", text);
675 #endif
676 
677   msglen = strlen(text);
678   while(i<msglen) {
679     r = send(TTX->sockfd, text+i, msglen-i, 0);
680     if (r <= 0) {
681       return r;
682     }
683     i += r;
684   }
685 
686   r = send(TTX->sockfd, "\r\n", 2, 0);
687   if (r > 0) {
688     i += r;
689   }
690   return i;
691 }
692 
693 /*
694  * deliver_socket(AGENT_CTX *ATX, const char *msg, int proto)
695  *
696  * DESCRIPTION
697  *   delivers message via LMTP or SMTP (instead of TrustedDeliveryAgent)
698  *
699  *   If LMTP/SMTP delivery was specified in dspam.conf, this function will be
700  *   called by deliver_message(). This function connects to and delivers the
701  *   message using standard LMTP or SMTP. Depending on how DSPAM was originally
702  *   called, either the address supplied with the incoming RCPT TO or the
703  *   address supplied on the commandline with --rcpt-to  will be used. If
704  *   neither are present, the username will be used.
705  *
706  * INPUT ARGUMENTS
707  *     ATX      agent context
708  *     msg      message to send
709  *     proto    protocol to use
710  *
711  * PROTOCOLS
712  *     DDP_LMTP    LMTP
713  *     DDP_SMTP    SMTP
714  *
715  * RETURN VALUES
716  *   returns 0 on success
717  */
718 
deliver_socket(AGENT_CTX * ATX,const char * msg,int proto)719 int deliver_socket(AGENT_CTX *ATX, const char *msg, int proto) {
720   THREAD_CTX TTX;
721   char buf[1024], err[256];
722   char *ident = _ds_read_attribute(agent_config, "DeliveryIdent");
723   int exitcode = EFAILURE;
724   int msglen, code;
725   int buflen;
726   char *inp;
727   int i;
728   int size_extension = 0;
729 
730   err[0] = 0;
731 
732   TTX.sockfd = client_connect(ATX, CCF_DELIVERY);
733   if (TTX.sockfd <0) {
734     STATUS(ERR_CLIENT_CONNECT);
735     LOG(LOG_ERR, ERR_CLIENT_CONNECT);
736     return TTX.sockfd;
737   }
738 
739   TTX.packet_buffer = buffer_create(NULL);
740   if (TTX.packet_buffer == NULL) {
741     LOG(LOG_CRIT, ERR_MEM_ALLOC);
742     STATUS(ERR_MEM_ALLOC);
743     goto BAIL;
744   }
745 
746   inp = client_expect(&TTX, LMTP_GREETING, err, sizeof(err));
747   if (inp == NULL) {
748     LOG(LOG_ERR, ERR_CLIENT_ON_GREETING, err);
749     STATUS("%s", err);
750     goto BAIL;
751   }
752   free(inp);
753 
754   /* LHLO / HELO */
755 
756   snprintf(buf, sizeof(buf), "%s %s", (proto == DDP_LMTP) ? "LHLO" : "HELO",
757            (ident) ? ident : "localhost");
758   if (send_socket(&TTX, buf)<=0) {
759     LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
760     STATUS(ERR_CLIENT_SEND_FAILED);
761     goto BAIL;
762   }
763 
764   /* Check for SIZE extension */
765 
766   if (proto == DDP_LMTP) {
767     char *dup, *ptr, *ptrptr;
768     inp = client_getline(&TTX, 300);
769     while(inp != NULL) {
770       code = 0;
771       dup = strdup(inp);
772       if (!dup) {
773         free(inp);
774         LOG(LOG_CRIT, ERR_MEM_ALLOC);
775         LOG(LOG_ERR, ERR_CLIENT_INVALID_RESPONSE, "LHLO", ERR_MEM_ALLOC);
776         STATUS("LHLO: %s", ERR_MEM_ALLOC);
777         goto QUIT;
778       }
779       if (!strcmp(dup, "250-SIZE") || (!strncmp(dup, "250-SIZE", 8) && strlen(dup)>=8 && isspace(dup[8]))) {
780         free(inp);
781         free(dup);
782         size_extension = 1;
783         inp = client_expect(&TTX, LMTP_OK, err, sizeof(err));
784         break;
785       } else if (strncmp(dup, "250-", 4)) {
786         ptr = strtok_r(dup, " ", &ptrptr);
787         if (ptr)
788           code = atoi(ptr);
789         if (code == LMTP_OK) {
790           ptr = strtok_r(NULL, " ", &ptrptr);
791           if (ptr && !strcmp(ptr, "SIZE"))
792             size_extension = 1;
793         }
794         free(dup);
795         if (code == LMTP_OK) {
796           err[0] = 0;
797           break;
798         }
799         LOG(LOG_WARNING, ERR_CLIENT_RESPONSE_CODE, code, inp);
800       }
801       strlcpy(err, inp, sizeof(err));
802       free(inp);
803       inp = client_getline(&TTX, 300);
804     }
805   } else {
806     inp = client_expect(&TTX, LMTP_OK, err, sizeof(err));
807   }
808   if (inp == NULL) {
809     LOG(LOG_ERR, ERR_CLIENT_INVALID_RESPONSE,
810       (proto == DDP_LMTP) ? "LHLO" : "HELO", err);
811     STATUS("%s: %s", (proto == DDP_LMTP) ? "LHLO" : "HELO", err);
812     goto QUIT;
813   }
814   free(inp);
815 
816   /* MAIL FROM */
817 
818   if (proto == DDP_LMTP && size_extension == 1) {
819     snprintf(buf, sizeof(buf), "MAIL FROM:<%s> SIZE=%ld",
820              ATX->mailfrom, (long) strlen(msg));
821   } else {
822     snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", ATX->mailfrom);
823   }
824 
825   if (send_socket(&TTX, buf)<=0) {
826     LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
827     STATUS(ERR_CLIENT_SEND_FAILED);
828     goto BAIL;
829   }
830 
831   code = client_getcode(&TTX, err, sizeof(err));
832 
833   if (code!=LMTP_OK) {
834     LOG(LOG_ERR, ERR_CLIENT_RESPONSE, code, "MAIL FROM", err);
835     if (code >= 500)
836       exitcode = EINVAL;
837     chomp(err);
838     STATUS((code >= 500) ? "Fatal: %s" : "Deferred: %s", err);
839     goto QUIT;
840   }
841 
842   /* RCPT TO */
843 
844   snprintf(buf, sizeof(buf), "RCPT TO:<%s>", (ATX->recipient) ? ATX->recipient : "");
845   if (send_socket(&TTX, buf)<=0) {
846     LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
847     STATUS(ERR_CLIENT_SEND_FAILED);
848     goto BAIL;
849   }
850 
851   code = client_getcode(&TTX, err, sizeof(err));
852 
853   if (code!=LMTP_OK) {
854     LOG(LOG_ERR, ERR_CLIENT_RESPONSE, code, "RCPT TO", err);
855     if (code >= 500)
856       exitcode = EINVAL;
857     chomp(err);
858     STATUS((code >= 500) ? "Fatal: %s" : "Deferred: %s", err);
859     goto QUIT;
860   }
861 
862   /* DATA */
863 
864   if (send_socket(&TTX, "DATA")<=0) {
865     LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
866     STATUS(ERR_CLIENT_SEND_FAILED);
867     goto BAIL;
868   }
869 
870   code = client_getcode(&TTX, err, sizeof(err));
871   if (code!=LMTP_DATA) {
872     LOG(LOG_ERR, ERR_CLIENT_RESPONSE, code, "DATA", err);
873     if (code >= 500)
874       exitcode = EINVAL;
875     chomp(err);
876     STATUS((code >= 500) ? "Fatal: %s" : "Deferred: %s", err);
877     goto QUIT;
878   }
879 
880   i = 0;
881   msglen = strlen(msg);
882   while(i<msglen) {
883     int r;
884     int t;
885 
886     /* fill buf with partial msg, replacing \n with \r\n */
887     buflen = 0;
888     while ((size_t)buflen < (sizeof(buf) - 2) && i < msglen) {
889       /* only replace \n and not \r\n */
890       if (i > 0 && msg[i] == '\n' && msg[i - 1] != '\r') {
891         buf[buflen] = '\r';
892         buflen++;
893       }
894 
895       /* escape dot if first character on line */
896       if (msg[i] == '.' && (i == 0 || msg[i - 1] == '\n')) {
897         buf[buflen] = '.';
898         buflen++;
899       }
900 
901       buf[buflen] = msg[i];
902       buflen++;
903       i++;
904     }
905 
906     /* send buf */
907     t = 0;
908     while (t < buflen) {
909       r = send(TTX.sockfd, buf+t, buflen - t, 0);
910       if (r <= 0) {
911         LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
912         STATUS(ERR_CLIENT_SEND_FAILED);
913         goto BAIL;
914       }
915       t += r;
916     }
917   }
918 
919   if (msg[strlen(msg)-1]!= '\n') {
920     if (send_socket(&TTX, "")<=0) {
921       LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
922       STATUS(ERR_CLIENT_SEND_FAILED);
923       goto BAIL;
924     }
925   }
926 
927   if (send_socket(&TTX, "\r\n.")<=0) {
928     LOG(LOG_ERR, ERR_CLIENT_SEND_FAILED);
929     STATUS(ERR_CLIENT_SEND_FAILED);
930     goto BAIL;
931   }
932 
933   /* server response */
934 
935   code = client_getcode(&TTX, err, sizeof(err));
936   if (code < 200 || code >= 300) {
937     LOG(LOG_ERR, ERR_CLIENT_RESPONSE, code, "message data", err);
938     if (code >= 400 && code < 500)
939       exitcode = EX_TEMPFAIL;
940     else if (code >= 500)
941       exitcode = EINVAL;
942     chomp(err);
943     STATUS((code >= 500) ? "Fatal: %s" : "Deferred: %s", err);
944     goto QUIT;
945   }
946 
947   send_socket(&TTX, "QUIT");
948   client_getcode(&TTX, err, sizeof(err));
949   close(TTX.sockfd);
950   buffer_destroy(TTX.packet_buffer);
951   return 0;
952 
953 QUIT:
954   send_socket(&TTX, "QUIT");
955   client_getcode(&TTX, err, sizeof(err));
956   buffer_destroy(TTX.packet_buffer);
957   close(TTX.sockfd);
958   return exitcode;
959 
960 BAIL:
961   LOG(LOG_ERR, ERR_CLIENT_DELIVERY_FAILED);
962   buffer_destroy(TTX.packet_buffer);
963   close(TTX.sockfd);
964   return exitcode;
965 }
966 
967 #endif /* DAEMON */
968