X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/a0f0cad535ac370a2c323171b5ebb33736623595..refs/heads/master:/mts/smtp/smtp.c diff --git a/mts/smtp/smtp.c b/mts/smtp/smtp.c index 68228ee3..d5ce434f 100644 --- a/mts/smtp/smtp.c +++ b/mts/smtp/smtp.c @@ -1,19 +1,22 @@ -/* - * smtp.c -- nmh SMTP interface +/* smtp.c -- nmh SMTP interface * * This code is Copyright (c) 2002, by the authors of nmh. See the * COPYRIGHT file in the root directory of the nmh distribution for * complete copyright information. */ -#include +#include "h/mh.h" +#include "sbr/r1bindex.h" +#include "sbr/client.h" +#include "sbr/error.h" #include "smtp.h" -#include -#include -#include -#include +#include "h/mts.h" +#include "h/signals.h" +#include "h/utils.h" +#include "h/netsec.h" #include +#include "sbr/base64.h" /* * This module implements an interface to SendMail very similar @@ -22,45 +25,23 @@ * RP_-style codes. */ -/* - * On older 4.2BSD machines without the POSIX function `sigaction', - * the alarm handing stuff for time-outs will NOT work due to the way - * syscalls get restarted. This is not really crucial, since SendMail - * is generally well-behaved in this area. - */ - -#ifdef SENDMAILBUG -/* - * It appears that some versions of Sendmail will return Code 451 - * when they don't really want to indicate a failure. - * "Code 451 almost always means sendmail has deferred; we don't - * really want bomb out at this point since sendmail will rectify - * things later." So, if you define SENDMAILBUG, Code 451 is - * considered the same as Code 250. Yuck! - */ -#endif - #define NBITS ((sizeof (int)) * 8) -/* - * these codes must all be different! - */ +/* Timeout in seconds for SMTP commands. + * Lore has it they must be distinct. */ #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */ #define SM_HELO 20 #define SM_RSET 15 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */ #define SM_RCPT 302 /* see above */ #define SM_DATA 120 /* see above */ -#define SM_TEXT 180 /* see above */ #define SM_DOT 600 /* see above */ #define SM_QUIT 30 -#define SM_CLOS 10 -#define SM_AUTH 45 static int sm_addrs = 0; static int sm_child = NOTOK; static int sm_debug = 0; -static int sm_nl = TRUE; +static bool sm_nl = true; static int sm_verbose = 0; static netsec_context *nsc = NULL; @@ -81,16 +62,16 @@ static int smtp_init (char *, char *, char *, int, int, int, int, const char *, static int sendmail_init (char *, int, int, int, int, const char *, const char *); -static int rclient (char *, char *); -static int sm_ierror (const char *fmt, ...); +static int rclient (char *, char *, char **); +static int sm_ierror (const char *fmt, ...) CHECK_PRINTF(1, 2); static int sm_nerror (char *); -static int smtalk (int time, char *fmt, ...); +static int smtalk (int time, char *fmt, ...) CHECK_PRINTF(2, 3); static int sm_wstream (char *, int); static int smhear (void); -static char *EHLOset (char *); +static char *EHLOset (char *) PURE; static int sm_sasl_callback(enum sasl_message_type, unsigned const char *, unsigned int, unsigned char **, unsigned int *, - char **); + void *, char **); int sm_init (char *client, char *server, char *port, int watch, int verbose, @@ -111,10 +92,10 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, const char *oauth_svc, int tls) { int result, sd1; - char *errstr; + char *errstr, *chosen_server; if (watch) - verbose = TRUE; + verbose = true; sm_verbose = verbose; sm_debug = debug; @@ -141,12 +122,21 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, if (user) netsec_set_userid(nsc, user); + if ((sd1 = rclient (server, port, &chosen_server)) == NOTOK) + return RP_BHST; + + SIGNAL (SIGPIPE, SIG_IGN); + + netsec_set_fd(nsc, sd1, sd1); + + netsec_set_hostname(nsc, chosen_server); + if (sm_debug) netsec_set_snoop(nsc, 1); if (sasl) { - if (netsec_set_sasl_params(nsc, server, "smtp", saslmech, - sm_sasl_callback, &errstr) != OK) + if (netsec_set_sasl_params(nsc, "smtp", saslmech, sm_sasl_callback, + NULL, &errstr) != OK) return sm_nerror(errstr); } @@ -155,26 +145,19 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, return sm_ierror("OAuth2 not supported"); } - if ((sd1 = rclient (server, port)) == NOTOK) - return RP_BHST; - - SIGNAL (SIGPIPE, SIG_IGN); - - netsec_set_fd(nsc, sd1, sd1); - - if (tls) { - if (netsec_set_tls(nsc, 1, &errstr) != OK) + if (tls & S_TLSENABLEMASK) { + if (netsec_set_tls(nsc, 1, tls & S_NOVERIFY, &errstr) != OK) return sm_nerror(errstr); } /* - * If tls == 2, that means that the user requested "initial" TLS, - * which happens right after the connection has opened. Do that - * negotiation now + * If tls == S_INITTLS, that means that the user requested + * "initial" TLS, which happens right after the connection has + * opened. Do that negotiation now */ - if (tls == 2) { - if (netsec_negotiate_tls(nsc, &errstr) != OK) { + if (tls & S_INITTLS) { + if (netsec_negotiate_tls(nsc, &errstr) != OK) { sm_end(NOTOK); return sm_nerror(errstr); } @@ -214,7 +197,7 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, * restart the EHLO dialog after TLS negotiation is complete. */ - if (tls == 1) { + if (tls & S_STARTTLS) { if (! EHLOset("STARTTLS")) { sm_end(NOTOK); return sm_ierror("SMTP server does not support TLS"); @@ -283,7 +266,7 @@ sendmail_init (char *client, int watch, int verbose, int debug, int sasl, char *vec[15], *errstr; if (watch) - verbose = TRUE; + verbose = true; sm_verbose = verbose; sm_debug = debug; @@ -308,12 +291,14 @@ sendmail_init (char *client, int watch, int verbose, int debug, int sasl, if (user) netsec_set_userid(nsc, user); + netsec_set_hostname(nsc, client); + if (sm_debug) netsec_set_snoop(nsc, 1); if (sasl) { - if (netsec_set_sasl_params(nsc, client, "smtp", saslmech, - sm_sasl_callback, &errstr) != OK) + if (netsec_set_sasl_params(nsc, "smtp", saslmech, sm_sasl_callback, + NULL, &errstr) != OK) return sm_nerror(errstr); } @@ -325,9 +310,7 @@ sendmail_init (char *client, int watch, int verbose, int debug, int sasl, return sm_ierror ("no pipes"); } - for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++) - sleep (5); - + sm_child = fork(); switch (sm_child) { case NOTOK: close (pdo[0]); @@ -359,7 +342,7 @@ sendmail_init (char *client, int watch, int verbose, int debug, int sasl, execvp (sendmail, vec); fprintf (stderr, "unable to exec "); perror (sendmail); - _exit (-1); /* NOTREACHED */ + _exit(1); /* NOTREACHED */ default: SIGNAL (SIGPIPE, SIG_IGN); @@ -367,7 +350,7 @@ sendmail_init (char *client, int watch, int verbose, int debug, int sasl, close (pdi[1]); close (pdo[0]); - netsec_set_fd(nsc, pdi[i], pdo[1]); + netsec_set_fd(nsc, pdi[0], pdo[1]); netsec_set_timeout(nsc, SM_OPEN); result = smhear (); switch (result) { @@ -403,7 +386,7 @@ sendmail_init (char *client, int watch, int verbose, int debug, int sasl, */ if (sasl) { - char *server_mechs; + char *server_mechs; if (! (server_mechs = EHLOset("AUTH"))) { sm_end(NOTOK); return sm_ierror("SMTP server does not support SASL"); @@ -422,11 +405,16 @@ sendmail_init (char *client, int watch, int verbose, int debug, int sasl, } static int -rclient (char *server, char *service) +rclient (char *server, char *service, char **chosen_server) { int sd; char response[BUFSIZ]; + if (server == NULL) + server = servers; + + *chosen_server = server; + if ((sd = client (server, service, response, sizeof(response), sm_debug)) != NOTOK) return sd; @@ -448,7 +436,7 @@ sm_winit (char *from, int smtputf8, int eightbit) if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) { mail_parameters = " BODY=8BITMIME SMTPUTF8"; } else { - advise (NULL, "SMTP server does not support %s, not sending.\n" + inform("SMTP server does not support %s, not sending.\n" "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.", EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8"); sm_end (NOTOK); @@ -460,7 +448,7 @@ sm_winit (char *from, int smtputf8, int eightbit) if (EHLOset ("8BITMIME")) { mail_parameters = " BODY=8BITMIME"; } else { - advise (NULL, "SMTP server does not support 8BITMIME, not sending.\n" + inform("SMTP server does not support 8BITMIME, not sending.\n" "Suggest encoding message for 7-bit transport by setting your\n" "locale to C, and/or specifying *b64 in mhbuild directives."); sm_end (NOTOK); @@ -489,17 +477,13 @@ sm_wadr (char *mbox, char *host, char *path) { switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>" : "RCPT TO:<%s%s>", - path ? path : "", mbox, host)) { + FENDNULL(path), mbox, host)) { case 250: case 251: sm_addrs++; return RP_OK; case 451: -#ifdef SENDMAILBUG - sm_addrs++; - return RP_OK; -#endif /* SENDMAILBUG */ case 421: case 450: case 452: @@ -526,14 +510,10 @@ sm_waend (void) { switch (smtalk (SM_DATA, "DATA")) { case 354: - sm_nl = TRUE; + sm_nl = true; return RP_OK; case 451: -#ifdef SENDMAILBUG - sm_nl = TRUE; - return RP_OK; -#endif /* SENDMAILBUG */ case 421: return RP_NO; @@ -552,29 +532,51 @@ sm_waend (void) int sm_wtxt (char *buffer, int len) { - int result; + int result, snoopstate; + + if ((snoopstate = netsec_get_snoop(nsc))) + netsec_set_snoop(nsc, 0); result = sm_wstream (buffer, len); - return (result == NOTOK ? RP_BHST : RP_OK); + netsec_set_snoop(nsc, snoopstate); + return result == NOTOK ? RP_BHST : RP_OK; } int sm_wtend (void) { - if (sm_wstream ((char *) NULL, 0) == NOTOK) + int snoopstate; + + if ((snoopstate = netsec_get_snoop(nsc))) + netsec_set_snoop(nsc, 0); + + if (sm_wstream(NULL, 0) == NOTOK) return RP_BHST; + /* + * Because snoop output now happens at flush time, if we are using snoop + * we force an extra flush here. While this introduces some extra network + * traffic, we're only doing it when snoop is in effect so I think it's + * reasonable. + */ + + if (snoopstate) { + char *errstr; + if (netsec_flush(nsc, &errstr) != OK) { + sm_nerror(errstr); + return RP_BHST; + } + netsec_set_snoop(nsc, snoopstate); + } + switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) { case 250: case 251: return RP_OK; case 451: -#ifdef SENDMAILBUG - return RP_OK; -#endif /* SENDMAILBUG */ case 452: default: return RP_NO; @@ -614,7 +616,8 @@ sm_end (int type) case NOTOK: sm_note.code = sm_reply.code; sm_note.length = sm_reply.length; - memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1);/* fall */ + memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1); + /* FALLTHRU */ case DONE: if (smtalk (SM_RSET, "RSET") == 250 && type == DONE) return RP_OK; @@ -644,7 +647,7 @@ sm_end (int type) } if (nsc != NULL) { - netsec_shutdown(nsc, 1); + netsec_shutdown(nsc); nsc = NULL; } @@ -657,7 +660,7 @@ sm_end (int type) status = OK; } - return (status ? RP_BHST : RP_OK); + return status ? RP_BHST : RP_OK; } @@ -743,7 +746,7 @@ sm_wstream (char *buffer, int len) for (bp = buffer; bp && len > 0; bp++, len--) { switch (*bp) { case '\n': - sm_nl = TRUE; + sm_nl = true; if (netsec_write(nsc, "\r", 1, &errstr) != OK) { sm_nerror(errstr); return NOTOK; @@ -755,10 +758,11 @@ sm_wstream (char *buffer, int len) if (netsec_write(nsc, ".", 1, &errstr) != OK) { sm_nerror(errstr); return NOTOK; - } /* FALL THROUGH */ + } + /* FALLTHRU */ default: - sm_nl = FALSE; + sm_nl = false; } if (netsec_write(nsc, bp, 1, &errstr) != OK) { sm_nerror(errstr); @@ -775,7 +779,8 @@ sm_wstream (char *buffer, int len) static int smhear (void) { - int i, code, cont, more; + int i, code; + bool cont, more; size_t buflen, rc; unsigned char *bp; char *rp; @@ -783,7 +788,7 @@ smhear (void) char **ehlo = EHLOkeys, *buffer; if (doingEHLO) { - static int at_least_once = 0; + static bool at_least_once; if (at_least_once) { char *ep; @@ -793,7 +798,7 @@ smhear (void) free (ep); } } else { - at_least_once = 1; + at_least_once = true; } ehlo = EHLOkeys; @@ -807,11 +812,11 @@ again: ; rp = sm_reply.text; rc = sizeof(sm_reply.text) - 1; - for (more = FALSE; (buffer = netsec_readline(nsc, &buflen, - &errstr)) != NULL ; ) { + for (more = false; (buffer = netsec_readline(nsc, &buflen, + &errstr)) != NULL ; ) { if (doingEHLO - && strncmp (buffer, "250", LEN("250")) == 0 + && has_prefix(buffer, "250") && (buffer[3] == '-' || doingEHLO == 2) && buffer[4]) { if (doingEHLO == 2) { @@ -833,13 +838,13 @@ again: ; for (; buflen > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, buflen--) continue; - cont = FALSE; + cont = false; code = atoi ((char *) bp); bp += 3, buflen -= 3; for (; buflen > 0 && isspace (*bp); bp++, buflen--) continue; if (buflen > 0 && *bp == '-') { - cont = TRUE; + cont = true; bp++, buflen--; for (; buflen > 0 && isspace (*bp); bp++, buflen--) continue; @@ -848,7 +853,7 @@ again: ; if (more) { if (code != sm_reply.code || cont) continue; - more = FALSE; + more = false; } else { sm_reply.code = code; more = cont; @@ -873,7 +878,7 @@ again: ; continue; if (sm_reply.code < 100) { if (sm_verbose) { - printf ("%s\n", sm_reply.text); + puts(sm_reply.text); fflush (stdout); } goto again; @@ -893,7 +898,8 @@ char * rp_string (int code) { char *text; - static char buffer[BUFSIZ]; + /* The additional space is to avoid warning from gcc -Wformat-truncation. */ + static char buffer[BUFSIZ + 19]; switch (sm_reply.code != NOTOK ? code : NOTOK) { case RP_AOK: @@ -950,14 +956,14 @@ EHLOset (char *s) for (ehlo = EHLOkeys; *ehlo; ehlo++) { ep = *ehlo; - if (strncmp (ep, s, len) == 0) { + if (has_prefix(ep, s)) { for (ep += len; *ep == ' '; ep++) continue; return ep; } } - return 0; + return NULL; } /* @@ -969,11 +975,12 @@ EHLOset (char *s) static int sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, unsigned int indatalen, unsigned char **outdata, - unsigned int *outdatalen, char **errstr) + unsigned int *outdatalen, void *context, char **errstr) { int rc, snoopoffset; char *mech, *line; size_t len; + NMH_UNUSED(context); switch (mtype) { case NETSEC_SASL_START: @@ -996,15 +1003,19 @@ sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, snoopoffset = 6 + strlen(mech); rc = netsec_printf(nsc, errstr, "AUTH %s %s\r\n", mech, b64data); free(b64data); - netsec_set_snoop_callback(nsc, NULL, NULL); } else { rc = netsec_printf(nsc, errstr, "AUTH %s\r\n", mech); } - if (rc != OK) + if (rc != OK) { + netsec_set_snoop_callback(nsc, NULL, NULL); return NOTOK; + } - if (netsec_flush(nsc, errstr) != OK) + rc = netsec_flush(nsc, errstr); + netsec_set_snoop_callback(nsc, NULL, NULL); + + if (rc != OK) return NOTOK; break; @@ -1028,7 +1039,7 @@ sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, return NOTOK; } - if (strncmp(line, "334 ", 4) != 0) { + if (!has_prefix(line, "334 ")) { netsec_err(errstr, "Improper SASL protocol response: %s", line); return NOTOK; } @@ -1037,7 +1048,7 @@ sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, *outdata = NULL; *outdatalen = 0; } else { - rc = decodeBase64(line + 4, outdata, &len, 0, NULL); + rc = decodeBase64(line + 4, outdata, &len, 0); if (rc != OK) { netsec_err(errstr, "Unable to decode base64 response"); return NOTOK; @@ -1058,14 +1069,16 @@ sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, writeBase64raw(indata, indatalen, b64data); netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL); rc = netsec_printf(nsc, errstr, "%s\r\n", b64data); - netsec_set_snoop_callback(nsc, NULL, NULL); free(b64data); } if (rc != OK) return NOTOK; - if (netsec_flush(nsc, errstr) != OK) + rc = netsec_flush(nsc, errstr); + netsec_set_snoop_callback(nsc, NULL, NULL); + + if (rc != OK) return NOTOK; break; @@ -1077,7 +1090,7 @@ sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, if (line == NULL) return NOTOK; - if (strncmp(line, "235 ", 4) != 0) { + if (!has_prefix(line, "235 ")) { if (len > 4) netsec_err(errstr, "Authentication failed: %s", line + 4); else