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