]>
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.
9 #include "sbr/client.h"
10 #include "sbr/error.h"
13 #include "h/signals.h"
17 #include <sys/socket.h>
18 #include "sbr/base64.h"
21 * This module implements an interface to SendMail very similar
22 * to the MMDF mm_(3) routines. The sm_() routines herein talk
23 * SMTP to a sendmail process, mapping SMTP reply codes into
27 #define NBITS ((sizeof (int)) * 8)
29 /* Timeout in seconds for SMTP commands.
30 * Lore has it they must be distinct. */
31 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
34 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
35 #define SM_RCPT 302 /* see above */
36 #define SM_DATA 120 /* see above */
37 #define SM_DOT 600 /* see above */
40 static int sm_addrs
= 0;
41 static int sm_child
= NOTOK
;
42 static int sm_debug
= 0;
43 static bool sm_nl
= true;
44 static int sm_verbose
= 0;
45 static netsec_context
*nsc
= NULL
;
47 static char *sm_noreply
= "No reply text given";
48 static char *sm_moreply
= "; ";
49 static struct smtp sm_reply
;
54 static char *EHLOkeys
[MAXEHLO
+ 1];
59 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
60 const char *, const char *, int);
61 static int sendmail_init (char *, int, int, int, int, const char *,
64 static int rclient (char *, char *, char **);
65 static int sm_ierror (const char *fmt
, ...) CHECK_PRINTF(1, 2);
66 static int sm_nerror (char *);
67 static int smtalk (int time
, char *fmt
, ...) CHECK_PRINTF(2, 3);
68 static int sm_wstream (char *, int);
69 static int smhear (void);
70 static char *EHLOset (char *) PURE
;
71 static int sm_sasl_callback(enum sasl_message_type
, unsigned const char *,
72 unsigned int, unsigned char **, unsigned int *,
76 sm_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
77 int debug
, int sasl
, const char *saslmech
, const char *user
,
78 const char *oauth_svc
, int tls
)
80 if (sm_mts
== MTS_SMTP
)
81 return smtp_init (client
, server
, port
, watch
, verbose
,
82 debug
, sasl
, saslmech
, user
, oauth_svc
, tls
);
84 return sendmail_init (client
, watch
, verbose
, debug
, sasl
,
89 smtp_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
90 int debug
, int sasl
, const char *saslmech
, const char *user
,
91 const char *oauth_svc
, int tls
)
94 char *errstr
, *chosen_server
;
105 if (client
== NULL
|| *client
== '\0') {
109 client
= LocalName(1); /* no clientname -> LocalName */
113 * Last-ditch check just in case client still isn't set to anything
115 if (client
== NULL
|| *client
== '\0')
116 client
= "localhost";
122 netsec_set_userid(nsc
, user
);
124 if ((sd1
= rclient (server
, port
, &chosen_server
)) == NOTOK
)
127 SIGNAL (SIGPIPE
, SIG_IGN
);
129 netsec_set_fd(nsc
, sd1
, sd1
);
131 netsec_set_hostname(nsc
, chosen_server
);
134 netsec_set_snoop(nsc
, 1);
137 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
138 NULL
, &errstr
) != OK
)
139 return sm_nerror(errstr
);
143 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
144 return sm_ierror("OAuth2 not supported");
147 if (tls
& S_TLSENABLEMASK
) {
148 if (netsec_set_tls(nsc
, 1, tls
& S_NOVERIFY
, &errstr
) != OK
)
149 return sm_nerror(errstr
);
153 * If tls == S_INITTLS, that means that the user requested
154 * "initial" TLS, which happens right after the connection has
155 * opened. Do that negotiation now
158 if (tls
& S_INITTLS
) {
159 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
161 return sm_nerror(errstr
);
165 netsec_set_timeout(nsc
, SM_OPEN
);
178 * Give EHLO or HELO command
182 result
= smtalk (SM_HELO
, "EHLO %s", client
);
185 if (result
>= 500 && result
<= 599)
186 result
= smtalk (SM_HELO
, "HELO %s", client
);
194 * If the user requested TLS support, then try to do the STARTTLS command
195 * as part of the initial dialog. Assuming this works, we then need to
196 * restart the EHLO dialog after TLS negotiation is complete.
199 if (tls
& S_STARTTLS
) {
200 if (! EHLOset("STARTTLS")) {
202 return sm_ierror("SMTP server does not support TLS");
205 result
= smtalk(SM_HELO
, "STARTTLS");
213 * Okay, the other side should be waiting for us to start TLS
214 * negotiation. Oblige them.
217 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
219 return sm_nerror(errstr
);
223 result
= smtalk (SM_HELO
, "EHLO %s", client
);
233 * If the user asked for SASL, then check to see if the SMTP server
234 * supports it. Otherwise, error out (because the SMTP server
235 * might have been spoofed; we don't want to just silently not
241 if (! (server_mechs
= EHLOset("AUTH"))) {
243 return sm_ierror("SMTP server does not support SASL");
246 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
248 return sm_nerror(errstr
);
253 if (watch
&& EHLOset ("XVRB"))
254 smtalk (SM_HELO
, "VERB on");
260 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
261 const char *saslmech
, const char *user
)
263 unsigned int i
, result
, vecp
;
265 char *vec
[15], *errstr
;
270 sm_verbose
= verbose
;
275 if (client
== NULL
|| *client
== '\0') {
279 client
= LocalName(1); /* no clientname -> LocalName */
282 * Last-ditch check just in case client still isn't set to anything
284 if (client
== NULL
|| *client
== '\0')
285 client
= "localhost";
291 netsec_set_userid(nsc
, user
);
293 netsec_set_hostname(nsc
, client
);
296 netsec_set_snoop(nsc
, 1);
299 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
300 NULL
, &errstr
) != OK
)
301 return sm_nerror(errstr
);
304 if (pipe (pdi
) == NOTOK
)
305 return sm_ierror ("no pipes");
306 if (pipe (pdo
) == NOTOK
) {
309 return sm_ierror ("no pipes");
319 return sm_ierror ("unable to fork");
322 if (pdo
[0] != fileno (stdin
))
323 dup2 (pdo
[0], fileno (stdin
));
324 if (pdi
[1] != fileno (stdout
))
325 dup2 (pdi
[1], fileno (stdout
));
326 if (pdi
[1] != fileno (stderr
))
327 dup2 (pdi
[1], fileno (stderr
));
328 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
332 vec
[vecp
++] = r1bindex (sendmail
, '/');
334 vec
[vecp
++] = watch
? "-odi" : "-odb";
335 vec
[vecp
++] = "-oem";
341 execvp (sendmail
, vec
);
342 fprintf (stderr
, "unable to exec ");
344 _exit(1); /* NOTREACHED */
347 SIGNAL (SIGPIPE
, SIG_IGN
);
352 netsec_set_fd(nsc
, pdi
[0], pdo
[1]);
353 netsec_set_timeout(nsc
, SM_OPEN
);
365 result
= smtalk (SM_HELO
, "EHLO %s", client
);
368 if (500 <= result
&& result
<= 599)
369 result
= smtalk (SM_HELO
, "HELO %s", client
);
381 * If the user asked for SASL, then check to see if the SMTP server
382 * supports it. Otherwise, error out (because the SMTP server
383 * might have been spoofed; we don't want to just silently not
389 if (! (server_mechs
= EHLOset("AUTH"))) {
391 return sm_ierror("SMTP server does not support SASL");
393 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
395 return sm_nerror(errstr
);
400 smtalk (SM_HELO
, "VERB on");
407 rclient (char *server
, char *service
, char **chosen_server
)
410 char response
[BUFSIZ
];
415 *chosen_server
= server
;
417 if ((sd
= client (server
, service
, response
, sizeof(response
),
421 sm_ierror ("%s", response
);
426 sm_winit (char *from
, int smtputf8
, int eightbit
)
428 const char *mail_parameters
= "";
431 /* Just for information, if an attempt is made to send to an 8-bit
432 address without specifying SMTPUTF8, Gmail responds with
433 555 5.5.2 Syntax error.
434 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
435 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
436 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
438 inform("SMTP server does not support %s, not sending.\n"
439 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
440 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
444 } else if (eightbit
) {
445 /* Comply with RFC 6152, for messages that have any 8-bit characters
447 if (EHLOset ("8BITMIME")) {
448 mail_parameters
= " BODY=8BITMIME";
450 inform("SMTP server does not support 8BITMIME, not sending.\n"
451 "Suggest encoding message for 7-bit transport by setting your\n"
452 "locale to C, and/or specifying *b64 in mhbuild directives.");
458 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
475 sm_wadr (char *mbox
, char *host
, char *path
)
477 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
479 FENDNULL(path
), mbox
, host
)) {
510 switch (smtalk (SM_DATA
, "DATA")) {
532 sm_wtxt (char *buffer
, int len
)
534 int result
, snoopstate
;
536 if ((snoopstate
= netsec_get_snoop(nsc
)))
537 netsec_set_snoop(nsc
, 0);
539 result
= sm_wstream (buffer
, len
);
541 netsec_set_snoop(nsc
, snoopstate
);
542 return result
== NOTOK
? RP_BHST
: RP_OK
;
551 if ((snoopstate
= netsec_get_snoop(nsc
)))
552 netsec_set_snoop(nsc
, 0);
554 if (sm_wstream(NULL
, 0) == NOTOK
)
558 * Because snoop output now happens at flush time, if we are using snoop
559 * we force an extra flush here. While this introduces some extra network
560 * traffic, we're only doing it when snoop is in effect so I think it's
566 if (netsec_flush(nsc
, &errstr
) != OK
) {
570 netsec_set_snoop(nsc
, snoopstate
);
573 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
596 if (sm_mts
== MTS_SENDMAIL_SMTP
) {
612 smtalk (SM_QUIT
, "QUIT");
616 sm_note
.code
= sm_reply
.code
;
617 sm_note
.length
= sm_reply
.length
;
618 memcpy (sm_note
.text
, sm_reply
.text
, sm_reply
.length
+ 1);
621 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
623 if (sm_mts
== MTS_SMTP
)
624 smtalk (SM_QUIT
, "QUIT");
626 /* The SIGPIPE block replaces old calls to discard ().
627 We're not sure what the discard () calls were for,
628 maybe to prevent deadlock on old systems. In any
629 case, blocking SIGPIPE should be harmless.
630 Because the file handles are closed below, leave it
634 sigaddset (&set
, SIGPIPE
);
635 sigprocmask (SIG_BLOCK
, &set
, &oset
);
637 kill (sm_child
, SIGKILL
);
641 sm_reply
.code
= sm_note
.code
;
642 sm_reply
.length
= sm_note
.length
;
643 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
649 netsec_shutdown(nsc
);
653 if (sm_mts
== MTS_SMTP
) {
655 } else if (sm_child
!= NOTOK
) {
656 status
= pidwait (sm_child
, OK
);
662 return status
? RP_BHST
: RP_OK
;
667 sm_ierror (const char *fmt
, ...)
672 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
675 sm_reply
.length
= strlen (sm_reply
.text
);
676 sm_reply
.code
= NOTOK
;
682 * Like sm_ierror, but assume it's an allocated error string we need to free.
686 sm_nerror (char *str
)
688 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
689 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
690 sm_reply
.length
= strlen(sm_reply
.text
);
691 sm_reply
.code
= NOTOK
;
698 smtalk (int time
, char *fmt
, ...)
705 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
709 return sm_nerror(errstr
);
711 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
712 return sm_nerror(errstr
);
714 if (netsec_flush (nsc
, &errstr
) != OK
)
715 return sm_nerror(errstr
);
717 netsec_set_timeout(nsc
, time
);
724 sm_wstream (char *buffer
, int len
)
727 static char lc
= '\0';
731 sm_ierror("No socket opened");
735 if (buffer
== NULL
&& len
== 0) {
738 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
745 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
749 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
757 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
766 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
787 char **ehlo
= EHLOkeys
, *buffer
;
790 static bool at_least_once
;
795 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
800 at_least_once
= true;
810 sm_reply
.text
[0] = 0;
812 rc
= sizeof(sm_reply
.text
) - 1;
814 for (more
= false; (buffer
= netsec_readline(nsc
, &buflen
,
815 &errstr
)) != NULL
; ) {
818 && has_prefix(buffer
, "250")
819 && (buffer
[3] == '-' || doingEHLO
== 2)
821 if (doingEHLO
== 2) {
822 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
823 strcpy (*ehlo
++, buffer
+ 4);
825 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
835 bp
= (unsigned char *) buffer
;
837 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
841 code
= atoi ((char *) bp
);
842 bp
+= 3, buflen
-= 3;
843 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
845 if (buflen
> 0 && *bp
== '-') {
848 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
853 if (code
!= sm_reply
.code
|| cont
)
857 sm_reply
.code
= code
;
860 bp
= (unsigned char *) sm_noreply
;
861 buflen
= strlen (sm_noreply
);
865 if ((i
= min (buflen
, rc
)) > 0) {
869 i
= strlen(sm_moreply
);
870 if (more
&& (int) rc
> i
+ 1) {
871 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
878 if (sm_reply
.code
< 100) {
886 sm_reply
.length
= rp
- sm_reply
.text
;
887 sm_reply
.text
[sm_reply
.length
] = 0;
888 return sm_reply
.code
;
900 /* The additional space is to avoid warning from gcc -Wformat-truncation. */
901 static char buffer
[BUFSIZ
+ 19];
903 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
923 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
943 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
944 text
, sm_reply
.code
, sm_reply
.text
);
956 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
958 if (has_prefix(ep
, s
)) {
959 for (ep
+= len
; *ep
== ' '; ep
++)
969 * Our SASL callback; we are either given SASL tokens to generate network
970 * protocols messages for, or we decode incoming protocol messages and
971 * convert them to binary SASL tokens to pass up into the SASL library.
975 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
976 unsigned int indatalen
, unsigned char **outdata
,
977 unsigned int *outdatalen
, void *context
, char **errstr
)
985 case NETSEC_SASL_START
:
987 * Generate an AUTH message; if we were given an input token
988 * then generate a an AUTH message that includes the initial
992 mech
= netsec_get_sasl_mechanism(nsc
);
997 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
998 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
1000 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
1002 snoopoffset
= 6 + strlen(mech
);
1003 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
1006 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1010 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1014 rc
= netsec_flush(nsc
, errstr
);
1015 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1022 case NETSEC_SASL_READ
:
1024 * Read in a line that should contain a 334 response code, followed
1025 * by base64 response data.
1028 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1030 line
= netsec_readline(nsc
, &len
, errstr
);
1031 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1037 netsec_err(errstr
, "Invalid format for SASL response");
1041 if (!has_prefix(line
, "334 ")) {
1042 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1050 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1052 netsec_err(errstr
, "Unable to decode base64 response");
1059 case NETSEC_SASL_WRITE
:
1061 * The output encoding is pretty simple, so this is easy.
1063 if (indatalen
== 0) {
1064 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1066 unsigned char *b64data
;
1067 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1068 writeBase64raw(indata
, indatalen
, b64data
);
1069 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1070 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1077 rc
= netsec_flush(nsc
, errstr
);
1078 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1084 case NETSEC_SASL_FINISH
:
1086 * Finish the protocol; we're looking for a 235 message.
1088 line
= netsec_readline(nsc
, &len
, errstr
);
1092 if (!has_prefix(line
, "235 ")) {
1094 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1096 netsec_err(errstr
, "Authentication failed: %s", line
);
1101 case NETSEC_SASL_CANCEL
:
1103 * Cancel the SASL exchange; this is done by sending a single "*".
1105 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1107 rc
= netsec_flush(nsc
, errstr
);