]>
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.
12 #include "h/signals.h"
16 #include <sys/socket.h>
17 #include "sbr/base64.h"
20 * This module implements an interface to SendMail very similar
21 * to the MMDF mm_(3) routines. The sm_() routines herein talk
22 * SMTP to a sendmail process, mapping SMTP reply codes into
26 #define NBITS ((sizeof (int)) * 8)
28 /* Timeout in seconds for SMTP commands.
29 * Lore has it they must be distinct. */
30 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
33 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
34 #define SM_RCPT 302 /* see above */
35 #define SM_DATA 120 /* see above */
36 #define SM_DOT 600 /* see above */
39 static int sm_addrs
= 0;
40 static int sm_child
= NOTOK
;
41 static int sm_debug
= 0;
42 static bool sm_nl
= true;
43 static int sm_verbose
= 0;
44 static netsec_context
*nsc
= NULL
;
46 static char *sm_noreply
= "No reply text given";
47 static char *sm_moreply
= "; ";
48 static struct smtp sm_reply
;
53 static char *EHLOkeys
[MAXEHLO
+ 1];
58 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
59 const char *, const char *, int);
60 static int sendmail_init (char *, int, int, int, int, const char *,
63 static int rclient (char *, char *, char **);
64 static int sm_ierror (const char *fmt
, ...) CHECK_PRINTF(1, 2);
65 static int sm_nerror (char *);
66 static int smtalk (int time
, char *fmt
, ...) CHECK_PRINTF(2, 3);
67 static int sm_wstream (char *, int);
68 static int smhear (void);
69 static char *EHLOset (char *) PURE
;
70 static int sm_sasl_callback(enum sasl_message_type
, unsigned const char *,
71 unsigned int, unsigned char **, unsigned int *,
75 sm_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
76 int debug
, int sasl
, const char *saslmech
, const char *user
,
77 const char *oauth_svc
, int tls
)
79 if (sm_mts
== MTS_SMTP
)
80 return smtp_init (client
, server
, port
, watch
, verbose
,
81 debug
, sasl
, saslmech
, user
, oauth_svc
, tls
);
83 return sendmail_init (client
, watch
, verbose
, debug
, sasl
,
88 smtp_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
89 int debug
, int sasl
, const char *saslmech
, const char *user
,
90 const char *oauth_svc
, int tls
)
93 char *errstr
, *chosen_server
;
104 if (client
== NULL
|| *client
== '\0') {
108 client
= LocalName(1); /* no clientname -> LocalName */
112 * Last-ditch check just in case client still isn't set to anything
114 if (client
== NULL
|| *client
== '\0')
115 client
= "localhost";
121 netsec_set_userid(nsc
, user
);
123 if ((sd1
= rclient (server
, port
, &chosen_server
)) == NOTOK
)
126 SIGNAL (SIGPIPE
, SIG_IGN
);
128 netsec_set_fd(nsc
, sd1
, sd1
);
130 netsec_set_hostname(nsc
, chosen_server
);
133 netsec_set_snoop(nsc
, 1);
136 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
137 NULL
, &errstr
) != OK
)
138 return sm_nerror(errstr
);
142 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
143 return sm_ierror("OAuth2 not supported");
146 if (tls
& S_TLSENABLEMASK
) {
147 if (netsec_set_tls(nsc
, 1, tls
& S_NOVERIFY
, &errstr
) != OK
)
148 return sm_nerror(errstr
);
152 * If tls == S_INITTLS, that means that the user requested
153 * "initial" TLS, which happens right after the connection has
154 * opened. Do that negotiation now
157 if (tls
& S_INITTLS
) {
158 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
160 return sm_nerror(errstr
);
164 netsec_set_timeout(nsc
, SM_OPEN
);
177 * Give EHLO or HELO command
181 result
= smtalk (SM_HELO
, "EHLO %s", client
);
184 if (result
>= 500 && result
<= 599)
185 result
= smtalk (SM_HELO
, "HELO %s", client
);
193 * If the user requested TLS support, then try to do the STARTTLS command
194 * as part of the initial dialog. Assuming this works, we then need to
195 * restart the EHLO dialog after TLS negotiation is complete.
198 if (tls
& S_STARTTLS
) {
199 if (! EHLOset("STARTTLS")) {
201 return sm_ierror("SMTP server does not support TLS");
204 result
= smtalk(SM_HELO
, "STARTTLS");
212 * Okay, the other side should be waiting for us to start TLS
213 * negotiation. Oblige them.
216 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
218 return sm_nerror(errstr
);
222 result
= smtalk (SM_HELO
, "EHLO %s", client
);
232 * If the user asked for SASL, then check to see if the SMTP server
233 * supports it. Otherwise, error out (because the SMTP server
234 * might have been spoofed; we don't want to just silently not
240 if (! (server_mechs
= EHLOset("AUTH"))) {
242 return sm_ierror("SMTP server does not support SASL");
245 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
247 return sm_nerror(errstr
);
252 if (watch
&& EHLOset ("XVRB"))
253 smtalk (SM_HELO
, "VERB on");
259 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
260 const char *saslmech
, const char *user
)
262 unsigned int i
, result
, vecp
;
264 char *vec
[15], *errstr
;
269 sm_verbose
= verbose
;
274 if (client
== NULL
|| *client
== '\0') {
278 client
= LocalName(1); /* no clientname -> LocalName */
281 * Last-ditch check just in case client still isn't set to anything
283 if (client
== NULL
|| *client
== '\0')
284 client
= "localhost";
290 netsec_set_userid(nsc
, user
);
292 netsec_set_hostname(nsc
, client
);
295 netsec_set_snoop(nsc
, 1);
298 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
299 NULL
, &errstr
) != OK
)
300 return sm_nerror(errstr
);
303 if (pipe (pdi
) == NOTOK
)
304 return sm_ierror ("no pipes");
305 if (pipe (pdo
) == NOTOK
) {
308 return sm_ierror ("no pipes");
318 return sm_ierror ("unable to fork");
321 if (pdo
[0] != fileno (stdin
))
322 dup2 (pdo
[0], fileno (stdin
));
323 if (pdi
[1] != fileno (stdout
))
324 dup2 (pdi
[1], fileno (stdout
));
325 if (pdi
[1] != fileno (stderr
))
326 dup2 (pdi
[1], fileno (stderr
));
327 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
331 vec
[vecp
++] = r1bindex (sendmail
, '/');
333 vec
[vecp
++] = watch
? "-odi" : "-odb";
334 vec
[vecp
++] = "-oem";
340 execvp (sendmail
, vec
);
341 fprintf (stderr
, "unable to exec ");
343 _exit(1); /* NOTREACHED */
346 SIGNAL (SIGPIPE
, SIG_IGN
);
351 netsec_set_fd(nsc
, pdi
[0], pdo
[1]);
352 netsec_set_timeout(nsc
, SM_OPEN
);
364 result
= smtalk (SM_HELO
, "EHLO %s", client
);
367 if (500 <= result
&& result
<= 599)
368 result
= smtalk (SM_HELO
, "HELO %s", client
);
380 * If the user asked for SASL, then check to see if the SMTP server
381 * supports it. Otherwise, error out (because the SMTP server
382 * might have been spoofed; we don't want to just silently not
388 if (! (server_mechs
= EHLOset("AUTH"))) {
390 return sm_ierror("SMTP server does not support SASL");
392 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
394 return sm_nerror(errstr
);
399 smtalk (SM_HELO
, "VERB on");
406 rclient (char *server
, char *service
, char **chosen_server
)
409 char response
[BUFSIZ
];
414 *chosen_server
= server
;
416 if ((sd
= client (server
, service
, response
, sizeof(response
),
420 sm_ierror ("%s", response
);
425 sm_winit (char *from
, int smtputf8
, int eightbit
)
427 const char *mail_parameters
= "";
430 /* Just for information, if an attempt is made to send to an 8-bit
431 address without specifying SMTPUTF8, Gmail responds with
432 555 5.5.2 Syntax error.
433 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
434 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
435 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
437 inform("SMTP server does not support %s, not sending.\n"
438 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
439 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
443 } else if (eightbit
) {
444 /* Comply with RFC 6152, for messages that have any 8-bit characters
446 if (EHLOset ("8BITMIME")) {
447 mail_parameters
= " BODY=8BITMIME";
449 inform("SMTP server does not support 8BITMIME, not sending.\n"
450 "Suggest encoding message for 7-bit transport by setting your\n"
451 "locale to C, and/or specifying *b64 in mhbuild directives.");
457 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
474 sm_wadr (char *mbox
, char *host
, char *path
)
476 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
478 FENDNULL(path
), mbox
, host
)) {
509 switch (smtalk (SM_DATA
, "DATA")) {
531 sm_wtxt (char *buffer
, int len
)
533 int result
, snoopstate
;
535 if ((snoopstate
= netsec_get_snoop(nsc
)))
536 netsec_set_snoop(nsc
, 0);
538 result
= sm_wstream (buffer
, len
);
540 netsec_set_snoop(nsc
, snoopstate
);
541 return result
== NOTOK
? RP_BHST
: RP_OK
;
550 if ((snoopstate
= netsec_get_snoop(nsc
)))
551 netsec_set_snoop(nsc
, 0);
553 if (sm_wstream(NULL
, 0) == NOTOK
)
557 * Because snoop output now happens at flush time, if we are using snoop
558 * we force an extra flush here. While this introduces some extra network
559 * traffic, we're only doing it when snoop is in effect so I think it's
565 if (netsec_flush(nsc
, &errstr
) != OK
) {
569 netsec_set_snoop(nsc
, snoopstate
);
572 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
595 if (sm_mts
== MTS_SENDMAIL_SMTP
) {
611 smtalk (SM_QUIT
, "QUIT");
615 sm_note
.code
= sm_reply
.code
;
616 sm_note
.length
= sm_reply
.length
;
617 memcpy (sm_note
.text
, sm_reply
.text
, sm_reply
.length
+ 1);
620 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
622 if (sm_mts
== MTS_SMTP
)
623 smtalk (SM_QUIT
, "QUIT");
625 /* The SIGPIPE block replaces old calls to discard ().
626 We're not sure what the discard () calls were for,
627 maybe to prevent deadlock on old systems. In any
628 case, blocking SIGPIPE should be harmless.
629 Because the file handles are closed below, leave it
633 sigaddset (&set
, SIGPIPE
);
634 sigprocmask (SIG_BLOCK
, &set
, &oset
);
636 kill (sm_child
, SIGKILL
);
640 sm_reply
.code
= sm_note
.code
;
641 sm_reply
.length
= sm_note
.length
;
642 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
648 netsec_shutdown(nsc
);
652 if (sm_mts
== MTS_SMTP
) {
654 } else if (sm_child
!= NOTOK
) {
655 status
= pidwait (sm_child
, OK
);
661 return status
? RP_BHST
: RP_OK
;
666 sm_ierror (const char *fmt
, ...)
671 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
674 sm_reply
.length
= strlen (sm_reply
.text
);
675 sm_reply
.code
= NOTOK
;
681 * Like sm_ierror, but assume it's an allocated error string we need to free.
685 sm_nerror (char *str
)
687 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
688 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
689 sm_reply
.length
= strlen(sm_reply
.text
);
690 sm_reply
.code
= NOTOK
;
697 smtalk (int time
, char *fmt
, ...)
704 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
708 return sm_nerror(errstr
);
710 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
711 return sm_nerror(errstr
);
713 if (netsec_flush (nsc
, &errstr
) != OK
)
714 return sm_nerror(errstr
);
716 netsec_set_timeout(nsc
, time
);
723 sm_wstream (char *buffer
, int len
)
726 static char lc
= '\0';
730 sm_ierror("No socket opened");
734 if (buffer
== NULL
&& len
== 0) {
737 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
744 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
748 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
756 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
765 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
786 char **ehlo
= EHLOkeys
, *buffer
;
789 static bool at_least_once
;
794 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
799 at_least_once
= true;
809 sm_reply
.text
[0] = 0;
811 rc
= sizeof(sm_reply
.text
) - 1;
813 for (more
= false; (buffer
= netsec_readline(nsc
, &buflen
,
814 &errstr
)) != NULL
; ) {
817 && has_prefix(buffer
, "250")
818 && (buffer
[3] == '-' || doingEHLO
== 2)
820 if (doingEHLO
== 2) {
821 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
822 strcpy (*ehlo
++, buffer
+ 4);
824 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
834 bp
= (unsigned char *) buffer
;
836 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
840 code
= atoi ((char *) bp
);
841 bp
+= 3, buflen
-= 3;
842 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
844 if (buflen
> 0 && *bp
== '-') {
847 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
852 if (code
!= sm_reply
.code
|| cont
)
856 sm_reply
.code
= code
;
859 bp
= (unsigned char *) sm_noreply
;
860 buflen
= strlen (sm_noreply
);
864 if ((i
= min (buflen
, rc
)) > 0) {
868 i
= strlen(sm_moreply
);
869 if (more
&& (int) rc
> i
+ 1) {
870 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
877 if (sm_reply
.code
< 100) {
885 sm_reply
.length
= rp
- sm_reply
.text
;
886 sm_reply
.text
[sm_reply
.length
] = 0;
887 return sm_reply
.code
;
899 /* The additional space is to avoid warning from gcc -Wformat-truncation. */
900 static char buffer
[BUFSIZ
+ 19];
902 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
922 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
942 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
943 text
, sm_reply
.code
, sm_reply
.text
);
955 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
957 if (has_prefix(ep
, s
)) {
958 for (ep
+= len
; *ep
== ' '; ep
++)
968 * Our SASL callback; we are either given SASL tokens to generate network
969 * protocols messages for, or we decode incoming protocol messages and
970 * convert them to binary SASL tokens to pass up into the SASL library.
974 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
975 unsigned int indatalen
, unsigned char **outdata
,
976 unsigned int *outdatalen
, void *context
, char **errstr
)
984 case NETSEC_SASL_START
:
986 * Generate an AUTH message; if we were given an input token
987 * then generate a an AUTH message that includes the initial
991 mech
= netsec_get_sasl_mechanism(nsc
);
996 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
997 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
999 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
1001 snoopoffset
= 6 + strlen(mech
);
1002 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
1005 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1009 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1013 rc
= netsec_flush(nsc
, errstr
);
1014 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1021 case NETSEC_SASL_READ
:
1023 * Read in a line that should contain a 334 response code, followed
1024 * by base64 response data.
1027 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1029 line
= netsec_readline(nsc
, &len
, errstr
);
1030 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1036 netsec_err(errstr
, "Invalid format for SASL response");
1040 if (!has_prefix(line
, "334 ")) {
1041 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1049 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1051 netsec_err(errstr
, "Unable to decode base64 response");
1058 case NETSEC_SASL_WRITE
:
1060 * The output encoding is pretty simple, so this is easy.
1062 if (indatalen
== 0) {
1063 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1065 unsigned char *b64data
;
1066 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1067 writeBase64raw(indata
, indatalen
, b64data
);
1068 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1069 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1076 rc
= netsec_flush(nsc
, errstr
);
1077 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1083 case NETSEC_SASL_FINISH
:
1085 * Finish the protocol; we're looking for a 235 message.
1087 line
= netsec_readline(nsc
, &len
, errstr
);
1091 if (!has_prefix(line
, "235 ")) {
1093 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1095 netsec_err(errstr
, "Authentication failed: %s", line
);
1100 case NETSEC_SASL_CANCEL
:
1102 * Cancel the SASL exchange; this is done by sending a single "*".
1104 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1106 rc
= netsec_flush(nsc
, errstr
);