source: trunk/src/sh_mail.c@ 558

Last change on this file since 558 was 481, checked in by katerina, 9 years ago

Enhancements and fixes for tickets #374, #375, #376, #377, #378, and #379.

File size: 44.6 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 1999, 2000 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include "config_xor.h"
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26#include <pwd.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <errno.h>
31#include <signal.h>
32#include <setjmp.h>
33
34#if defined(SH_WITH_MAIL)
35
36#if TIME_WITH_SYS_TIME
37#include <sys/time.h>
38#include <time.h>
39#else
40#if HAVE_SYS_TIME_H
41#include <sys/time.h>
42#else
43#include <time.h>
44#endif
45#endif
46
47
48#ifdef HAVE_MEMORY_H
49#include <memory.h>
50#endif
51
52#include "samhain.h"
53#include "sh_error.h"
54#include "sh_unix.h"
55#include "sh_tiger.h"
56#include "sh_mail.h"
57#include "sh_utils.h"
58#include "sh_fifo.h"
59#include "sh_tools.h"
60#include "sh_pthread.h"
61#include "sh_filter.h"
62#include "sh_mail_int.h"
63#include "sh_nmail.h"
64#include "sh_ipvx.h"
65
66#undef FIL__
67#define FIL__ _("sh_mail.c")
68#undef GOOD
69#undef BAD
70
71static int failedMail = S_FALSE;
72
73static dnsrep * return_mx (char *domain);
74
75/*********************************************
76 * utility function for verifying mails
77 *********************************************/
78
79typedef struct mail_trail_struct {
80 char trail_id[2*SH_MINIBUF];
81 char trail_key[KEY_LEN+1];
82 struct mail_trail_struct * next;
83} mail_trail_type;
84
85static mail_trail_type * mail_trail = NULL;
86
87int sh_mail_sigverify (const char * s)
88{
89 SL_TICKET fd;
90 long i;
91 char * buf;
92 char * bufc;
93 char key[81];
94 char number[2*SH_MINIBUF];
95 char audit_id[2 * SH_MINIBUF];
96 long numsig;
97 char key2[KEY_LEN+1];
98
99 char * theSig;
100
101 mail_trail_type * mail_trail_ptr = NULL;
102
103 sh_error_logoff();
104
105 ASSERT((s != NULL && sl_strlen(s) < PATH_MAX),
106 _("(s != NULL && sl_strlen(s) < PATH_MAX)"));
107
108 if (s == NULL || sl_strlen(s) >= PATH_MAX)
109 _exit (EXIT_FAILURE);
110
111 /* open the file, then check it
112 */
113 if (0 != sl_is_suid())
114 {
115 fprintf(stderr, _("Cannot open file %s in suid mode\n"), s);
116 _exit (EXIT_FAILURE);
117 }
118 if ( SL_ISERROR(fd = sl_open_read (FIL__, __LINE__, s, SL_NOPRIV)))
119 {
120 fprintf(stderr, _("Could not open file %s\n"), s);
121 _exit (EXIT_FAILURE);
122 }
123
124 buf = SH_ALLOC( (size_t)(SH_MSG_BUF+SH_BUFSIZE+1));
125 bufc = SH_ALLOC( (size_t)(SH_MSG_BUF+SH_MAXBUF+1));
126
127 while (1 == 1)
128 {
129 buf[0] = '\0';
130 bufc[0] = '\0';
131
132 /* find start of next message
133 */
134 while (0 != sl_strncmp(buf, _("-----BEGIN MESSAGE-----"),
135 sizeof("-----BEGIN MESSAGE-----")-1))
136 {
137 (void) sh_unix_getline (fd, buf, SH_MSG_BUF+SH_BUFSIZE);
138 if (buf[0] == '\0')
139 {
140 /* End of mailbox reached, exit.
141 */
142 (void) fflush(stdout);
143 _exit (EXIT_SUCCESS);
144
145 /* Fix for AIX cc complaint.
146 */
147 /*@notreached@*/
148 return 0;
149 }
150 }
151
152 /* Read message, compress into bufc.
153 */
154 while (1 == 1)
155 {
156 (void) sh_unix_getline (fd, buf, SH_MSG_BUF+SH_BUFSIZE);
157 if (0 == sl_strncmp(buf, _("-----BEGIN SIGNATURE-----"),
158 sizeof("-----BEGIN SIGNATURE-----")-1))
159 break;
160 if (buf[0] == '\0')
161 _exit (EXIT_FAILURE);
162 (void) sh_util_compress(bufc, buf, SH_MSG_BUF+SH_MAXBUF-KEY_LEN);
163 }
164
165 /* get signature and number
166 */
167 (void) sh_unix_getline (fd, key, (int)sizeof(key));
168 key[KEY_LEN] = '\0';
169
170 (void) sh_unix_getline (fd, number, (int)sizeof(number));
171 number[(2*SH_MINIBUF) - 2] = '\0';
172 numsig = atol (number);
173 (void) sl_strlcpy (audit_id, &number[7], 2*SH_MINIBUF);
174
175 fprintf(stderr, _("Message %06ld Trail %s\n"),
176 numsig, /*@-usedef@*/ audit_id /*@+usedef@*/);
177
178 mail_trail_ptr = mail_trail;
179 while (mail_trail_ptr)
180 {
181 if (0 == sl_strcmp(mail_trail_ptr->trail_id, audit_id))
182 break;
183 mail_trail_ptr = mail_trail_ptr->next;
184 }
185
186 if (!mail_trail_ptr)
187 {
188 if (numsig > 0)
189 {
190 fprintf (stderr, "%s",_("ERROR (no key -- cannot check)\n"));
191 continue;
192 }
193 else
194 {
195 mail_trail_ptr = SH_ALLOC (sizeof(mail_trail_type));
196 mail_trail_ptr->next = mail_trail;
197 mail_trail = mail_trail_ptr;
198 (void) sl_strlcpy (mail_trail_ptr->trail_id,
199 audit_id, 2*SH_MINIBUF);
200 }
201 }
202 else if (numsig == 0)
203 {
204 fprintf (stderr, "%s",_("ERROR (repeated audit trail)\n"));
205 continue;
206 }
207
208
209 if (numsig == 0)
210 {
211 sh_util_encode(key, bufc, 1, 'A');
212 (void) sl_strlcpy (mail_trail_ptr->trail_key, key, KEY_LEN+1);
213 fprintf (stderr, "%s",_("(unchecked)\n"));
214 }
215 else
216 {
217 char sigbuf[KEYBUF_SIZE];
218
219 /* iterate key
220 */
221 (void) sl_strlcpy(key2, mail_trail_ptr->trail_key, KEY_LEN+1);
222 for (i = 0; i < numsig; ++i)
223 {
224 char hashbuf[KEYBUF_SIZE];
225 (void) sl_strlcpy (key2,
226 sh_tiger_hash (key2, TIGER_DATA, KEY_LEN,
227 hashbuf, sizeof(hashbuf)),
228 KEY_LEN+1);
229 }
230
231 theSig = sh_util_siggen (key2, bufc, sl_strlen(bufc),
232 sigbuf, sizeof(sigbuf));
233
234 if (sl_strncmp (key,
235 theSig,
236 KEY_LEN) != 0)
237 {
238 fprintf (stderr, "%s",_("(FAILED)\n"));
239 }
240 else
241 {
242 fprintf (stderr, "%s",_("(passed)\n"));
243 }
244
245 }
246
247 } /* end scan mailbox */
248
249 /*@notreached@*/
250}
251
252int sh_mail_setNum (const char * str)
253{
254 int i = atoi (str);
255
256 SL_ENTER(_("sh_mail_setNum"));
257
258 if (i >= 0 && i < SH_FIFO_MAX)
259 sh.mailNum.alarm_interval = (time_t) i;
260 else
261 SL_RETURN ((-1), _("sh_mail_setNum"));
262 SL_RETURN( (0), _("sh_mail_setNum"));
263}
264
265
266int sh_mail_all_in_one = S_FALSE;
267
268int sh_mail_setFlag (const char * str)
269{
270 int i;
271 SL_ENTER(_("sh_mail_setFlag"));
272 i = sh_util_flagval(str, &sh_mail_all_in_one);
273 SL_RETURN(i, _("sh_mail_setFlag"));
274}
275
276static char * mail_subject = NULL;
277
278int set_mail_subject (const char * str)
279{
280 SL_ENTER(_("set_mail_subject"));
281 if (!str)
282 SL_RETURN( (-1), _("set_mail_subject"));
283
284 if (mail_subject != NULL)
285 SH_FREE(mail_subject);
286
287 if (0 == sl_strncmp(str, _("NULL"), 4))
288 {
289 mail_subject = NULL;
290 SL_RETURN( 0, _("set_mail_subject"));
291 }
292
293 mail_subject = sh_util_strdup(str);
294 SL_RETURN( (0), _("set_mail_subject"));
295}
296
297SH_MUTEX_INIT(mutex_fifo_mail, PTHREAD_MUTEX_INITIALIZER);
298
299SH_FIFO * fifo_mail = NULL;
300
301static
302void sh_mail_emptystack (void)
303{
304 char * msg;
305 size_t len;
306
307 SL_ENTER(_("sh_mail_emptystack"));
308
309 if (fifo_mail == NULL)
310 SL_RET0(_("sh_mail_emptystack"));
311
312 SH_MUTEX_LOCK(mutex_fifo_mail);
313 while (NULL != (msg = pop_list(fifo_mail)))
314 {
315 len = sl_strlen(msg);
316 memset(msg, 0, len);
317 SH_FREE(msg);
318 }
319 SH_MUTEX_UNLOCK(mutex_fifo_mail);
320
321 SL_RET0(_("sh_mail_emptystack"));
322}
323
324/* insert "\r\n" after each 998 char
325 */
326static char * split_string(const char * str);
327
328/* fixes warning: variable ‘p’ might be clobbered by ‘longjmp’ or ‘vfork’*/
329static char ** p_dummy;
330
331int sh_mail_pushstack (int severity, const char * msg, const char * alias)
332{
333 char * p;
334 volatile int retval = 0;
335 int status;
336
337 SL_ENTER(_("sh_mail_pushstack"));
338
339 if (msg == NULL || failedMail == S_TRUE /* || sh.srvmail.name[0] == '\0' */)
340 SL_RETURN((0), (_("sh_mail_pushstack")));
341
342 p = split_string(msg);
343 /* fixes "variable ‘p’ might be clobbered by ‘longjmp’ or ‘vfork’" */
344 p_dummy = &p;
345
346 SH_MUTEX_LOCK(mutex_fifo_mail);
347
348 if (fifo_mail == NULL)
349 {
350 fifo_mail = SH_ALLOC(sizeof(SH_FIFO));
351 fifo_init(fifo_mail);
352 }
353 status = push_list (fifo_mail, p, severity, alias);
354 SH_MUTEX_UNLOCK(mutex_fifo_mail);
355
356 if (status >= 0)
357 ++sh.mailNum.alarm_last;
358
359 SH_FREE(p);
360
361 if (sh.mailNum.alarm_last >= sh.mailNum.alarm_interval)
362 {
363 BREAKEXIT(sh_nmail_flush);
364 retval = sh_nmail_flush ();
365 }
366
367 if (status == SH_FIFO_MAX)
368 retval = -2;
369 SL_RETURN(retval, (_("sh_mail_pushstack")));
370}
371
372
373/* The mailer.
374 */
375static int sh_mail_end_conn (FILE * connfile, int fd);
376static FILE * sh_mail_start_conn (struct alias * address, int * fd, int * anum);
377
378static
379void sh_mail_get_subject(const char * message,
380 char * mheader, size_t len)
381{
382 st_format rep_serv_tab[] = {
383 { 'T', S_FMT_TIME, 0, 0, NULL},
384 { 'H', S_FMT_STRING, 0, 0, NULL},
385 { 'M', S_FMT_STRING, 0, 0, NULL},
386 { 'S', S_FMT_STRING, 0, 0, NULL},
387 {'\0', S_FMT_ULONG, 0, 0, NULL},
388 };
389
390 char * p;
391 char * mptr;
392 char sev[8];
393 char * msg;
394
395 SL_ENTER(_("sh_mail_get_subject"));
396
397 (void) sl_strlcpy(mheader, _("Subject: "), len);
398 if (NULL == strchr(mail_subject, '%'))
399 {
400 (void) sl_strlcat(mheader, mail_subject, len);
401 SL_RET0(_("sh_mail_get_subject"));
402 }
403
404
405 rep_serv_tab[0].data_ulong = (unsigned long) time(NULL);
406 rep_serv_tab[1].data_str = sh.host.name;
407
408 /* fast forward to the important part
409 */
410 msg = sh_util_strdup(message);
411
412 mptr = sl_strstr(msg, _("msg="));
413 if (mptr)
414 {
415 mptr += 4;
416 rep_serv_tab[2].data_str = mptr;
417 }
418 else
419 rep_serv_tab[2].data_str = msg;
420
421 mptr = sl_strstr(msg, _("sev="));
422 if (mptr)
423 {
424 mptr += 5;
425 sev[0] = *mptr; ++mptr;
426 sev[1] = *mptr; ++mptr;
427 sev[2] = *mptr; ++mptr;
428 sev[3] = *mptr; ++mptr;
429 sev[4] = '\0';
430 }
431 else
432 {
433 mptr = msg;
434 sev[0] = *mptr; ++mptr;
435 sev[1] = *mptr; ++mptr;
436 sev[2] = *mptr; ++mptr;
437 sev[3] = *mptr; ++mptr;
438 if (*mptr == ' ') {
439 sev[4] = '\0';
440 } else {
441 sev[4] = *mptr; ++mptr;
442 if (*mptr == ' ') {
443 sev[5] = '\0';
444 } else {
445 sev[5] = *mptr;
446 sev[6] = '\0';
447 }
448 }
449 }
450 rep_serv_tab[3].data_str = sev;
451
452
453 p = sh_util_formatted(mail_subject, rep_serv_tab);
454 (void) sl_strlcat(mheader, p, len);
455 SH_FREE(p);
456 SH_FREE(msg);
457 SL_RET0(_("sh_mail_get_subject"));
458}
459
460sh_string * sh_mail_signature_block (sh_string * sigMsg, char * recipient,
461 char * bufcompress)
462{
463 time_t id_audit;
464 char * theSig;
465 char ibuf[80];
466 unsigned int count;
467
468 /* ------ signature block ------------------------------------ */
469
470 sigMsg = sh_string_add_from_char(sigMsg,
471 _("-----BEGIN SIGNATURE-----\r\n"));
472
473 count = sh_nmail_get_mailkey (recipient, skey->mailkey_new, KEY_LEN+1,
474 &id_audit);
475
476 if (count != 0)
477 {
478 char sigbuf[KEYBUF_SIZE];
479
480 /* Sign the message with the signature key.
481 */
482 theSig = sh_util_siggen (skey->mailkey_new,
483 bufcompress, sl_strlen(bufcompress),
484 sigbuf, sizeof(sigbuf));
485 sigMsg = sh_string_add_from_char(sigMsg, theSig);
486 }
487 else
488 {
489 /* reveal first signature key
490 */
491 /* flawfinder: ignore */
492 (void) sl_strlcpy(skey->crypt, skey->mailkey_new, KEY_LEN+1);
493
494 BREAKEXIT(sh_util_encode);
495 /* flawfinder: ignore */
496 sh_util_encode(skey->crypt, bufcompress, 0, 'A');
497
498 /* flawfinder: ignore */
499 sigMsg = sh_string_add_from_char(sigMsg, skey->crypt);
500
501 /* flawfinder: ignore */
502 memset (skey->crypt, 0, KEY_LEN);
503 }
504
505 sigMsg = sh_string_add_from_char(sigMsg, "\r\n");
506
507 sl_snprintf(ibuf, sizeof(ibuf), _("%06u %010lu::%s\r\n"),
508 count, (unsigned long) id_audit, sh.host.name);
509
510 sigMsg = sh_string_add_from_char(sigMsg, ibuf);
511 sigMsg = sh_string_add_from_char(sigMsg, _("-----END MESSAGE-----"));
512
513 return sigMsg;
514}
515
516int sh_mail_msg (const char * message)
517{
518 char subject[32+32+SH_MINIBUF+2+3+SH_PATHBUF];
519 char mheader[32+32+SH_MINIBUF+2+3];
520
521 sh_string * mailMsg;
522 sh_string * compMsg;
523 int status = 0;
524 volatile int errcount;
525 size_t wrlen;
526 volatile int retval = -1;
527
528 char * bufcompress;
529 size_t compressed;
530
531 static int failcount = 0;
532 FILE * connfile = NULL;
533
534 static time_t fail_time = 0;
535 static time_t success_time = 0;
536
537 int ma_socket = -1;
538
539 int address_num = 0;
540 sh_string * theMsg = NULL;
541
542 /* #define SH_MAILBUF (256) */
543#define SH_MAILBUF 4096
544
545 char timebuf[81];
546
547 SL_ENTER(_("sh_mail_msg"));
548
549 /*
550 * Return if we cannot mail.
551 */
552 if (failedMail == S_TRUE)
553 SL_RETURN((-1), _("sh_mail_msg"));
554
555 /*
556 * Final failure, can't mail for SH_MAX_FAIL hours.
557 */
558 if ( (success_time > 0) && (fail_time > 0) &&
559 (time(NULL) - success_time) > 3600*SH_MAX_FAIL)
560 {
561 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_SRV_FAIL,
562 _("mail"),
563 sh_string_str(all_recipients->recipient));
564 sh_mail_emptystack();
565 sh.mailNum.alarm_last = 0;
566 failedMail = S_TRUE;
567 SL_RETURN((-1), _("sh_mail_msg"));
568 }
569
570 /*
571 * Try at most every three seconds to mail if there was a failure.
572 */
573 if ((fail_time > 0) && (time(NULL) - fail_time) < 3/*600*/)
574 {
575 if (failcount > 3)
576 {
577 /* -- Save for later. Changed: done by caller. --
578 * sh_nmail_pushstack (severity, message, alias);
579 */
580 ++failcount;
581
582 SL_RETURN((-2), _("sh_mail_msg"));
583 }
584 else
585 {
586 (void) retry_msleep(2, 0);
587 ++failcount;
588 }
589 }
590
591 /* -- Reset time of last failure. --
592 */
593 fail_time = 0;
594
595
596 /* --------- Build complete message. ------------------------ */
597
598 /* Don't flush the queue here, because tag_list doesn't know
599 * how to filter messages. */
600
601 theMsg = sh_string_new_from_lchar(message, sl_strlen(message));
602 if (!theMsg)
603 {
604 SL_RETURN((-1), _("sh_mail_msg"));
605 }
606
607 /* ---------- Header ---------------------------------------- */
608
609 if (mail_subject == NULL)
610 {
611 (void) sl_strlcpy(mheader, _("Subject: "), sizeof(mheader)-5);
612 (void) sl_strlcat(mheader,
613 sh_unix_time (0, timebuf, sizeof(timebuf)),
614 sizeof(mheader)-5);
615 (void) sl_strlcat(mheader, " ", sizeof(mheader)-5);
616 (void) sl_strlcat(mheader, sh.host.name, sizeof(mheader)-5);
617 }
618 else
619 {
620
621 if (message)
622 {
623 sh_mail_get_subject(message, mheader, sizeof(mheader)-5);
624 }
625 else
626 {
627 (void) sl_strlcpy(mheader, _("Subject: "), sizeof(mheader)-5);
628 (void) sl_strlcat(mheader,
629 sh_unix_time (0, timebuf, sizeof(timebuf)),
630 sizeof(mheader)-5);
631 (void) sl_strlcat(mheader, " ", sizeof(mheader)-5);
632 (void) sl_strlcat(mheader, sh.host.name, sizeof(mheader)-5);
633 }
634 }
635
636 /* RFC 821: Header is terminated by an empty line
637 */
638 (void) sl_strlcat(mheader, "\015\012\015\012", sizeof(mheader));
639
640 /* ---------- Message --------------------------------------- */
641
642 (void) sl_strlcpy(subject, sh_unix_time (0, timebuf, sizeof(timebuf)),
643 sizeof(subject));
644 (void) sl_strlcat(subject, " ", sizeof(subject));
645 (void) sl_strlcat(subject, sh.host.name, sizeof(subject));
646 (void) sl_strlcat(subject, "\r\n", sizeof(subject));
647
648
649 mailMsg = sh_string_new (SH_MAILBUF);
650 compMsg = sh_string_new (SH_MAILBUF);
651
652 mailMsg = sh_string_add_from_char(mailMsg, mheader);
653 mailMsg = sh_string_add_from_char(mailMsg,
654 _("-----BEGIN MESSAGE-----\r\n"));
655
656 mailMsg = sh_string_add_from_char(mailMsg, subject);
657 mailMsg = sh_string_add (mailMsg, theMsg);
658 mailMsg = sh_string_add_from_char(mailMsg, "\r\n");
659
660 /* ---------- Compressed Message ---------------------------- */
661
662 compMsg = sh_string_add_from_char(compMsg, subject);
663 compMsg = sh_string_add (compMsg, theMsg);
664 compMsg = sh_string_add_from_char(compMsg, "\r\n");
665
666 bufcompress = SH_ALLOC(sh_string_len(compMsg) + KEY_LEN + 1);
667 bufcompress[0] = '\0';
668
669 compressed = sh_util_compress (bufcompress,
670 sh_string_str(compMsg),
671 sh_string_len(compMsg) + 1);
672
673 /* ---------- Connect ---------------------------------------- */
674
675 errcount = 0;
676
677 if (sh_mail_all_in_one == S_FALSE)
678 {
679 struct alias * address_list;
680
681 address_list = all_recipients;
682
683 while (address_list)
684 {
685 if (address_list->send_mail == 1)
686 {
687 connfile = sh_mail_start_conn (address_list,
688 &ma_socket, &address_num);
689
690 if (NULL != connfile)
691 {
692 wrlen = fwrite (sh_string_str(mailMsg), 1,
693 sh_string_len(mailMsg), connfile);
694 wrlen -= sh_string_len(mailMsg);
695
696 if (wrlen == 0)
697 {
698 sh_string * sigMsg = sh_string_new (0);
699
700 sigMsg = sh_mail_signature_block (sigMsg,
701 sh_string_str(address_list->recipient),
702 bufcompress);
703
704 wrlen = fwrite (sh_string_str(sigMsg), 1,
705 sh_string_len(sigMsg), connfile);
706 wrlen -= sh_string_len(sigMsg);
707
708 sh_string_destroy(&sigMsg);
709 }
710
711 if (wrlen == 0)
712 status = sh_mail_end_conn (connfile, ma_socket);
713 else
714 status = -1;
715 }
716 if (NULL == connfile || status != 0)
717 {
718 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_SRV_FAIL,
719 _("mail"),
720 sh_string_str(address_list->recipient));
721 ++errcount;
722 ++sh.statistics.mail_failed;
723 }
724 else
725 {
726 ++sh.statistics.mail_success;
727 }
728
729 if (connfile != NULL)
730 {
731 (void) sl_fclose (FIL__, __LINE__, connfile);
732 connfile = NULL;
733 }
734 }
735 address_list = address_list->all_next;
736 }
737 }
738 else
739 {
740 connfile = sh_mail_start_conn (NULL, &ma_socket, &address_num);
741
742 if (NULL != connfile)
743 {
744 wrlen = fwrite (sh_string_str(mailMsg), 1,
745 sh_string_len(mailMsg), connfile);
746 wrlen -= sh_string_len(mailMsg);
747
748 if (wrlen == 0)
749 {
750 sh_string * sigMsg = sh_string_new (0);
751
752 sigMsg = sh_mail_signature_block (sigMsg,
753 NULL,
754 bufcompress);
755
756 wrlen = fwrite (sh_string_str(sigMsg), 1,
757 sh_string_len(sigMsg), connfile);
758 wrlen -= sh_string_len(sigMsg);
759
760 sh_string_destroy(&sigMsg);
761 }
762
763 if (wrlen == 0)
764 status = sh_mail_end_conn (connfile, ma_socket);
765 else
766 status = -1;
767 }
768
769 if (NULL == connfile || status != 0)
770 {
771 struct alias* ma_address = all_recipients;
772
773 while (ma_address)
774 {
775 if (ma_address->send_mail == 1)
776 break;
777 ma_address = ma_address->all_next;
778 }
779
780 if (ma_address)
781 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_SRV_FAIL,
782 _("mail"),
783 sh_string_str(ma_address->recipient));
784 errcount = address_num;
785 ++sh.statistics.mail_failed;
786 }
787 else
788 {
789 ++sh.statistics.mail_success;
790 }
791
792 if (connfile != NULL)
793 {
794 (void) sl_fclose (FIL__, __LINE__, connfile);
795 connfile = NULL;
796 }
797 }
798
799 memset (bufcompress, 0, compressed);
800 SH_FREE(bufcompress);
801
802 memset (sh_string_str(mailMsg), 0, sh_string_len(mailMsg));
803 memset (sh_string_str(compMsg), 0, sh_string_len(compMsg));
804 memset (sh_string_str(theMsg), 0, sh_string_len(theMsg));
805
806 sh_string_destroy(&mailMsg);
807 sh_string_destroy(&compMsg);
808 sh_string_destroy(&theMsg);
809
810 /* --- Stay responsible for delivery in case of failure --- */
811
812 if (errcount == address_num)
813 {
814 rollback_list(fifo_mail);
815 retval = -3;
816 }
817 else
818 {
819 mark_list(fifo_mail);
820 }
821
822 if (errcount == address_num)
823 {
824 fail_time = time(NULL);
825 SL_RETURN((retval), _("sh_mail_msg"));
826 }
827
828 success_time = time(NULL);
829 failcount = 0;
830
831 SL_RETURN((0), _("sh_mail_msg"));
832}
833
834
835/*
836 *
837 * SMTP CODE BELOW
838 *
839 *
840 */
841
842#include <ctype.h>
843#ifdef HOST_IS_HPUX
844#define _XOPEN_SOURCE_EXTENDED
845#endif
846#include <netdb.h>
847#include <sys/types.h>
848#include <sys/socket.h>
849#include <netinet/in.h>
850#ifndef S_SPLINT_S
851#include <arpa/inet.h>
852#else
853#define AF_INET 2
854#endif
855
856#define SH_NEED_GETHOSTBYXXX
857#include "sh_static.h"
858
859/* missing on HP-UX 10.20 */
860#ifndef IPPORT_SMTP
861#define IPPORT_SMTP 25
862#endif
863
864static int sh_mail_wait(int code, int ma_socket);
865
866static char * relay_host = NULL;
867
868int sh_mail_set_relay (const char * str_s)
869{
870 SL_ENTER(_("sh_mail_set_relay"));
871
872 if (str_s == NULL)
873 SL_RETURN( -1, _("sh_mail_set_relay"));
874
875 if (relay_host != NULL)
876 {
877 SH_FREE (relay_host);
878 relay_host = NULL;
879 }
880
881 if (0 == sl_strncmp(str_s, _("NULL"), 4))
882 {
883 SL_RETURN( 0, _("sh_mail_set_relay"));
884 }
885
886 relay_host = sh_util_strdup(str_s);
887
888 SL_RETURN( 0, _("sh_mail_set_relay"));
889}
890
891static char * mail_sender = NULL;
892
893int sh_mail_set_sender (const char *str)
894{
895 if (mail_sender != NULL)
896 {
897 SH_FREE (mail_sender);
898 mail_sender = NULL;
899 }
900 if (str != NULL)
901 {
902 mail_sender = sh_util_strdup (str);
903 }
904 if (mail_sender == NULL)
905 {
906 return -1;
907 }
908 return 0;
909}
910
911static int sh_mail_port = IPPORT_SMTP;
912
913int sh_mail_set_port (const char * str)
914{
915 int i = atoi (str);
916
917 SL_ENTER(_("sh_mail_set_port"));
918
919 if (i >= 0 && i < 65535)
920 {
921 sh_mail_port = i;
922 }
923 else
924 {
925 sh_mail_port = IPPORT_SMTP;
926 SL_RETURN ((-1), _("sh_mail_set_port"));
927 }
928
929 SL_RETURN( (0), _("sh_mail_set_port"));
930}
931
932/*************************
933 *
934 * start connection
935 * for details on SMTP, see RFC 821
936 *
937 * If ma_address == NULL, will send to all marked with
938 * send_mail=1 in recipient list, else to ma_address.
939 */
940
941static time_t time_wait = 300;
942static void report_smtp (char * reply);
943
944static FILE * sh_mail_start_conn (struct alias * ma_address,
945 int * ma_socket, int * anum)
946{
947 char * address;
948 int aFlag = 0;
949
950 int ecount;
951
952 char this_address[256];
953 char ma_machine[256];
954 char ma_user[256];
955 char error_msg[256];
956 char error_call[SH_MINIBUF];
957 int error_num = 0;
958 register int i, j, k;
959 FILE * connFile = NULL;
960 struct tm * my_tm;
961#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_LOCALTIME_R)
962 struct tm time_tm;
963#endif
964 time_t my_time;
965 char my_tbuf[128];
966
967 int fd;
968
969 dnsrep * answers;
970 mx * result;
971
972 SL_ENTER(_("sh_mail_start_conn"));
973
974 *ma_socket = -1;
975 time_wait = 300;
976
977 if (ma_address == NULL)
978 {
979 aFlag = 1;
980 ma_address = all_recipients;
981
982 while (ma_address)
983 {
984 if (ma_address->send_mail == 1)
985 break;
986 ma_address = ma_address->all_next;
987 }
988 }
989
990 if (!ma_address)
991 {
992 SL_RETURN( NULL, _("sh_mail_start_conn"));
993 }
994
995 address = sh_string_str(ma_address->recipient);
996
997 TPT(( 0, FIL__, __LINE__, _("msg=<address %s>\n"),
998 address));
999
1000 /* ------- split adress ------------------ */
1001
1002 if (strchr (address, '@') == NULL) {
1003 (void) sl_strlcpy(ma_user, address, 256);
1004 (void) sl_strlcpy(ma_machine, _("localhost"), 256);
1005 } else {
1006 i = 0;
1007 while (i < 255 && address[i] != '@') {
1008 ma_user[i] = address[i];
1009 ++i;
1010 }
1011
1012 /* adress[i] = '@'
1013 */
1014 ma_user[i] = '\0';
1015 j = i + 1; k = i; i = 0;
1016 while (i < 255 && address[i+j] != '\0') {
1017 ma_machine[i] = address[i+j];
1018 ++i;
1019 }
1020 ma_machine[i] = '\0';
1021 if (address[k] != '@' || address[k+i+1] != '\0')
1022 {
1023 SL_RETURN( NULL, _("sh_mail_start_conn"));
1024 }
1025 }
1026
1027
1028 if (relay_host != NULL)
1029 {
1030 (void) sl_strlcpy (ma_machine, relay_host, sizeof(ma_machine));
1031 TPT((0, FIL__, __LINE__, _("msg=<user %s machine %s>\n"),
1032 ma_user, ma_machine));
1033 fd = connect_port (ma_machine, sh_mail_port,
1034 error_call, &error_num, error_msg, 256);
1035 }
1036 else
1037 {
1038 answers = ma_address->mx_list;
1039 if (!answers)
1040 {
1041 answers = return_mx (ma_machine);
1042 ma_address->mx_list = answers;
1043 }
1044
1045 if (answers)
1046 {
1047 result = answers->reply;
1048 fd = -1;
1049 for (i = 0; i < answers->count; ++i)
1050 {
1051 (void) sl_strlcpy(ma_machine, result[i].address,
1052 sizeof(ma_machine));
1053 TPT((0, FIL__, __LINE__,
1054 _("msg=<user %s mx %s pref %d>\n"),
1055 ma_user, ma_machine, result[i].pref));
1056 fd = connect_port (ma_machine, sh_mail_port,
1057 error_call, &error_num, error_msg, 256);
1058 if (fd >= 0)
1059 break;
1060 }
1061 }
1062 else
1063 {
1064 (void) sl_strlcpy(error_call, _("return_mx"), SH_MINIBUF);
1065 (void) sl_strlcpy(error_msg, _("The specified host is unknown: "),
1066 256);
1067 (void) sl_strlcat(error_msg, ma_machine, 256);
1068 fd = -1;
1069 }
1070 }
1071
1072
1073 if (fd < 0)
1074 {
1075 sh_error_handle ((-1), FIL__, __LINE__, error_num,
1076 MSG_E_NET, error_msg, error_call,
1077 _("email"), ma_machine);
1078 SL_RETURN( NULL, _("sh_mail_start_conn"));
1079 }
1080
1081 /* associate a FILE structure with it
1082 */
1083 connFile = fdopen (fd, "r+");
1084 if (connFile == NULL)
1085 {
1086 TPT(( 0, FIL__, __LINE__, _("msg=<fdopen() failed>\n")));
1087 (void) sl_close_fd(FIL__, __LINE__, fd);
1088 SL_RETURN( NULL, _("sh_mail_start_conn"));
1089 }
1090
1091
1092 /* say HELO to the other socket
1093 */
1094 if (0 == sh_mail_wait (220, fd))
1095 {
1096 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET,
1097 _("Timeout on SMTP session init"),
1098 _("sh_mail_start_conn"),
1099 _("mail"), sh.host.name);
1100 TPT(( 0, FIL__, __LINE__, _("msg=<Timeout>\n")));
1101 (void) sl_fclose(FIL__, __LINE__, connFile);
1102 SL_RETURN( NULL, _("sh_mail_start_conn"));
1103 }
1104
1105 (void) fflush(connFile);
1106
1107 if (0 != sh_ipvx_is_numeric(sh.host.name))
1108 {
1109 sl_snprintf(error_msg, sizeof(error_msg), "HELO [%s]",
1110 sh.host.name);
1111 }
1112 else
1113 {
1114 sl_snprintf(error_msg, sizeof(error_msg), "HELO %s",
1115 sh.host.name);
1116 }
1117 report_smtp(error_msg);
1118
1119 if (0 != sh_ipvx_is_numeric(sh.host.name))
1120 fprintf(connFile, _("HELO [%s]%c%c"), sh.host.name, 13, 10);
1121 else
1122 fprintf(connFile, _("HELO %s%c%c"), sh.host.name, 13, 10);
1123
1124 (void) fflush(connFile);
1125
1126 if (0 == sh_mail_wait(250, fd))
1127 {
1128 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET,
1129 _("HELO failed"), _("sh_mail_start_conn"),
1130 _("mail"), sh.host.name);
1131
1132 TPT(( 0, FIL__, __LINE__, _("msg=<Timeout.>\n")));
1133 (void) sl_fclose(FIL__, __LINE__, connFile);
1134 SL_RETURN( NULL, _("sh_mail_start_conn"));
1135 }
1136
1137 /* tell them who we are
1138 */
1139 (void) sl_strlcpy (this_address,
1140 mail_sender ? mail_sender : DEFAULT_SENDER, 256);
1141 if (NULL == strchr(this_address, '@'))
1142 {
1143 (void) sl_strlcat (this_address, "@", 256);
1144 if (0 != sh_ipvx_is_numeric(sh.host.name))
1145 (void) sl_strlcat (this_address, _("example.com"), 256);
1146 else
1147 (void) sl_strlcat (this_address, sh.host.name, 256);
1148 }
1149
1150 sl_snprintf(error_msg, sizeof(error_msg), "MAIL FROM:<%s>",
1151 this_address);
1152 report_smtp(error_msg);
1153
1154 (void) fflush(connFile);
1155 /*@-usedef@*/
1156 fprintf(connFile, _("MAIL FROM:<%s>%c%c"), this_address, 13, 10);
1157 /*@+usedef@*/
1158 (void) fflush(connFile);
1159
1160 if (0 == sh_mail_wait(250, fd))
1161 {
1162 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET,
1163 _("MAIL FROM failed"), _("sh_mail_start_conn"),
1164 _("mail"), this_address);
1165 TPT(( 0, FIL__, __LINE__, _("msg=<Timeout.>\n")));
1166 (void) sl_fclose(FIL__, __LINE__, connFile);
1167 SL_RETURN( NULL, _("sh_mail_start_conn"));
1168 }
1169
1170 /* tell them who to send mail to
1171 */
1172 if (aFlag == 0)
1173 {
1174 sl_snprintf(error_msg, sizeof(error_msg), "RCPT TO:<%s>",
1175 address);
1176 report_smtp(error_msg);
1177
1178 (void) fflush(connFile);
1179 fprintf(connFile, _("RCPT TO:<%s>%c%c"), address, 13, 10);
1180 (void) fflush(connFile);
1181
1182 if (0 == sh_mail_wait(250, fd))
1183 {
1184 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET,
1185 _("RCPT TO failed"), _("sh_mail_start_conn"),
1186 _("mail"), address);
1187 TPT(( 0, FIL__, __LINE__, _("msg=<Timeout.>\n")));
1188 (void) sl_fclose(FIL__, __LINE__, connFile);
1189 SL_RETURN( NULL, _("sh_mail_start_conn"));
1190 }
1191 *anum = 1;
1192 }
1193 else
1194 {
1195 int address_num = 0;
1196 ecount = 0;
1197
1198 ma_address = all_recipients;
1199
1200 while (ma_address)
1201 {
1202 if (ma_address->send_mail != 1)
1203 {
1204 ma_address = ma_address->next;
1205 continue;
1206 }
1207
1208 ++address_num;
1209
1210 sl_snprintf(error_msg, sizeof(error_msg), "RCPT TO:<%s>",
1211 sh_string_str(ma_address->recipient));
1212 report_smtp(error_msg);
1213
1214 (void) fflush(connFile);
1215 fprintf(connFile, _("RCPT TO:<%s>%c%c"),
1216 sh_string_str(ma_address->recipient), 13, 10);
1217 (void) fflush(connFile);
1218
1219 if (0 == sh_mail_wait(250, fd))
1220 {
1221 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET,
1222 _("RCPT TO failed"), _("sh_mail_start_conn"),
1223 _("mail"), sh_string_str(ma_address->recipient));
1224
1225 TPT(( 0, FIL__, __LINE__, _("msg=<Timeout.>\n")));
1226 ++ecount;
1227 }
1228 ma_address = ma_address->next;
1229 }
1230
1231 *anum += address_num;
1232
1233 if (ecount == address_num)
1234 {
1235 (void) sl_fclose(FIL__, __LINE__, connFile);
1236 SL_RETURN( NULL, _("sh_mail_start_conn"));
1237 }
1238 }
1239
1240 /* Send the message
1241 */
1242 report_smtp(_("DATA"));
1243
1244 (void) fflush(connFile);
1245 fprintf(connFile, _("DATA%c%c"), 13, 10);
1246 (void) fflush(connFile);
1247
1248 if (0 == sh_mail_wait(354, fd))
1249 {
1250 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET,
1251 _("DATA failed"), _("sh_mail_start_conn"),
1252 _("mail"), address);
1253 TPT(( 0, FIL__, __LINE__, _("msg=<Timeout.>\n")));
1254 (void) sl_fclose(FIL__, __LINE__, connFile);
1255 SL_RETURN( NULL, _("sh_mail_start_conn"));
1256 }
1257
1258
1259 my_time = time(NULL);
1260#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_LOCALTIME_R)
1261 my_tm = localtime_r(&my_time, &time_tm);
1262#else
1263 my_tm = localtime(&my_time);
1264#endif
1265
1266 if (!my_tm)
1267 {
1268 sl_strlcpy(my_tbuf, _("Thu, 01 Jan 1970 00:00:00 +00:00"), sizeof(my_tbuf));
1269 }
1270 else
1271 {
1272#if defined(HAVE_STRFTIME_Z)
1273 (void) strftime(my_tbuf, 127, _("%a, %d %b %Y %H:%M:%S %z"), my_tm);
1274#else
1275 (void) strftime(my_tbuf, 127, _("%a, %d %b %Y %H:%M:%S %Z"), my_tm);
1276#endif
1277 }
1278
1279 TPT(( 0, FIL__, __LINE__, _("msg=<From: <%s>%c%cTo: <%s>%c%cDate: %s>%c%c"),
1280 this_address, 13, 10, address, 13, 10, my_tbuf, 13, 10));
1281
1282 report_smtp(_("sending data.."));
1283
1284 (void) fflush(connFile);
1285 fprintf(connFile,
1286 _("From: <%s>%c%c"\
1287 "To: <%s>%c%c"\
1288 "Date: %s%c%c"),
1289 this_address, 13, 10,
1290 address, 13, 10,
1291 my_tbuf, 13, 10);
1292
1293 *ma_socket = fd;
1294 SL_RETURN( connFile, _("sh_mail_start_conn"));
1295}
1296
1297/*************************
1298 *
1299 * end connection
1300 *
1301 */
1302
1303static int sh_mail_end_conn (FILE * connFile, int fd)
1304{
1305 SL_ENTER(_("sh_mail_end_conn"));
1306
1307 time_wait = 300;
1308
1309 report_smtp(_("."));
1310
1311 (void) fflush(connFile);
1312 fprintf(connFile, _("%c%c.%c%c"), 13, 10, 13, 10);
1313 (void) fflush(connFile);
1314
1315 if (0 != sh_mail_wait(250, fd))
1316 {
1317 (void) fflush(connFile);
1318 fprintf(connFile, _("QUIT%c%c"), 13, 10);
1319 (void) fflush(connFile);
1320 TPT(( 0, FIL__, __LINE__, _("msg=<exit>\n")));
1321
1322 SL_RETURN (0, _("sh_mail_end_conn"));
1323 }
1324
1325 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET,
1326 _("QUIT failed"), _("sh_mail_end_conn"),
1327 _("mail"), _("SMTP server"));
1328
1329 TPT(( 0, FIL__, __LINE__, _("msg=<abnormal exit>\n")));
1330
1331 SL_RETURN ((-1), _("sh_mail_end_conn"));
1332}
1333
1334
1335
1336/****************************
1337 *
1338 * Handle server replies
1339 *
1340 *
1341 */
1342extern int flag_err_debug;
1343
1344static void report_smtp (char * reply)
1345{
1346 char * tmp;
1347
1348 if (flag_err_debug == S_TRUE)
1349 {
1350 tmp = sh_util_safe_name_keepspace(reply);
1351
1352 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
1353 tmp,
1354 _("report_smtp") );
1355 SH_FREE(tmp);
1356 }
1357 return;
1358}
1359
1360
1361static int sh_mail_wait(int code, int ma_socket)
1362{
1363 int rcode, g;
1364
1365 char c;
1366
1367 char errmsg[194];
1368 char reply[128];
1369 unsigned int ireply = 0;
1370
1371 enum {
1372 WAIT_CODE_START,
1373 WAIT_CODE,
1374 WAIT_NL,
1375 WAIT_NL_CONT
1376 } state;
1377
1378 time_t waited_time;
1379
1380 SL_ENTER(_("mail_wait"));
1381
1382 waited_time = time(NULL);
1383
1384 /* timeout after 5 minutes
1385 */
1386
1387 rcode = 0;
1388 state = WAIT_CODE_START;
1389 reply[0] = '\0';
1390
1391 while (sl_read_timeout_fd (ma_socket, &c, 1, time_wait, S_FALSE) > 0) {
1392
1393 if (ireply < (sizeof(reply) - 1))
1394 {
1395 if (c != '\n' && c != '\r')
1396 {
1397 reply[ireply] = c;
1398 ++ireply;
1399 reply[ireply] = '\0';
1400 }
1401 }
1402
1403 g = (int) c;
1404
1405 /*
1406 if (g == EOF)
1407 {
1408 TPT((0, FIL__, __LINE__, _("msg=<mail_wait: EOF>\n")));
1409 SL_RETURN( 0, _("mail_wait"));
1410 }
1411 */
1412
1413 switch(state) {
1414
1415 /* wait for start of a numerical code
1416 */
1417 case WAIT_CODE_START:
1418 if (0 != isspace(g))
1419 break; /* Skip white space */
1420 if (0 == isdigit(g))
1421 {
1422 report_smtp(reply);
1423 SL_RETURN( 0, _("mail_wait")); /* No leading number */
1424 }
1425 rcode = g-(int)'0'; /* convert to number */
1426 state = WAIT_CODE;
1427 break;
1428
1429 /* wait for completion of numerical code
1430 */
1431 case WAIT_CODE:
1432 if (0 != isdigit(g)) {
1433 rcode = rcode * 10 + (g-(int)'0'); /* next digit */
1434 break;
1435 }
1436 /*@+charintliteral@*/
1437 state = ((g == '-') ? WAIT_NL_CONT : WAIT_NL);
1438 /*@-charintliteral@*/
1439 break;
1440
1441 /* wait for newline, then return with status code
1442 */
1443 case WAIT_NL:
1444 /*@+charintliteral@*/
1445 if (g != '\n')
1446 break;
1447 /*@-charintliteral@*/
1448
1449 TPT((0, FIL__, __LINE__,
1450 _("msg=<mail_wait: OK got %d (%d) need %d (%d)>\n"),
1451 rcode, (int)(rcode/100), code, (int)(code/100) ));
1452 g = ((int)(rcode/100) == (int)(code/100)) ? 1 : 0;
1453 if (g != 1)
1454 {
1455 char * tmp = sh_util_safe_name_keepspace(reply);
1456 sl_snprintf(errmsg, sizeof(errmsg),
1457 _("Bad response (%s), expected %d"), tmp, code);
1458 SH_FREE(tmp);
1459
1460 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET,
1461 errmsg, _("sh_mail_wait"),
1462 _("mail"), _("SMTP server"));
1463 }
1464 else
1465 {
1466 report_smtp(reply);
1467 }
1468 waited_time = time(NULL) - waited_time;
1469 time_wait -= waited_time;
1470 TPT((0, FIL__, __LINE__,
1471 _("msg=<mail_wait: time_wait reduced to %d sec>\n"),
1472 (int) time_wait));
1473 SL_RETURN( (g), _("mail_wait")) ;
1474
1475 /* wait for continuation line
1476 */
1477 /*@fallthrough@*//* no, but splint doesn't understand */
1478 case WAIT_NL_CONT:
1479 /*@+charintliteral@*/
1480 if (g == '\n')
1481 state = WAIT_CODE_START; /* There is a continuation line */
1482 /*@-charintliteral@*/
1483 break;
1484
1485 default:
1486
1487 TPT((0, FIL__, __LINE__, _("msg=<mail_wait: bad>\n")));
1488 report_smtp(reply);
1489 SL_RETURN( 0, _("mail_wait"));
1490
1491 }
1492 }
1493
1494 TPT((0, FIL__, __LINE__, _("msg=<mail_wait: failed>\n")));
1495
1496 /* Failed, EOF or error on socket */
1497 report_smtp(reply);
1498 SL_RETURN( 0, _("mail_wait"));
1499}
1500
1501/* -- function to insert "\r\n" after each 998 chars --
1502 */
1503
1504#define SPLIT_AT 998
1505
1506static char * split_string(const char * str)
1507{
1508 size_t size;
1509 size_t blocks;
1510 int count = 0;
1511
1512 char * p, * p0;
1513 const char * q;
1514
1515 if (!str)
1516 return NULL;
1517
1518 size = strlen(str) + 1;
1519 blocks = 1 + (size / SPLIT_AT);
1520
1521 if (sl_ok_muls(2, blocks) && sl_ok_adds(size, (2*blocks)))
1522 {
1523 size = size + (2*blocks);
1524 }
1525 else
1526 {
1527 /* integer overflow, do not split */
1528 p = sh_util_strdup(str);
1529 return p;
1530 }
1531
1532 p = SH_ALLOC(size);
1533 memset(p, 0, size);
1534
1535 p0 = p;
1536
1537 q = str;
1538 while (*q != '\0') {
1539 *p = *q;
1540 ++p;
1541 ++q;
1542 ++count;
1543 if (0 == (count % SPLIT_AT)) {
1544 count = 0;
1545 *p = '\r';
1546 ++p;
1547 *p = '\n';
1548 ++p;
1549 }
1550 }
1551 /* fprintf(stderr, "used = %d\n", strlen(p0)); */
1552
1553 return p0;
1554}
1555
1556
1557
1558/*****************************************************************
1559 *
1560 * MX Resolver Routines
1561 *
1562 *****************************************************************/
1563
1564#if defined(HAVE_ARPA_NAMESER_H)
1565
1566#include <netinet/in.h>
1567#ifdef __APPLE__
1568#define BIND_8_COMPAT 1
1569#endif
1570#ifndef S_SPLINT_S
1571#include <arpa/nameser.h>
1572#include <resolv.h>
1573#endif
1574#include <netdb.h>
1575#include <sys/socket.h>
1576#ifndef S_SPLINT_S
1577#include <arpa/inet.h>
1578#endif
1579
1580#include "sh_tools.h"
1581
1582#ifndef HFIXEDSZ
1583#define HFIXEDSZ 12
1584#endif
1585#ifndef QFIXEDSZ
1586#define QFIXEDSZ 4
1587#endif
1588
1589/*@unused@*//* used in get_mx() which is not parsed by splint */
1590static unsigned int get_short (unsigned char * loc)
1591{
1592 unsigned int retval = 0;
1593 if (loc)
1594 {
1595 /* byte order: MSB first
1596 */
1597 /*@+charint@*/
1598 retval = (((unsigned char) * loc) * 256) | ((unsigned char) * (loc + 1));
1599 /*@-charint@*/
1600 }
1601 return (retval);
1602}
1603
1604/* parser errors with splint */
1605#ifndef S_SPLINT_S
1606static dnsrep * get_mx (char *hostname)
1607{
1608 int ret, length, status;
1609 mx * result;
1610 size_t len;
1611
1612 typedef union
1613 {
1614 HEADER head;
1615 unsigned char buffer[4096];
1616 } querybuf;
1617
1618 querybuf * reply;
1619 char expanded[1024];
1620 unsigned char * comp_dn, * eom;
1621 HEADER * header;
1622 int type, rdlength, pref;
1623 unsigned int count, theindex;
1624 dnsrep * retval;
1625
1626 SL_ENTER(_("get_mx"));
1627
1628 if (0 != res_init ())
1629 SL_RETURN (NULL, _("get_mx"));
1630
1631 reply = SH_ALLOC(sizeof(querybuf));
1632
1633 errno = 0;
1634 length = res_query (hostname, C_IN, T_MX,
1635 (unsigned char *) reply, 4095);
1636
1637 if (length < 1)
1638 {
1639 char errbuf[SH_ERRBUF_SIZE];
1640
1641 /* error handling
1642 */
1643 if (length == -1)
1644 {
1645 if (errno == ECONNREFUSED)
1646 status = ECONNREFUSED;
1647 else
1648 status = h_errno;
1649
1650#ifdef FIL__
1651 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN,
1652 (errno == ECONNREFUSED) ?
1653 sh_error_message (status, errbuf, sizeof(errbuf)) :
1654 sh_tools_errmessage(status, errbuf, sizeof(errbuf)),
1655 _("res_query"));
1656#else
1657 if (errno == ECONNREFUSED)
1658 fprintf(stderr, " ERROR: %s: \n", strerror(errno)); /* TESTONLY */
1659 else
1660 fprintf(stderr, "HERROR: %s\n", hstrerror(h_errno));/* TESTONLY */
1661#endif
1662 }
1663 SH_FREE(reply);
1664 SL_RETURN (NULL, _("get_mx"));
1665 }
1666
1667
1668 header = (HEADER *) reply;
1669
1670 /* start of data section
1671 */
1672 comp_dn = (unsigned char *) reply + HFIXEDSZ;
1673
1674 /* end-of-message
1675 */
1676 eom = (unsigned char *) reply + length;
1677
1678 /* HEADER NAME -- must be skipped or decompressed
1679 * TYPE -- type of data we got back, 16 bit integer
1680 * CLASS -- class we got back, also a 16 bit integer
1681 * TTL -- 32 bit time-to-live. just skip this
1682 * RDLENGTH -- length of the data to follow
1683 * RDATA -- the data:
1684 * PREF -- 16 bit preference
1685 * MX -- name of mail exchanger, must be decompressed
1686 */
1687
1688 /* Skip the query data.
1689 * QDCOUNT is the number of entries (unsigned 16 bit int).
1690 */
1691 count = ntohs (header->qdcount);
1692 for (theindex = 0; theindex < count; ++theindex)
1693 {
1694 ret = dn_skipname (comp_dn, eom);
1695 comp_dn += ret + QFIXEDSZ;
1696 if (ret < 1 || comp_dn >= eom)
1697 {
1698 SH_FREE(reply);
1699 SL_RETURN (NULL, _("get_mx"));
1700 }
1701 }
1702
1703 count = ntohs (header->ancount);
1704 if (count < 1)
1705 {
1706 SH_FREE(reply);
1707 SL_RETURN (NULL, _("get_mx"));
1708 }
1709
1710 retval = SH_ALLOC (sizeof (dnsrep));
1711 if (!retval)
1712 {
1713 SH_FREE(reply);
1714 SL_RETURN (NULL, _("get_mx"));
1715 }
1716
1717 retval->count = count;
1718
1719 /* allocate space for the results */
1720
1721 if (!sl_ok_muls(count, sizeof (mx)))
1722 {
1723 SH_FREE(reply);
1724 SH_FREE (retval);
1725 SL_RETURN (NULL, _("get_mx"));
1726 }
1727
1728 result = SH_ALLOC (count * sizeof (mx));
1729
1730 if (!result)
1731 {
1732 SH_FREE(reply);
1733 SH_FREE (retval);
1734 SL_RETURN (NULL, _("get_mx"));
1735 }
1736 retval->reply = result;
1737
1738 do
1739 {
1740 /* HEADER NAME
1741 */
1742 ret = dn_expand ((unsigned char *) reply, eom, comp_dn,
1743 (char *) expanded, 1023);
1744 comp_dn += ret;
1745 if (ret < 1 || comp_dn >= eom)
1746 {
1747 SH_FREE(reply);
1748 SH_FREE (result);
1749 SH_FREE (retval);
1750 SL_RETURN (NULL, _("get_mx"));
1751 }
1752
1753 /* TYPE
1754 */
1755 type = get_short (comp_dn);
1756 comp_dn += 2;
1757 if (type != T_MX || comp_dn >= eom)
1758 {
1759 SH_FREE(reply);
1760 SH_FREE (result);
1761 SH_FREE (retval);
1762 SL_RETURN (NULL, _("get_mx"));
1763 }
1764
1765
1766 /* CLASS (re-use 'type' var)
1767 */
1768 /* type = get_short (comp_dn); *//* don't care */
1769 comp_dn += 2;
1770 if (comp_dn >= eom)
1771 {
1772 SH_FREE(reply);
1773 SH_FREE (result);
1774 SH_FREE (retval);
1775 SL_RETURN (NULL, _("get_mx"));
1776 }
1777
1778
1779 /* TTL
1780 */
1781 comp_dn += 4;
1782 if (comp_dn >= eom)
1783 {
1784 SH_FREE(reply);
1785 SH_FREE (result);
1786 SH_FREE (retval);
1787 SL_RETURN (NULL, _("get_mx"));
1788 }
1789
1790 /* RDLENGTH
1791 */
1792 rdlength = get_short (comp_dn);
1793 comp_dn += 2;
1794 if (rdlength < 1 || comp_dn >= eom)
1795 {
1796 SH_FREE(reply);
1797 SH_FREE (result);
1798 SH_FREE (retval);
1799 SL_RETURN (NULL, _("get_mx"));
1800 }
1801
1802 /* RDATA
1803 */
1804 pref = get_short (comp_dn);
1805 comp_dn += 2;
1806 if (comp_dn >= eom)
1807 {
1808 SH_FREE(reply);
1809 SH_FREE (result);
1810 SH_FREE (retval);
1811 SL_RETURN (NULL, _("get_mx"));
1812 }
1813
1814 ret = dn_expand ((unsigned char *) reply, eom, comp_dn,
1815 (char *) expanded, 1023);
1816 comp_dn += ret;
1817 if (ret < 1)
1818 {
1819 SH_FREE(reply);
1820 SH_FREE (result);
1821 SH_FREE (retval);
1822 SL_RETURN (NULL, _("get_mx"));
1823 }
1824 count--;
1825
1826 /* fill in the struct
1827 */
1828 result[count].pref = pref;
1829 len = strlen (expanded) + 1;
1830 result[count].address = SH_ALLOC (len);
1831 sl_strlcpy (result[count].address, expanded, len);
1832 }
1833 while (ret > 0 && comp_dn < eom && count);
1834
1835 SH_FREE(reply);
1836 SL_RETURN (retval, _("get_mx"));
1837}
1838/* ifndef S_SPLINT_S */
1839#endif
1840
1841/* #if defined(HAVE_ARPA_NAMESER_H) */
1842#endif
1843
1844
1845static int comp_mx_pref (const void * a, const void * b)
1846{
1847 const mx * ax = (const mx *) a;
1848 const mx * bx = (const mx *) b;
1849
1850 if (ax->pref > bx->pref)
1851 return 1;
1852 else if (ax->pref < bx->pref)
1853 return -1;
1854 else
1855 return 0;
1856}
1857
1858/*
1859 * return_mx returns a list of valid mail exchangers for domain
1860 */
1861static dnsrep * return_mx (char *domain)
1862{
1863 dnsrep * answers = NULL;
1864 mx * result;
1865 dnsrep * retval;
1866 char * address = NULL;
1867 char errmsg[128];
1868
1869 SL_ENTER(_("return_mx"));
1870
1871#if defined(HAVE_ARPA_NAMESER_H)
1872 if (domain != NULL)
1873 answers = /*@-unrecog@*/get_mx (domain)/*@+unrecog@*/;
1874#endif
1875
1876 if (answers != NULL && answers->count > 0)
1877 {
1878 qsort(answers->reply, (size_t) answers->count, sizeof(mx),
1879 comp_mx_pref);
1880 SL_RETURN (answers, _("return_mx"));
1881 }
1882 else
1883 {
1884 char numeric[SH_IP_BUF];
1885
1886 if (domain != NULL)
1887 {
1888#if defined(HAVE_ARPA_NAMESER_H)
1889#ifdef FIL__
1890 (void) sl_strlcpy (errmsg, _("No MX record for domain "), 127);
1891 (void) sl_strlcat (errmsg, domain, 127);
1892 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
1893 errmsg,
1894 _("get_mx"));
1895#else
1896 /* flawfinder: ignore *//* test code only */
1897 strcpy (errmsg, /* known to fit */
1898 _("No MX record for domain "));
1899 strncat (errmsg, domain, 100);
1900 errmsg[122] = '\0';
1901 fprintf(stderr, "Warning: %s\n", errmsg);
1902#endif
1903#endif
1904 }
1905
1906 retval = NULL;
1907
1908 if (domain != NULL)
1909 address = sh_ipvx_canonical(domain, numeric, sizeof(numeric));
1910
1911 if (address)
1912 {
1913 result = SH_ALLOC (sizeof (mx));
1914 retval = SH_ALLOC (sizeof (dnsrep));
1915 retval->reply = result;
1916 retval->count = 1;
1917 result->pref = 0;
1918
1919 result->address = address;
1920 }
1921 else
1922 {
1923#ifdef FIL__
1924 (void) sl_strlcpy (errmsg, _("Unknown host "), 127);
1925 (void) sl_strlcat (errmsg, domain, 127);
1926 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
1927 errmsg,
1928 _("return_mx"));
1929#endif
1930 SL_RETURN (NULL, _("return_mx"));
1931 }
1932
1933 SL_RETURN (retval, _("return_mx"));
1934 }
1935}
1936
1937int free_mx (dnsrep * answers)
1938{
1939 mx * result;
1940 int i;
1941
1942 SL_ENTER(_("free_mx"));
1943 if (!answers)
1944 SL_RETURN (0, _("return_mx"));
1945
1946 result = answers->reply;
1947 for (i = 0; i < answers->count; ++i)
1948 {
1949 SH_FREE (result[i].address);
1950 }
1951 SH_FREE(result);
1952 SH_FREE(answers);
1953 SL_RETURN (0, _("return_mx"));
1954}
1955
1956#ifdef TEST_ONLY
1957int main(int argc, char * argv[])
1958{
1959 int i;
1960 dnsrep * answers;
1961 mx * result;
1962
1963 if (argc < 2)
1964 {
1965 fprintf(stderr, "Usage: dns <hostname>\n");
1966 return -1;
1967 }
1968 answers = return_mx(argv[1]);
1969
1970 if (!answers)
1971 {
1972 fprintf(stderr, "No answer\n");
1973 return -1;
1974 }
1975
1976 if (answers->count > 0)
1977 {
1978 result = answers->reply;
1979 for (i = 0; i < answers->count; ++i)
1980 {
1981 fprintf(stderr, "Record %3d: [%3d] %s\n", i,
1982 result[i].pref, result[i].address);
1983 }
1984 }
1985 else
1986 {
1987 fprintf(stderr, "No answer\n");
1988 free_mx(answers);
1989 return -1;
1990 }
1991 free_mx(answers);
1992 return (0);
1993}
1994#endif
1995
1996
1997
1998/* if defined(SH_WITH_MAIL) */
1999#endif
2000
2001
2002
Note: See TracBrowser for help on using the repository browser.