/* SAMHAIN file system integrity testing */ /* Copyright (C) 1999, 2000 Rainer Wichmann */ /* */ /* This program is free software; you can redistribute it */ /* and/or modify */ /* it under the terms of the GNU General Public License as */ /* published by */ /* the Free Software Foundation; either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config_xor.h" #include #include #include #include #include #include #include #include #include #include #include #if defined(SH_WITH_MAIL) #if TIME_WITH_SYS_TIME #include #include #else #if HAVE_SYS_TIME_H #include #else #include #endif #endif #ifdef HAVE_MEMORY_H #include #endif #include "samhain.h" #include "sh_error.h" #include "sh_unix.h" #include "sh_tiger.h" #include "sh_mail.h" #include "sh_utils.h" #include "sh_fifo.h" #include "sh_tools.h" #include "sh_pthread.h" #undef FIL__ #define FIL__ _("sh_mail.c") #undef GOOD #undef BAD static int failedMail = SL_FALSE; /* MX Resolver Struct */ typedef struct mx_ { int pref; char * address; } mx; typedef struct dnsrep_ { int count; mx * reply; } dnsrep; static int free_mx (dnsrep * answers); static dnsrep * return_mx (char *domain); /********************************************* * utility function for verifying mails *********************************************/ typedef struct mail_trail_struct { char trail_id[2*SH_MINIBUF]; char trail_key[KEY_LEN+1]; struct mail_trail_struct * next; } mail_trail_type; static mail_trail_type * mail_trail = NULL; int sh_mail_sigverify (const char * s) { SL_TICKET fd; long i; char * buf; char * bufc; char key[81]; char number[2*SH_MINIBUF]; char audit_id[2 * SH_MINIBUF]; long numsig; char key2[KEY_LEN+1]; char * theSig; mail_trail_type * mail_trail_ptr = NULL; sh_error_logoff(); ASSERT((s != NULL && sl_strlen(s) < PATH_MAX), _("(s != NULL && sl_strlen(s) < PATH_MAX)")); if (s == NULL || sl_strlen(s) >= PATH_MAX) _exit (EXIT_FAILURE); /* open the file, then check it */ if (0 != sl_is_suid()) { fprintf(stderr, _("Cannot open file %s in suid mode\n"), s); _exit (EXIT_FAILURE); } if ( SL_ISERROR(fd = sl_open_read (s, SL_NOPRIV))) { fprintf(stderr, _("Could not open file %s\n"), s); _exit (EXIT_FAILURE); } buf = SH_ALLOC( (size_t)(SH_MSG_BUF+SH_BUFSIZE+1)); bufc = SH_ALLOC( (size_t)(SH_MSG_BUF+SH_MAXBUF+1)); while (1 == 1) { buf[0] = '\0'; bufc[0] = '\0'; /* find start of next message */ while (0 != sl_strncmp(buf, _("-----BEGIN MESSAGE-----"), sizeof("-----BEGIN MESSAGE-----")-1)) { (void) sh_unix_getline (fd, buf, SH_MSG_BUF+SH_BUFSIZE); if (buf[0] == '\0') { /* End of mailbox reached, exit. */ (void) fflush(stdout); _exit (EXIT_SUCCESS); /* Fix for AIX cc complaint. */ /*@notreached@*/ return 0; } } /* Read message, compress into bufc. */ while (1 == 1) { (void) sh_unix_getline (fd, buf, SH_MSG_BUF+SH_BUFSIZE); if (0 == sl_strncmp(buf, _("-----BEGIN SIGNATURE-----"), sizeof("-----BEGIN SIGNATURE-----")-1)) break; if (buf[0] == '\0') _exit (EXIT_FAILURE); (void) sh_util_compress(bufc, buf, SH_MSG_BUF+SH_MAXBUF-KEY_LEN); } /* get signature and number */ (void) sh_unix_getline (fd, key, (int)sizeof(key)); key[KEY_LEN] = '\0'; (void) sh_unix_getline (fd, number, (int)sizeof(number)); number[(2*SH_MINIBUF) - 2] = '\0'; numsig = atol (number); (void) sl_strlcpy (audit_id, &number[7], 2*SH_MINIBUF); fprintf(stderr, _("Message %06ld Trail %s\n"), numsig, /*@-usedef@*/ audit_id /*@+usedef@*/); mail_trail_ptr = mail_trail; while (mail_trail_ptr) { if (0 == sl_strcmp(mail_trail_ptr->trail_id, audit_id)) break; mail_trail_ptr = mail_trail_ptr->next; } if (!mail_trail_ptr) { if (numsig > 0) { fprintf (stderr, _("ERROR (no key -- cannot check)\n")); continue; } else { mail_trail_ptr = SH_ALLOC (sizeof(mail_trail_type)); mail_trail_ptr->next = mail_trail; mail_trail = mail_trail_ptr; (void) sl_strlcpy (mail_trail_ptr->trail_id, audit_id, 2*SH_MINIBUF); } } else if (numsig == 0) { fprintf (stderr, _("ERROR (repeated audit trail)\n")); continue; } if (numsig == 0) { sh_util_encode(key, bufc, 1, 'A'); (void) sl_strlcpy (mail_trail_ptr->trail_key, key, KEY_LEN+1); fprintf (stderr, _("(unchecked)\n")); } else { char sigbuf[KEYBUF_SIZE]; /* iterate key */ (void) sl_strlcpy(key2, mail_trail_ptr->trail_key, KEY_LEN+1); for (i = 0; i < numsig; ++i) { char hashbuf[KEYBUF_SIZE]; (void) sl_strlcpy (key2, sh_tiger_hash (key2, TIGER_DATA, KEY_LEN, hashbuf, sizeof(hashbuf)), KEY_LEN+1); } theSig = sh_util_siggen (key2, bufc, sl_strlen(bufc), sigbuf, sizeof(sigbuf)); if (sl_strncmp (key, theSig, KEY_LEN) != 0) { fprintf (stderr, _("(FAILED)\n")); } else { fprintf (stderr, _("(passed)\n")); } } } /* end scan mailbox */ /*@notreached@*/ } #define SH_FILT_NUM 32 #define SH_FILT_OR 0 #define SH_FILT_AND 1 #define SH_FILT_NOT 2 #define SH_FILT_INIT { 0, { NULL }, 0, { NULL }, 0, { NULL }} typedef struct _sh_filter_type { int for_c; char * for_v[SH_FILT_NUM]; int fand_c; char * fand_v[SH_FILT_NUM]; int fnot_c; char * fnot_v[SH_FILT_NUM]; } sh_filter_type; static int sh_filter_filteradd (const char * argstring, sh_filter_type * filter, int ftype) { int i = 0; int flag = 0; size_t s; char * dupp; char * p; char * end; int * ntok; char ** stok; SL_ENTER(_("sh_filter_filteradd")); if (NULL == argstring) { SL_RETURN((-1), _("sh_filter_filteradd")); } if (ftype == SH_FILT_OR) { ntok = &(filter->for_c); stok = filter->for_v; } else if (ftype == SH_FILT_AND) { ntok = &(filter->fand_c); stok = filter->fand_v; } else if (ftype == SH_FILT_NOT) { ntok = &(filter->fnot_c); stok = filter->fnot_v; } else { SL_RETURN((-1), _("sh_filter_filteradd")); } *ntok = 0; dupp = sh_util_strdup(argstring); p = dupp; do { while (*p == ',' || *p == ' ' || *p == '\t') ++p; if (*p == '\0') break; end = p; ++end; if (*end == '\0') break; if (*p == '\'') { ++p; end = p; ++end; if (*p == '\0' || *end == '\0') break; while (*end != '\0' && *end != '\'') ++end; } else if (*p == '"') { ++p; end = p; ++end; if (*p == '\0' || *end == '\0') break; while (*end != '\0' && *end != '"') ++end; } else { while (*end != '\0' && *end != ',' && *end != ' ' && *end != '\t') ++end; } if (*end == '\0') flag = 1; else *end = '\0'; s = strlen(p) + 1; if (stok[i] != NULL) SH_FREE(stok[i]); stok[i] = SH_ALLOC(s); (void) sl_strlcpy(stok[i], p, s); p = end; ++p; ++i; if (i == SH_FILT_NUM) break; } while (p != NULL && *p != '\0' && flag == 0); *ntok = i; SH_FREE(dupp); SL_RETURN (0, _("sh_filter_filteradd")); } /* * -- check filters */ static int sh_filter_filter (const char * message, sh_filter_type * filter) { int i; int j = 0; SL_ENTER(_("sh_mail_filter")); /* Presence of any of these keywords prevents execution. */ if (filter->fnot_c > 0) { for (i = 0; i < filter->fnot_c; ++i) { if (NULL != sl_strstr(message, filter->fnot_v[i])) { SL_RETURN ((-1), _("sh_filter_filter")); } } } /* Presence of all of these keywords is required for execution. */ if (filter->fand_c > 0) { j = 0; for (i = 0; i < filter->fand_c; ++i) if (NULL != sl_strstr(message, filter->fand_v[i])) ++j; if (j != filter->fand_c) { SL_RETURN ((-1), _("sh_filter_filter")); } } /* Presence of at least one of these keywords is required for execution. */ if (filter->for_c > 0) { for (i = 0; i < filter->for_c; ++i) { if (NULL != sl_strstr(message, filter->for_v[i])) { goto isok; } } SL_RETURN ((-1), _("sh_filter_filter")); } isok: SL_RETURN ((0), _("sh_filter_filter")); } static sh_filter_type mail_filter = SH_FILT_INIT; /* * -- add keywords to the OR filter */ int sh_mail_add_or (const char * str) { return (sh_filter_filteradd (str, &(mail_filter), SH_FILT_OR)); } /* * -- add keywords to the AND filter */ int sh_mail_add_and (const char * str) { return (sh_filter_filteradd (str, &(mail_filter), SH_FILT_AND)); } /* * -- add keywords to the NOT filter */ int sh_mail_add_not (const char * str) { return (sh_filter_filteradd (str, &(mail_filter), SH_FILT_NOT)); } static char * address_list[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static int address_num = 0; static int address_num_compiled = 0; static int setaddress_compiled = S_FALSE; void reset_count_dev_mail(void) { /* if not, then we still have the compiled-in address (if any), so we * don' touch them */ if (address_num_compiled == -99) address_num = 0; return; } int sh_mail_setaddress (const char * address) { char * p; SL_ENTER(_("sh_mail_setaddress")); if (0 == strcmp(address, _("NULL"))) SL_RETURN ( (0), _("sh_mail_setaddress")); if (address != NULL && address_num < (2 * SH_PATHBUF / 64 )) { if (address_num < (SH_PATHBUF / 64 )) p = &sh.srvmail.name[address_num*64]; else p = &sh.srvmail.alt[address_num*64]; (void) sl_strlcpy (p, address, 64); if ((p == NULL) || ( sl_strlen(address) != sl_strlen(p))) { memset(p, (int)'\0', 64); SL_RETURN ( (-1), _("sh_mail_setaddress")); } address_list[address_num] = p; #if 0 if (!sl_is_suid()) { TPT(( 0, FIL__, __LINE__, _("msg=\n"), address_num, address_list[address_num])); } #endif if (setaddress_compiled == S_TRUE) { ++address_num; ++address_num_compiled; } else { if (address_num == address_num_compiled) { address_num = 0; address_num_compiled = -99; } ++address_num; } SL_RETURN ( (0), _("sh_mail_setaddress")); } SL_RETURN ( (-1), _("sh_mail_setaddress")); } int sh_mail_setaddress_int (const char * address) { int i; SL_ENTER(_("sh_mail_setaddress_int")); setaddress_compiled = S_TRUE; i = sh_mail_setaddress(address); setaddress_compiled = S_FALSE; SL_RETURN(i, _("sh_mail_setaddress_int")); } int sh_mail_setNum (const char * str) { int i = atoi (str); SL_ENTER(_("sh_mail_setNum")); if (i >= 0 && i < SH_FIFO_MAX) sh.mailNum.alarm_interval = (time_t) i; else SL_RETURN ((-1), _("sh_mail_setNum")); SL_RETURN( (0), _("sh_mail_setNum")); } static int all_in_one = S_FALSE; int sh_mail_setFlag (const char * str) { int i; SL_ENTER(_("sh_mail_setFlag")); i = sh_util_flagval(str, &all_in_one); SL_RETURN(i, _("sh_mail_setFlag")); } static char * mail_subject = NULL; int set_mail_subject (const char * str) { SL_ENTER(_("set_mail_subject")); if (!str) SL_RETURN( (-1), _("set_mail_subject")); if (mail_subject != NULL) SH_FREE(mail_subject); if (0 == sl_strncmp(str, _("NULL"), 4)) { mail_subject = NULL; SL_RETURN( 0, _("set_mail_subject")); } mail_subject = sh_util_strdup(str); SL_RETURN( (0), _("set_mail_subject")); } static SH_FIFO * fifo_mail = NULL; static void sh_mail_emptystack (void) { char * msg; size_t len; SL_ENTER(_("sh_mail_emptystack")); if (fifo_mail == NULL) SL_RET0(_("sh_mail_emptystack")); while (NULL != (msg = pop_list(fifo_mail))) { len = sl_strlen(msg); memset(msg, 0, len); SH_FREE(msg); } SL_RET0(_("sh_mail_emptystack")); } /* insert "\r\n" after each 998 char */ static char * split_string(char * str); int sh_mail_pushstack (/*@null@*/char * msg) { char * p; int retval = 0; int status; SL_ENTER(_("sh_mail_pushstack")); if (msg == NULL || failedMail == SL_TRUE || sh.srvmail.name[0] == '\0') SL_RETURN((0), (_("sh_mail_pushstack"))); if (0 != sh_filter_filter(msg, &mail_filter)) SL_RETURN((0), (_("sh_mail_pushstack"))); #if 0 if (msg != NULL && sl_strlen(msg) > 998) /* RFC 2822 */ msg[998] = '\0'; #endif p = split_string(msg); if (fifo_mail == NULL) { fifo_mail = SH_ALLOC(sizeof(SH_FIFO)); fifo_init(fifo_mail); } status = push_list (fifo_mail, p); if (status >= 0) ++sh.mailNum.alarm_last; SH_FREE(p); if (sh.mailNum.alarm_last >= sh.mailNum.alarm_interval) { BREAKEXIT(sh_mail_msg); retval = sh_mail_msg (NULL); } if (status == SH_FIFO_MAX) retval = -2; SL_RETURN(retval, (_("sh_mail_pushstack"))); } /* The mailer. */ static int sh_mail_end_conn (FILE * connfile, int fd); static FILE * sh_mail_start_conn (int aFlag, int * fd); static void sh_mail_get_subject(char * message, char * mheader, size_t len) { st_format rep_serv_tab[] = { { 'T', S_FMT_TIME, 0, 0, NULL}, { 'H', S_FMT_STRING, 0, 0, NULL}, { 'M', S_FMT_STRING, 0, 0, NULL}, { 'S', S_FMT_STRING, 0, 0, NULL}, {'\0', S_FMT_ULONG, 0, 0, NULL}, }; char * p; char * mptr; char sev[8]; SL_ENTER(_("sh_mail_get_subject")); (void) sl_strlcpy(mheader, _("Subject: "), len); if (NULL == strchr(mail_subject, '%')) { (void) sl_strlcat(mheader, mail_subject, len); SL_RET0(_("sh_mail_get_subject")); } rep_serv_tab[0].data_ulong = (unsigned long) time(NULL); rep_serv_tab[1].data_str = sh.host.name; /* fast forward to the important part */ mptr = (char*)sl_strstr(message, _("msg=")); if (mptr) { mptr += 4; rep_serv_tab[2].data_str = mptr; } else rep_serv_tab[2].data_str = message; mptr = (char*)sl_strstr(message, _("sev=")); if (mptr) { mptr += 5; sev[0] = *mptr; ++mptr; sev[1] = *mptr; ++mptr; sev[2] = *mptr; ++mptr; sev[3] = *mptr; ++mptr; sev[4] = '\0'; } else { mptr = message; sev[0] = *mptr; ++mptr; sev[1] = *mptr; ++mptr; sev[2] = *mptr; ++mptr; sev[3] = *mptr; ++mptr; if (*mptr == ' ') { sev[4] = '\0'; } else { sev[4] = *mptr; ++mptr; if (*mptr == ' ') { sev[5] = '\0'; } else { sev[5] = *mptr; sev[6] = '\0'; } } } rep_serv_tab[3].data_str = sev; p = sh_util_formatted(mail_subject, rep_serv_tab); (void) sl_strlcat(mheader, p, len); SH_FREE(p); SL_RET0(_("sh_mail_get_subject")); } #if defined(HAVE_MLOCK) && !defined(HAVE_BROKEN_MLOCK) #include #endif static char * sh_mail_realloc (char * inbuf, size_t * insize, size_t increase) { size_t newsize; char * outbuf = inbuf; SL_ENTER(_("sh_mail_realloc")); if (sl_ok_adds((*insize), 1)) { newsize = (*insize) + 1; if (sl_ok_adds(newsize, increase)) { newsize += increase; outbuf = SH_ALLOC(newsize); MLOCK(outbuf, newsize); (void) sl_strlcpy(outbuf, inbuf, newsize); memset (inbuf, 0, (*insize)); MUNLOCK(inbuf, (*insize)); SH_FREE(inbuf); *insize = newsize; } } SL_RETURN( (outbuf), _("sh_mail_realloc")); } int sh_mail_msg (/*@null@*/char * message) { char subject[32+32+SH_MINIBUF+2+3+SH_PATHBUF]; char mheader[32+32+SH_MINIBUF+2+3]; char * mailMsg; char * popMsg; int status = 0, errcount; size_t wrlen; int i; int num_popped = 0; int retval = -1; char * bufcompress; static int failcount = 0; static int isfirst = 1; static int mailcount = 0; FILE * connfile = NULL; struct sigaction old_act; struct sigaction new_act; static time_t id_audit = 0; static time_t fail_time = 0; static time_t success_time = 0; static int ma_block = 0; int ma_socket = -1; SH_FIFO * fifo_temp = NULL; char * theSig; char * theMsg = NULL; /* #define SH_MAILBUF (256) */ #define SH_MAILBUF (8*4096) size_t msgbufsize = SH_MAILBUF; size_t combufsize = SH_MAILBUF; char timebuf[81]; char hashbuf[KEYBUF_SIZE]; SL_ENTER(_("sh_mail_msg")); if (ma_block == 1) SL_RETURN( (0), _("sh_mail_msg")); /* Return if we cannot mail. */ if (failedMail == SL_TRUE) SL_RETURN((-1), _("sh_mail_msg")); if (failedMail == SL_FALSE && address_list[0] == NULL) { TPT((0, FIL__, __LINE__, _("msg=\n"))); failedMail = SL_TRUE; SL_RETURN((-1), _("sh_mail_msg")); } if ( (success_time > 0) && (fail_time > 0) && (time(NULL) - success_time) > 3600*SH_MAX_FAIL) { ma_block = 1; sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_SRV_FAIL, _("mail"), address_list[0]); ma_block = 0; sh_mail_emptystack(); sh.mailNum.alarm_last = 0; failedMail = SL_TRUE; SL_RETURN((-1), _("sh_mail_msg")); } /* Try at most each hour. */ if ((fail_time > 0) && (time(NULL) - fail_time) < 3/*600*/) { if (failcount > 3) { /* -- Save for later. -- */ sh_mail_pushstack (message); ++failcount; SL_RETURN((-1), _("sh_mail_msg")); } else { (void) retry_msleep(2, 0); ++failcount; } } /* -- Reset time of last failure. -- */ fail_time = 0; /* -- Polling, empty queue. -- */ if (message == NULL && sh.mailNum.alarm_last == 0) SL_RETURN((-1), _("sh_mail_msg")); /* -- Filtered. -- */ if (message != NULL && 0 != sh_filter_filter(message, &mail_filter)) SL_RETURN((-1), (_("sh_mail_msg"))); /* --------- Build complete message. ------------------------ */ theMsg = split_string(message); /* ---------- Header ---------------------------------------- */ if (mail_subject == NULL) { (void) sl_strlcpy(mheader, _("Subject: "), sizeof(mheader)-5); (void) sl_strlcat(mheader, sh_unix_time (0, timebuf, sizeof(timebuf)), sizeof(mheader)-5); (void) sl_strlcat(mheader, " ", sizeof(mheader)-5); (void) sl_strlcat(mheader, sh.host.name, sizeof(mheader)-5); } else { if (message == NULL) { theMsg = pop_list(fifo_mail); message = theMsg; if (message) --sh.mailNum.alarm_last; } if (message) { sh_mail_get_subject(message, mheader, sizeof(mheader)-5); } else { (void) sl_strlcpy(mheader, _("Subject: "), sizeof(mheader)-5); (void) sl_strlcat(mheader, sh_unix_time (0, timebuf, sizeof(timebuf)), sizeof(mheader)-5); (void) sl_strlcat(mheader, " ", sizeof(mheader)-5); (void) sl_strlcat(mheader, sh.host.name, sizeof(mheader)-5); } } /* RFC 821: Header is terminated by an empty line */ (void) sl_strlcat(mheader, "\015\012\015\012", sizeof(mheader)); /* ---------- Message --------------------------------------- */ (void) sl_strlcpy(subject, sh_unix_time (0, timebuf, sizeof(timebuf)), sizeof(subject)); (void) sl_strlcat(subject, " ", sizeof(subject)); (void) sl_strlcat(subject, sh.host.name, sizeof(subject)); (void) sl_strlcat(subject, "\r\n", sizeof(subject)); mailMsg = (char *) SH_ALLOC (msgbufsize); bufcompress = (char *) SH_ALLOC (combufsize); MLOCK(mailMsg , msgbufsize); MLOCK(bufcompress , combufsize); (void) sl_strlcpy(mailMsg, mheader, msgbufsize); bufcompress[0] = '\0'; (void) sl_strlcat(mailMsg, _("-----BEGIN MESSAGE-----\r\n"), msgbufsize); (void) sl_strlcat(mailMsg, subject, msgbufsize); (void) sh_util_compress (bufcompress, subject, (combufsize - KEY_LEN - 1)); if (message != NULL) { if ((sl_strlen(theMsg) + sl_strlen(mailMsg) + 1) > (msgbufsize-(4*KEY_LEN))) { mailMsg = sh_mail_realloc(mailMsg, &msgbufsize, sl_strlen(theMsg)+2); bufcompress = sh_mail_realloc(bufcompress, &combufsize, sl_strlen(theMsg)); } (void) sl_strlcat(mailMsg, theMsg, msgbufsize-(4*KEY_LEN)); (void) sl_strlcat(mailMsg, "\r\n", msgbufsize-(4*KEY_LEN)); (void) sh_util_compress (bufcompress, theMsg, combufsize-KEY_LEN-1); } if (sh.mailNum.alarm_last > 0) { fifo_temp = SH_ALLOC (sizeof(SH_FIFO)); fifo_init (fifo_temp); while ( NULL != (popMsg = pop_list(fifo_mail)) ) { (void) push_list (fifo_temp, popMsg); if ((sl_strlen(popMsg) + sl_strlen(mailMsg) + 1) > (msgbufsize-(4*KEY_LEN))) { mailMsg = sh_mail_realloc(mailMsg, &msgbufsize, sl_strlen(popMsg)+2); bufcompress = sh_mail_realloc(bufcompress, &combufsize, sl_strlen(popMsg)); } (void) sl_strlcat(mailMsg, popMsg, msgbufsize-(4*KEY_LEN)); (void) sl_strlcat(mailMsg, "\r\n", msgbufsize-(4*KEY_LEN)); (void) sh_util_compress(bufcompress, popMsg, combufsize-KEY_LEN-1); SH_FREE(popMsg); --sh.mailNum.alarm_last; ++num_popped; } } /* ------ signature block ------------------------------------ */ (void) sl_strlcat(mailMsg, _("-----BEGIN SIGNATURE-----\r\n"), msgbufsize); /* Generate new signature key. */ if (isfirst == 1) { BREAKEXIT(sh_util_keyinit); (void) sh_util_keyinit (skey->mailkey_old, KEY_LEN+1); } /* iterate the key */ (void) sl_strlcpy(skey->mailkey_new, sh_tiger_hash (skey->mailkey_old, TIGER_DATA, KEY_LEN, hashbuf, sizeof(hashbuf)), KEY_LEN+1); if (isfirst == 0) { char sigbuf[KEYBUF_SIZE]; /* Sign the message with the signature key. */ theSig = sh_util_siggen (skey->mailkey_new, bufcompress, sl_strlen(bufcompress), sigbuf, sizeof(sigbuf)); (void) sl_strlcat (mailMsg, theSig, msgbufsize); } else { id_audit = time (NULL); /* reveal first signature key */ /* flawfinder: ignore */ (void) sl_strlcpy(skey->crypt, skey->mailkey_new, KEY_LEN+1); BREAKEXIT(sh_util_encode); /* flawfinder: ignore */ sh_util_encode(skey->crypt, bufcompress, 0, 'A'); /* flawfinder: ignore */ (void) sl_strlcat (mailMsg, skey->crypt, msgbufsize); /* flawfinder: ignore */ memset (skey->crypt, 0, KEY_LEN); isfirst = 0; } (void) sl_strlcat (mailMsg, "\r\n", msgbufsize); /* X(n) -> X(n-1) */ (void) sl_strlcpy (skey->mailkey_old, skey->mailkey_new, KEY_LEN+1); sl_snprintf(subject, sizeof(subject), _("%06d %010ld::%s\r\n"), mailcount, (long) id_audit, sh.host.name); (void) sl_strlcat (mailMsg, subject, msgbufsize); ++mailcount; (void) sl_strlcat (mailMsg, _("-----END MESSAGE-----"), msgbufsize); /* ---------- Connect ---------------------------------------- */ /* -- Catch (ignore) 'broken pipe'. */ new_act.sa_handler = SIG_IGN; sigemptyset( &new_act.sa_mask ); /* set an empty mask */ new_act.sa_flags = 0; /* init sa_flags */ (void) sigaction (SIGPIPE, &new_act, &old_act); i = 0; errcount = 0; if (all_in_one == S_FALSE) { while (address_list[i] != NULL && i < address_num) { connfile = sh_mail_start_conn (i, &ma_socket); if (NULL != connfile) { wrlen = fwrite (mailMsg, 1, sl_strlen(mailMsg), connfile); wrlen -= sl_strlen(mailMsg); if (wrlen == 0) status = sh_mail_end_conn (connfile, ma_socket); else status = -1; } if (NULL == connfile || status != 0) { ma_block = 1; sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_SRV_FAIL, _("mail"), address_list[i]); ma_block = 0; ++errcount; ++sh.statistics.mail_failed; } else { ++sh.statistics.mail_success; } if (connfile != NULL) { (void) fclose (connfile); connfile = NULL; } ++i; } } else { connfile = sh_mail_start_conn ( -9 , &ma_socket); if (NULL != connfile) { wrlen = fwrite (mailMsg, 1, sl_strlen(mailMsg), connfile); wrlen -= sl_strlen(mailMsg); if (wrlen == 0) status = sh_mail_end_conn (connfile, ma_socket); else status = -1; } if (NULL == connfile || status != 0) { ma_block = 1; sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_SRV_FAIL, _("mail"), address_list[0]); ma_block = 0; errcount = address_num; ++sh.statistics.mail_failed; } else { ++sh.statistics.mail_success; } if (connfile != NULL) { (void) fclose (connfile); connfile = NULL; } } memset (bufcompress, 0, combufsize); MUNLOCK(bufcompress , combufsize); SH_FREE(bufcompress); memset (mailMsg, 0, msgbufsize); MUNLOCK(mailMsg , msgbufsize); SH_FREE(mailMsg); /* --- Stay responsible for delivery in case of failure --- */ if (errcount == address_num && fifo_temp != NULL) { while ( (NULL != (popMsg = pop_list(fifo_temp))) ) { if (push_list (fifo_mail, popMsg) >= 0) ++sh.mailNum.alarm_last; SH_FREE(popMsg); } if (message != NULL) { if (fifo_mail == NULL) { fifo_mail = SH_ALLOC(sizeof(SH_FIFO)); fifo_init(fifo_mail); } retval = push_list (fifo_mail, theMsg); if (retval >= 0) ++sh.mailNum.alarm_last; if (retval == SH_FIFO_MAX) retval = -2; else retval = -1; } } else if (fifo_temp != NULL) { while ( (NULL != (popMsg = pop_list(fifo_temp))) ) { SH_FREE(popMsg); } } if (fifo_temp != NULL) SH_FREE(fifo_temp); /* if (connfile != NULL) fclose (connfile); */ if (theMsg != NULL) SH_FREE(theMsg); /* --- Reset signal. --- */ (void) sigaction (SIGPIPE, &old_act, NULL); if (errcount == address_num) { fail_time = time(NULL); SL_RETURN((retval), _("sh_mail_msg")); } success_time = time(NULL); failcount = 0; SL_RETURN((0), _("sh_mail_msg")); } /* * * SMTP CODE BELOW * * */ #include #ifdef HOST_IS_HPUX #define _XOPEN_SOURCE_EXTENDED #endif #include #include #include #include #ifndef S_SPLINT_S #include #else #define AF_INET 2 #endif #define SH_NEED_GETHOSTBYXXX #include "sh_static.h" /* missing on HP-UX 10.20 */ #ifndef IPPORT_SMTP #define IPPORT_SMTP 25 #endif static int sh_mail_wait(int code, int ma_socket); static char * relay_host = NULL; int sh_mail_set_relay (const char * str_s) { SL_ENTER(_("sh_mail_set_relay")); if (str_s == NULL) SL_RETURN( -1, _("sh_mail_set_relay")); if (relay_host != NULL) { SH_FREE (relay_host); relay_host = NULL; } if (0 == sl_strncmp(str_s, _("NULL"), 4)) { SL_RETURN( 0, _("sh_mail_set_relay")); } relay_host = sh_util_strdup(str_s); SL_RETURN( 0, _("sh_mail_set_relay")); } static char * mail_sender = NULL; int sh_mail_set_sender (const char *str) { if (mail_sender != NULL) { SH_FREE (mail_sender); mail_sender = NULL; } if (str != NULL) { mail_sender = sh_util_strdup (str); } if (mail_sender == NULL) { return -1; } return 0; } /************************* * * start connection * for details on SMTP, see RFC 821 */ static time_t time_wait = 300; static FILE * sh_mail_start_conn (int aFlag, int * ma_socket) { char * address; int ecount; char this_address[256]; char ma_machine[256]; char ma_user[256]; char error_msg[256]; char error_call[SH_MINIBUF]; int error_num = 0; register int i, j, k; FILE * connFile = NULL; struct tm * my_tm; #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_LOCALTIME_R) struct tm time_tm; #endif time_t my_time; char my_tbuf[128]; int fd; dnsrep * answers; mx * result; SL_ENTER(_("sh_mail_start_conn")); *ma_socket = -1; time_wait = 300; if (aFlag >= 0) address = address_list[aFlag]; else address = address_list[0]; TPT(( 0, FIL__, __LINE__, _("msg=\n"), aFlag, address)); /* ------- split adress ------------------ */ if (0 == strcmp(address, _("NULL"))) { SL_RETURN( NULL, _("sh_mail_start_conn")); } if (strchr (address, '@') == NULL) { (void) sl_strlcpy(ma_user, address, 256); (void) sl_strlcpy(ma_machine, _("localhost"), 256); } else { i = 0; while (i < 255 && address[i] != '@') { ma_user[i] = address[i]; ++i; } /* adress[i] = '@' */ ma_user[i] = '\0'; j = i + 1; k = i; i = 0; while (i < 255 && address[i+j] != '\0') { ma_machine[i] = address[i+j]; ++i; } ma_machine[i] = '\0'; if (address[k] != '@' || address[k+i+1] != '\0') { SL_RETURN( NULL, _("sh_mail_start_conn")); } } if (relay_host != NULL) { (void) sl_strlcpy (ma_machine, relay_host, sizeof(ma_machine)); TPT((0, FIL__, __LINE__, _("msg=\n"), ma_user, ma_machine)); fd = connect_port (ma_machine, IPPORT_SMTP, error_call, &error_num, error_msg, 256); } else { answers = return_mx (ma_machine); if (answers) { result = answers->reply; fd = -1; for (i = 0; i < answers->count; ++i) { (void) sl_strlcpy(ma_machine, result[i].address, sizeof(ma_machine)); TPT((0, FIL__, __LINE__, _("msg=\n"), ma_user, ma_machine, result[i].pref)); fd = connect_port (ma_machine, IPPORT_SMTP, error_call, &error_num, error_msg, 256); if (fd >= 0) break; } (void) free_mx(answers); } else { (void) sl_strlcpy(error_call, _("return_mx"), SH_MINIBUF); (void) sl_strlcpy(error_msg, _("The specified host is unknown: "), 256); (void) sl_strlcat(error_msg, ma_machine, 256); fd = -1; } } if (fd < 0) { sh_error_handle ((-1), FIL__, __LINE__, error_num, MSG_E_NET, error_msg, error_call, _("email"), ma_machine); SL_RETURN( NULL, _("sh_mail_start_conn")); } /* associate a FILE structure with it */ connFile = fdopen (fd, "r+"); if (connFile == NULL) { TPT(( 0, FIL__, __LINE__, _("msg=\n"))); (void) close(fd); SL_RETURN( NULL, _("sh_mail_start_conn")); } /* say HELO to the other socket */ if (0 == sh_mail_wait (220, fd)) { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET, _("Timeout on SMTP session init"), _("sh_mail_start_conn"), _("mail"), sh.host.name); TPT(( 0, FIL__, __LINE__, _("msg=\n"))); (void) fclose(connFile); SL_RETURN( NULL, _("sh_mail_start_conn")); } (void) fflush(connFile); if (0 != is_numeric(sh.host.name)) { TPT(( 0, FIL__, __LINE__, _("msg=%c%c"), sh.host.name, 13, 10)); } else { TPT(( 0, FIL__, __LINE__, _("msg=%c%c"), sh.host.name, 13, 10)); } if (0 != is_numeric(sh.host.name)) fprintf(connFile, _("HELO [%s]%c%c"), sh.host.name, 13, 10); else fprintf(connFile, _("HELO %s%c%c"), sh.host.name, 13, 10); (void) fflush(connFile); if (0 == sh_mail_wait(250, fd)) { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET, _("HELO failed"), _("sh_mail_start_conn"), _("mail"), sh.host.name); TPT(( 0, FIL__, __LINE__, _("msg=\n"))); (void) fclose(connFile); SL_RETURN( NULL, _("sh_mail_start_conn")); } /* tell them who we are */ (void) sl_strlcpy (this_address, mail_sender ? mail_sender : DEFAULT_SENDER, 256); if (NULL == strchr(this_address, '@')) { (void) sl_strlcat (this_address, "@", 256); if (0 != is_numeric(sh.host.name)) (void) sl_strlcat (this_address, _("example.com"), 256); else (void) sl_strlcat (this_address, sh.host.name, 256); } TPT(( 0, FIL__, __LINE__, _("msg=>%c%c"), this_address, 13, 10)); (void) fflush(connFile); /*@-usedef@*/ fprintf(connFile, _("MAIL FROM:<%s>%c%c"), this_address, 13, 10); /*@+usedef@*/ (void) fflush(connFile); if (0 == sh_mail_wait(250, fd)) { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET, _("MAIL FROM failed"), _("sh_mail_start_conn"), _("mail"), this_address); TPT(( 0, FIL__, __LINE__, _("msg=\n"))); (void) fclose(connFile); SL_RETURN( NULL, _("sh_mail_start_conn")); } /* tell them who to send mail to */ if (aFlag >= 0) { TPT(( 0, FIL__, __LINE__, _("msg=>%c%c"), address, 13, 10)); (void) fflush(connFile); fprintf(connFile, _("RCPT TO:<%s>%c%c"), address, 13, 10); (void) fflush(connFile); if (0 == sh_mail_wait(250, fd)) { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET, _("RCPT TO failed"), _("sh_mail_start_conn"), _("mail"), address); TPT(( 0, FIL__, __LINE__, _("msg=\n"))); (void) fclose(connFile); SL_RETURN( NULL, _("sh_mail_start_conn")); } } else { ecount = 0; for (i = 0; i < address_num; ++i) { if (address_list[i] == NULL) /* paranoia */ break; TPT(( 0, FIL__, __LINE__, _("msg=>%c%c"), address_list[i], 13, 10)); (void) fflush(connFile); fprintf(connFile, _("RCPT TO:<%s>%c%c"), address_list[i], 13, 10); (void) fflush(connFile); if (0 == sh_mail_wait(250, fd)) { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET, _("RCPT TO failed"), _("sh_mail_start_conn"), _("mail"), address_list[i]); TPT(( 0, FIL__, __LINE__, _("msg=\n"))); ++ecount; } } if (ecount == address_num) { (void) fclose(connFile); SL_RETURN( NULL, _("sh_mail_start_conn")); } } /* Send the message */ TPT(( 0, FIL__, __LINE__, _("msg=%c%c"), 13, 10)); (void) fflush(connFile); fprintf(connFile, _("DATA%c%c"), 13, 10); (void) fflush(connFile); if (0 == sh_mail_wait(354, fd)) { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET, _("DATA failed"), _("sh_mail_start_conn"), _("mail"), address); TPT(( 0, FIL__, __LINE__, _("msg=\n"))); (void) fclose(connFile); SL_RETURN( NULL, _("sh_mail_start_conn")); } my_time = time(NULL); #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_LOCALTIME_R) my_tm = localtime_r(&my_time, &time_tm); #else my_tm = localtime(&my_time); #endif (void) strftime(my_tbuf, 127, _("%a, %d %b %Y %H:%M:%S %Z"), my_tm); TPT(( 0, FIL__, __LINE__, _("msg=%c%cTo: <%s>%c%cDate: %s>%c%c"), this_address, 13, 10, address, 13, 10, my_tbuf, 13, 10)); (void) fflush(connFile); fprintf(connFile, _("From: <%s>%c%c"\ "To: <%s>%c%c"\ "Date: %s%c%c"), this_address, 13, 10, address, 13, 10, my_tbuf, 13, 10); *ma_socket = fd; SL_RETURN( connFile, _("sh_mail_start_conn")); } /************************* * * end connection * */ static int sh_mail_end_conn (FILE * connFile, int fd) { SL_ENTER(_("sh_mail_end_conn")); time_wait = 300; (void) fflush(connFile); fprintf(connFile, _("%c%c.%c%c"), 13, 10, 13, 10); (void) fflush(connFile); TPT(( 0, FIL__, __LINE__, _("msg=\n"))); if (0 != sh_mail_wait(250, fd)) { (void) fflush(connFile); fprintf(connFile, _("QUIT%c%c"), 13, 10); (void) fflush(connFile); TPT(( 0, FIL__, __LINE__, _("msg=\n"))); SL_RETURN (0, _("sh_mail_end_conn")); } sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET, _("QUIT failed"), _("sh_mail_end_conn"), _("mail"), _("SMTP server")); TPT(( 0, FIL__, __LINE__, _("msg=\n"))); SL_RETURN ((-1), _("sh_mail_end_conn")); } /**************************** * * Handle server replies * * */ static int sh_mail_wait(int code, int ma_socket) { int rcode, g; char c; char errmsg[128]; enum { WAIT_CODE_START, WAIT_CODE, WAIT_NL, WAIT_NL_CONT } state; time_t waited_time; SL_ENTER(_("mail_wait")); waited_time = time(NULL); /* timeout after 5 minutes */ rcode = 0; state = WAIT_CODE_START; while (sl_read_timeout_fd (ma_socket, &c, 1, time_wait, SL_FALSE) > 0) { g = (int) c; /* if (g == EOF) { TPT((0, FIL__, __LINE__, _("msg=\n"))); SL_RETURN( 0, _("mail_wait")); } */ switch(state) { /* wait for start of a numerical code */ case WAIT_CODE_START: if (0 != isspace(g)) break; /* Skip white space */ if (0 == isdigit(g)) return 0; /* No leading number */ rcode = g-(int)'0'; /* convert to number */ state = WAIT_CODE; break; /* wait for completion of numerical code */ case WAIT_CODE: if (0 != isdigit(g)) { rcode = rcode * 10 + (g-(int)'0'); /* next digit */ break; } /*@+charintliteral@*/ state = ((g == '-') ? WAIT_NL_CONT : WAIT_NL); /*@-charintliteral@*/ break; /* wait for newline, then return with status code */ case WAIT_NL: /*@+charintliteral@*/ if (g != '\n') break; /*@-charintliteral@*/ TPT((0, FIL__, __LINE__, _("msg=\n"), rcode, (int)(rcode/100), code, (int)(code/100) )); g = ((int)(rcode/100) == (int)(code/100)) ? 1 : 0; if (g != 1) { sl_snprintf(errmsg, sizeof(errmsg), _("Bad response (%d), expected %d"), rcode, code); sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_NET, errmsg, _("sh_mail_wait"), _("mail"), _("SMTP server")); } waited_time = time(NULL) - waited_time; time_wait -= waited_time; TPT((0, FIL__, __LINE__, _("msg=\n"), (int) time_wait)); SL_RETURN( (g), _("mail_wait")) ; /* wait for continuation line */ /*@fallthrough@*//* no, but splint doesn't understand */ case WAIT_NL_CONT: /*@+charintliteral@*/ if (g == '\n') state = WAIT_CODE_START; /* There is a continuation line */ /*@-charintliteral@*/ break; default: TPT((0, FIL__, __LINE__, _("msg=\n"))); SL_RETURN( 0, _("mail_wait")); } } TPT((0, FIL__, __LINE__, _("msg=\n"))); /* Failed, EOF or error on socket */ SL_RETURN( 0, _("mail_wait")); } /* -- function to insert "\r\n" after each 998 chars -- */ #define SPLIT_AT 998 static char * split_string(char * str) { size_t size; size_t blocks; int count = 0; char * p, * p0; char * q; if (!str) return NULL; size = strlen(str) + 1; blocks = 1 + (size / SPLIT_AT); if (sl_ok_muls(2, blocks) && sl_ok_adds(size, (2*blocks))) { size = size + (2*blocks); } else { /* integer overflow, do not split */ p = sh_util_strdup(str); return p; } p = SH_ALLOC(size); memset(p, 0, size); p0 = p; q = str; while (*q != '\0') { *p = *q; ++p; ++q; ++count; if (0 == (count % SPLIT_AT)) { count = 0; *p = '\r'; ++p; *p = '\n'; ++p; } } /* fprintf(stderr, "used = %d\n", strlen(p0)); */ return p0; } /***************************************************************** * * MX Resolver Routines * *****************************************************************/ #if defined(HAVE_ARPA_NAMESER_H) #include #ifdef __APPLE__ #define BIND_8_COMPAT 1 #endif #ifndef S_SPLINT_S #include #include #endif #include #include #ifndef S_SPLINT_S #include #endif #include "sh_tools.h" #ifndef HFIXEDSZ #define HFIXEDSZ 12 #endif #ifndef QFIXEDSZ #define QFIXEDSZ 4 #endif /*@unused@*//* used in get_mx() which is not parsed by splint */ static unsigned int get_short (unsigned char * loc) { unsigned int retval = 0; if (loc) { /* byte order: MSB first */ /*@+charint@*/ retval = (((unsigned char) * loc) * 256) | ((unsigned char) * (loc + 1)); /*@-charint@*/ } return (retval); } /* parser errors with splint */ #ifndef S_SPLINT_S static dnsrep * get_mx (char *hostname) { int ret, length, status; mx * result; size_t len; typedef union { HEADER head; unsigned char buffer[4096]; } querybuf; querybuf * reply; char expanded[1024]; unsigned char * comp_dn, * eom; HEADER * header; int type, rdlength, pref; unsigned int count, theindex; dnsrep * retval; SL_ENTER(_("get_mx")); if (0 != res_init ()) SL_RETURN (NULL, _("get_mx")); reply = SH_ALLOC(sizeof(querybuf)); errno = 0; length = res_query (hostname, C_IN, T_MX, (unsigned char *) reply, 4095); if (length < 1) { char errbuf[SH_ERRBUF_SIZE]; /* error handling */ if (length == -1) { if (errno == ECONNREFUSED) status = ECONNREFUSED; else status = h_errno; #ifdef FIL__ sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN, (errno == ECONNREFUSED) ? sh_error_message (status, errbuf, sizeof(errbuf)) : sh_tools_errmessage(status, errbuf, sizeof(errbuf)), _("res_query")); #else if (errno == ECONNREFUSED) fprintf(stderr, " ERROR: %s: \n", strerror(errno)); /* TESTONLY */ else fprintf(stderr, "HERROR: %s\n", hstrerror(h_errno));/* TESTONLY */ #endif } SH_FREE(reply); SL_RETURN (NULL, _("get_mx")); } ret = 0; header = (HEADER *) reply; /* start of data section */ comp_dn = (unsigned char *) reply + HFIXEDSZ; /* end-of-message */ eom = (unsigned char *) reply + length; /* HEADER NAME -- must be skipped or decompressed * TYPE -- type of data we got back, 16 bit integer * CLASS -- class we got back, also a 16 bit integer * TTL -- 32 bit time-to-live. just skip this * RDLENGTH -- length of the data to follow * RDATA -- the data: * PREF -- 16 bit preference * MX -- name of mail exchanger, must be decompressed */ /* Skip the query data. * QDCOUNT is the number of entries (unsigned 16 bit int). */ count = ntohs (header->qdcount); for (theindex = 0; theindex < count; ++theindex) { ret = dn_skipname (comp_dn, eom); comp_dn += ret + QFIXEDSZ; if (ret < 1 || comp_dn >= eom) { SH_FREE(reply); SL_RETURN (NULL, _("get_mx")); } } count = ntohs (header->ancount); if (count < 1) { SH_FREE(reply); SL_RETURN (NULL, _("get_mx")); } retval = SH_ALLOC (sizeof (dnsrep)); if (!retval) { SH_FREE(reply); SL_RETURN (NULL, _("get_mx")); } retval->count = count; /* allocate space for the results */ if (!sl_ok_muls(count, sizeof (mx))) { SH_FREE(reply); SH_FREE (retval); SL_RETURN (NULL, _("get_mx")); } result = SH_ALLOC (count * sizeof (mx)); if (!result) { SH_FREE(reply); SH_FREE (retval); SL_RETURN (NULL, _("get_mx")); } retval->reply = result; do { /* HEADER NAME */ ret = dn_expand ((unsigned char *) &reply, eom, comp_dn, (char *) expanded, 1023); comp_dn += ret; if (ret < 1 || comp_dn >= eom) { SH_FREE(reply); SH_FREE (result); SH_FREE (retval); SL_RETURN (NULL, _("get_mx")); } /* TYPE */ type = get_short (comp_dn); comp_dn += 2; if (type != T_MX || comp_dn >= eom) { SH_FREE(reply); SH_FREE (result); SH_FREE (retval); SL_RETURN (NULL, _("get_mx")); } /* CLASS (re-use 'type' var) */ type = get_short (comp_dn); comp_dn += 2; if (comp_dn >= eom) { SH_FREE(reply); SH_FREE (result); SH_FREE (retval); SL_RETURN (NULL, _("get_mx")); } /* TTL */ comp_dn += 4; if (comp_dn >= eom) { SH_FREE(reply); SH_FREE (result); SH_FREE (retval); SL_RETURN (NULL, _("get_mx")); } /* RDLENGTH */ rdlength = get_short (comp_dn); comp_dn += 2; if (rdlength < 1 || comp_dn >= eom) { SH_FREE(reply); SH_FREE (result); SH_FREE (retval); SL_RETURN (NULL, _("get_mx")); } /* RDATA */ pref = get_short (comp_dn); comp_dn += 2; if (comp_dn >= eom) { SH_FREE(reply); SH_FREE (result); SH_FREE (retval); SL_RETURN (NULL, _("get_mx")); } ret = dn_expand ((unsigned char *) &reply, eom, comp_dn, (char *) expanded, 1023); comp_dn += ret; if (ret < 1) { SH_FREE(reply); SH_FREE (result); SH_FREE (retval); SL_RETURN (NULL, _("get_mx")); } count--; /* fill in the struct */ result[count].pref = pref; len = strlen (expanded) + 1; result[count].address = SH_ALLOC (len); sl_strlcpy (result[count].address, expanded, len); } while (ret > 0 && comp_dn < eom && count); SH_FREE(reply); SL_RETURN (retval, _("get_mx")); } /* ifndef S_SPLINT_S */ #endif /* #if defined(HAVE_ARPA_NAMESER_H) */ #endif static int comp_mx_pref (const void * a, const void * b) { const mx * ax = (const mx *) a; const mx * bx = (const mx *) b; if (ax->pref > bx->pref) return 1; else if (ax->pref < bx->pref) return -1; else return 0; } /* * return_mx returns a list of valid mail exchangers for domain */ static dnsrep * return_mx (char *domain) { struct hostent *host; dnsrep * answers = NULL; mx * result; dnsrep * retval; char errmsg[128]; size_t len; SL_ENTER(_("return_mx")); #if defined(HAVE_ARPA_NAMESER_H) if (domain != NULL) answers = /*@-unrecog@*/get_mx (domain)/*@+unrecog@*/; #endif if (answers != NULL && answers->count > 0) { qsort(answers->reply, (size_t) answers->count, sizeof(mx), comp_mx_pref); SL_RETURN (answers, _("return_mx")); } else { if (domain != NULL) { #if defined(HAVE_ARPA_NAMESER_H) #ifdef FIL__ (void) sl_strlcpy (errmsg, _("No MX record for domain "), 127); (void) sl_strlcat (errmsg, domain, 127); sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN, errmsg, _("get_mx")); #else /* flawfinder: ignore *//* test code only */ strcpy (errmsg, /* known to fit */ _("No MX record for domain ")); strncat (errmsg, domain, 100); errmsg[122] = '\0'; fprintf(stderr, "Warning: %s\n", errmsg); #endif #endif } SH_MUTEX_LOCK(mutex_resolv); host = NULL; retval = NULL; if (domain != NULL) host = /*@-unrecog@*/sh_gethostbyname (domain)/*@+unrecog@*/; if (host) { result = SH_ALLOC (sizeof (mx)); retval = SH_ALLOC (sizeof (dnsrep)); retval->reply = result; retval->count = 1; result->pref = 0; /*@-type@*/ len = strlen (host->h_name) + 1; result->address = SH_ALLOC (len); sl_strlcpy (result->address, host->h_name, len); /*@+type@*/ } SH_MUTEX_UNLOCK(mutex_resolv); if (!host) { #ifdef FIL__ (void) sl_strlcpy (errmsg, _("Unknown host "), 127); (void) sl_strlcat (errmsg, domain, 127); sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, errmsg, _("return_mx")); #endif SL_RETURN (NULL, _("return_mx")); } SL_RETURN (retval, _("return_mx")); } } static int free_mx (dnsrep * answers) { mx * result; int i; SL_ENTER(_("free_mx")); if (!answers) SL_RETURN (0, _("return_mx")); result = answers->reply; for (i = 0; i < answers->count; ++i) { SH_FREE (result[i].address); } SH_FREE(result); SH_FREE(answers); SL_RETURN (0, _("return_mx")); } #ifdef TEST_ONLY int main(int argc, char * argv[]) { int i; dnsrep * answers; mx * result; if (argc < 2) { fprintf(stderr, "Usage: dns \n"); return -1; } answers = return_mx(argv[1]); if (!answers) { fprintf(stderr, "No answer\n"); return -1; } if (answers->count > 0) { result = answers->reply; for (i = 0; i < answers->count; ++i) { fprintf(stderr, "Record %3d: [%3d] %s\n", i, result[i].pref, result[i].address); } } else { fprintf(stderr, "No answer\n"); free_mx(answers); return -1; } free_mx(answers); return (0); } #endif /* if defined(SH_WITH_MAIL) */ #endif