source: trunk/src/sh_mail.c@ 216

Last change on this file since 216 was 216, checked in by katerina, 16 years ago

Fix ticket #138 (ability to specify SMTP port). Patch by lucas sizzo org.

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