]>
diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
1 /* smtp.c -- nmh SMTP interface
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
11 #include <h/signals.h>
15 #include <sys/socket.h>
16 #include "sbr/base64.h"
19 * This module implements an interface to SendMail very similar
20 * to the MMDF mm_(3) routines. The sm_() routines herein talk
21 * SMTP to a sendmail process, mapping SMTP reply codes into
25 #define NBITS ((sizeof (int)) * 8)
27 /* Timeout in seconds for SMTP commands.
28 * Lore has it they must be distinct. */
29 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
32 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
33 #define SM_RCPT 302 /* see above */
34 #define SM_DATA 120 /* see above */
35 #define SM_DOT 600 /* see above */
38 static int sm_addrs
= 0;
39 static int sm_child
= NOTOK
;
40 static int sm_debug
= 0;
41 static bool sm_nl
= true;
42 static int sm_verbose
= 0;
43 static netsec_context
*nsc
= NULL
;
45 static char *sm_noreply
= "No reply text given";
46 static char *sm_moreply
= "; ";
47 static struct smtp sm_reply
;
52 static char *EHLOkeys
[MAXEHLO
+ 1];
57 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
58 const char *, const char *, int);
59 static int sendmail_init (char *, int, int, int, int, const char *,
62 static int rclient (char *, char *, char **);
63 static int sm_ierror (const char *fmt
, ...) CHECK_PRINTF(1, 2);
64 static int sm_nerror (char *);
65 static int smtalk (int time
, char *fmt
, ...) CHECK_PRINTF(2, 3);
66 static int sm_wstream (char *, int);
67 static int smhear (void);
68 static char *EHLOset (char *) PURE
;
69 static int sm_sasl_callback(enum sasl_message_type
, unsigned const char *,
70 unsigned int, unsigned char **, unsigned int *,
74 sm_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
75 int debug
, int sasl
, const char *saslmech
, const char *user
,
76 const char *oauth_svc
, int tls
)
78 if (sm_mts
== MTS_SMTP
)
79 return smtp_init (client
, server
, port
, watch
, verbose
,
80 debug
, sasl
, saslmech
, user
, oauth_svc
, tls
);
82 return sendmail_init (client
, watch
, verbose
, debug
, sasl
,
87 smtp_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
88 int debug
, int sasl
, const char *saslmech
, const char *user
,
89 const char *oauth_svc
, int tls
)
92 char *errstr
, *chosen_server
;
103 if (client
== NULL
|| *client
== '\0') {
107 client
= LocalName(1); /* no clientname -> LocalName */
111 * Last-ditch check just in case client still isn't set to anything
113 if (client
== NULL
|| *client
== '\0')
114 client
= "localhost";
120 netsec_set_userid(nsc
, user
);
122 if ((sd1
= rclient (server
, port
, &chosen_server
)) == NOTOK
)
125 SIGNAL (SIGPIPE
, SIG_IGN
);
127 netsec_set_fd(nsc
, sd1
, sd1
);
129 netsec_set_hostname(nsc
, chosen_server
);
132 netsec_set_snoop(nsc
, 1);
135 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
136 NULL
, &errstr
) != OK
)
137 return sm_nerror(errstr
);
141 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
142 return sm_ierror("OAuth2 not supported");
145 if (tls
& S_TLSENABLEMASK
) {
146 if (netsec_set_tls(nsc
, 1, tls
& S_NOVERIFY
, &errstr
) != OK
)
147 return sm_nerror(errstr
);
151 * If tls == S_INITTLS, that means that the user requested
152 * "initial" TLS, which happens right after the connection has
153 * opened. Do that negotiation now
156 if (tls
& S_INITTLS
) {
157 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
159 return sm_nerror(errstr
);
163 netsec_set_timeout(nsc
, SM_OPEN
);
176 * Give EHLO or HELO command
180 result
= smtalk (SM_HELO
, "EHLO %s", client
);
183 if (result
>= 500 && result
<= 599)
184 result
= smtalk (SM_HELO
, "HELO %s", client
);
192 * If the user requested TLS support, then try to do the STARTTLS command
193 * as part of the initial dialog. Assuming this works, we then need to
194 * restart the EHLO dialog after TLS negotiation is complete.
197 if (tls
& S_STARTTLS
) {
198 if (! EHLOset("STARTTLS")) {
200 return sm_ierror("SMTP server does not support TLS");
203 result
= smtalk(SM_HELO
, "STARTTLS");
211 * Okay, the other side should be waiting for us to start TLS
212 * negotiation. Oblige them.
215 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
217 return sm_nerror(errstr
);
221 result
= smtalk (SM_HELO
, "EHLO %s", client
);
231 * If the user asked for SASL, then check to see if the SMTP server
232 * supports it. Otherwise, error out (because the SMTP server
233 * might have been spoofed; we don't want to just silently not
239 if (! (server_mechs
= EHLOset("AUTH"))) {
241 return sm_ierror("SMTP server does not support SASL");
244 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
246 return sm_nerror(errstr
);
251 if (watch
&& EHLOset ("XVRB"))
252 smtalk (SM_HELO
, "VERB on");
258 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
259 const char *saslmech
, const char *user
)
261 unsigned int i
, result
, vecp
;
263 char *vec
[15], *errstr
;
268 sm_verbose
= verbose
;
273 if (client
== NULL
|| *client
== '\0') {
277 client
= LocalName(1); /* no clientname -> LocalName */
280 * Last-ditch check just in case client still isn't set to anything
282 if (client
== NULL
|| *client
== '\0')
283 client
= "localhost";
289 netsec_set_userid(nsc
, user
);
291 netsec_set_hostname(nsc
, client
);
294 netsec_set_snoop(nsc
, 1);
297 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
298 NULL
, &errstr
) != OK
)
299 return sm_nerror(errstr
);
302 if (pipe (pdi
) == NOTOK
)
303 return sm_ierror ("no pipes");
304 if (pipe (pdo
) == NOTOK
) {
307 return sm_ierror ("no pipes");
317 return sm_ierror ("unable to fork");
320 if (pdo
[0] != fileno (stdin
))
321 dup2 (pdo
[0], fileno (stdin
));
322 if (pdi
[1] != fileno (stdout
))
323 dup2 (pdi
[1], fileno (stdout
));
324 if (pdi
[1] != fileno (stderr
))
325 dup2 (pdi
[1], fileno (stderr
));
326 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
330 vec
[vecp
++] = r1bindex (sendmail
, '/');
332 vec
[vecp
++] = watch
? "-odi" : "-odb";
333 vec
[vecp
++] = "-oem";
339 execvp (sendmail
, vec
);
340 fprintf (stderr
, "unable to exec ");
342 _exit(1); /* NOTREACHED */
345 SIGNAL (SIGPIPE
, SIG_IGN
);
350 netsec_set_fd(nsc
, pdi
[0], pdo
[1]);
351 netsec_set_timeout(nsc
, SM_OPEN
);
363 result
= smtalk (SM_HELO
, "EHLO %s", client
);
366 if (500 <= result
&& result
<= 599)
367 result
= smtalk (SM_HELO
, "HELO %s", client
);
379 * If the user asked for SASL, then check to see if the SMTP server
380 * supports it. Otherwise, error out (because the SMTP server
381 * might have been spoofed; we don't want to just silently not
387 if (! (server_mechs
= EHLOset("AUTH"))) {
389 return sm_ierror("SMTP server does not support SASL");
391 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
393 return sm_nerror(errstr
);
398 smtalk (SM_HELO
, "VERB on");
405 rclient (char *server
, char *service
, char **chosen_server
)
408 char response
[BUFSIZ
];
413 *chosen_server
= server
;
415 if ((sd
= client (server
, service
, response
, sizeof(response
),
419 sm_ierror ("%s", response
);
424 sm_winit (char *from
, int smtputf8
, int eightbit
)
426 const char *mail_parameters
= "";
429 /* Just for information, if an attempt is made to send to an 8-bit
430 address without specifying SMTPUTF8, Gmail responds with
431 555 5.5.2 Syntax error.
432 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
433 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
434 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
436 inform("SMTP server does not support %s, not sending.\n"
437 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
438 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
442 } else if (eightbit
) {
443 /* Comply with RFC 6152, for messages that have any 8-bit characters
445 if (EHLOset ("8BITMIME")) {
446 mail_parameters
= " BODY=8BITMIME";
448 inform("SMTP server does not support 8BITMIME, not sending.\n"
449 "Suggest encoding message for 7-bit transport by setting your\n"
450 "locale to C, and/or specifying *b64 in mhbuild directives.");
456 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
473 sm_wadr (char *mbox
, char *host
, char *path
)
475 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
477 FENDNULL(path
), mbox
, host
)) {
508 switch (smtalk (SM_DATA
, "DATA")) {
530 sm_wtxt (char *buffer
, int len
)
532 int result
, snoopstate
;
534 if ((snoopstate
= netsec_get_snoop(nsc
)))
535 netsec_set_snoop(nsc
, 0);
537 result
= sm_wstream (buffer
, len
);
539 netsec_set_snoop(nsc
, snoopstate
);
540 return result
== NOTOK
? RP_BHST
: RP_OK
;
549 if ((snoopstate
= netsec_get_snoop(nsc
)))
550 netsec_set_snoop(nsc
, 0);
552 if (sm_wstream(NULL
, 0) == NOTOK
)
556 * Because snoop output now happens at flush time, if we are using snoop
557 * we force an extra flush here. While this introduces some extra network
558 * traffic, we're only doing it when snoop is in effect so I think it's
564 if (netsec_flush(nsc
, &errstr
) != OK
) {
568 netsec_set_snoop(nsc
, snoopstate
);
571 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
594 if (sm_mts
== MTS_SENDMAIL_SMTP
) {
610 smtalk (SM_QUIT
, "QUIT");
614 sm_note
.code
= sm_reply
.code
;
615 sm_note
.length
= sm_reply
.length
;
616 memcpy (sm_note
.text
, sm_reply
.text
, sm_reply
.length
+ 1);
619 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
621 if (sm_mts
== MTS_SMTP
)
622 smtalk (SM_QUIT
, "QUIT");
624 /* The SIGPIPE block replaces old calls to discard ().
625 We're not sure what the discard () calls were for,
626 maybe to prevent deadlock on old systems. In any
627 case, blocking SIGPIPE should be harmless.
628 Because the file handles are closed below, leave it
632 sigaddset (&set
, SIGPIPE
);
633 sigprocmask (SIG_BLOCK
, &set
, &oset
);
635 kill (sm_child
, SIGKILL
);
639 sm_reply
.code
= sm_note
.code
;
640 sm_reply
.length
= sm_note
.length
;
641 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
647 netsec_shutdown(nsc
);
651 if (sm_mts
== MTS_SMTP
) {
653 } else if (sm_child
!= NOTOK
) {
654 status
= pidwait (sm_child
, OK
);
660 return status
? RP_BHST
: RP_OK
;
665 sm_ierror (const char *fmt
, ...)
670 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
673 sm_reply
.length
= strlen (sm_reply
.text
);
674 sm_reply
.code
= NOTOK
;
680 * Like sm_ierror, but assume it's an allocated error string we need to free.
684 sm_nerror (char *str
)
686 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
687 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
688 sm_reply
.length
= strlen(sm_reply
.text
);
689 sm_reply
.code
= NOTOK
;
696 smtalk (int time
, char *fmt
, ...)
703 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
707 return sm_nerror(errstr
);
709 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
710 return sm_nerror(errstr
);
712 if (netsec_flush (nsc
, &errstr
) != OK
)
713 return sm_nerror(errstr
);
715 netsec_set_timeout(nsc
, time
);
722 sm_wstream (char *buffer
, int len
)
725 static char lc
= '\0';
729 sm_ierror("No socket opened");
733 if (buffer
== NULL
&& len
== 0) {
736 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
743 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
747 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
755 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
764 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
785 char **ehlo
= EHLOkeys
, *buffer
;
788 static int at_least_once
= 0;
793 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
808 sm_reply
.text
[0] = 0;
810 rc
= sizeof(sm_reply
.text
) - 1;
812 for (more
= false; (buffer
= netsec_readline(nsc
, &buflen
,
813 &errstr
)) != NULL
; ) {
816 && has_prefix(buffer
, "250")
817 && (buffer
[3] == '-' || doingEHLO
== 2)
819 if (doingEHLO
== 2) {
820 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
821 strcpy (*ehlo
++, buffer
+ 4);
823 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
833 bp
= (unsigned char *) buffer
;
835 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
839 code
= atoi ((char *) bp
);
840 bp
+= 3, buflen
-= 3;
841 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
843 if (buflen
> 0 && *bp
== '-') {
846 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
851 if (code
!= sm_reply
.code
|| cont
)
855 sm_reply
.code
= code
;
858 bp
= (unsigned char *) sm_noreply
;
859 buflen
= strlen (sm_noreply
);
863 if ((i
= min (buflen
, rc
)) > 0) {
867 i
= strlen(sm_moreply
);
868 if (more
&& (int) rc
> i
+ 1) {
869 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
876 if (sm_reply
.code
< 100) {
884 sm_reply
.length
= rp
- sm_reply
.text
;
885 sm_reply
.text
[sm_reply
.length
] = 0;
886 return sm_reply
.code
;
898 /* The additional space is to avoid warning from gcc -Wformat-truncation. */
899 static char buffer
[BUFSIZ
+ 19];
901 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
921 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
941 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
942 text
, sm_reply
.code
, sm_reply
.text
);
954 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
956 if (has_prefix(ep
, s
)) {
957 for (ep
+= len
; *ep
== ' '; ep
++)
967 * Our SASL callback; we are either given SASL tokens to generate network
968 * protocols messages for, or we decode incoming protocol messages and
969 * convert them to binary SASL tokens to pass up into the SASL library.
973 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
974 unsigned int indatalen
, unsigned char **outdata
,
975 unsigned int *outdatalen
, void *context
, char **errstr
)
983 case NETSEC_SASL_START
:
985 * Generate an AUTH message; if we were given an input token
986 * then generate a an AUTH message that includes the initial
990 mech
= netsec_get_sasl_mechanism(nsc
);
995 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
996 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
998 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
1000 snoopoffset
= 6 + strlen(mech
);
1001 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
1004 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1008 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1012 rc
= netsec_flush(nsc
, errstr
);
1013 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1020 case NETSEC_SASL_READ
:
1022 * Read in a line that should contain a 334 response code, followed
1023 * by base64 response data.
1026 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1028 line
= netsec_readline(nsc
, &len
, errstr
);
1029 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1035 netsec_err(errstr
, "Invalid format for SASL response");
1039 if (!has_prefix(line
, "334 ")) {
1040 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1048 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1050 netsec_err(errstr
, "Unable to decode base64 response");
1057 case NETSEC_SASL_WRITE
:
1059 * The output encoding is pretty simple, so this is easy.
1061 if (indatalen
== 0) {
1062 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1064 unsigned char *b64data
;
1065 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1066 writeBase64raw(indata
, indatalen
, b64data
);
1067 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1068 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1075 rc
= netsec_flush(nsc
, errstr
);
1076 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1082 case NETSEC_SASL_FINISH
:
1084 * Finish the protocol; we're looking for a 235 message.
1086 line
= netsec_readline(nsc
, &len
, errstr
);
1090 if (!has_prefix(line
, "235 ")) {
1092 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1094 netsec_err(errstr
, "Authentication failed: %s", line
);
1099 case NETSEC_SASL_CANCEL
:
1101 * Cancel the SASL exchange; this is done by sending a single "*".
1103 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1105 rc
= netsec_flush(nsc
, errstr
);