]>
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/r1bindex.h"
10 #include "sbr/client.h"
11 #include "sbr/error.h"
14 #include "h/signals.h"
18 #include <sys/socket.h>
19 #include "sbr/base64.h"
22 * This module implements an interface to SendMail very similar
23 * to the MMDF mm_(3) routines. The sm_() routines herein talk
24 * SMTP to a sendmail process, mapping SMTP reply codes into
28 #define NBITS ((sizeof (int)) * 8)
30 /* Timeout in seconds for SMTP commands.
31 * Lore has it they must be distinct. */
32 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
35 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
36 #define SM_RCPT 302 /* see above */
37 #define SM_DATA 120 /* see above */
38 #define SM_DOT 600 /* see above */
41 static int sm_addrs
= 0;
42 static int sm_child
= NOTOK
;
43 static int sm_debug
= 0;
44 static bool sm_nl
= true;
45 static int sm_verbose
= 0;
46 static netsec_context
*nsc
= NULL
;
48 static char *sm_noreply
= "No reply text given";
49 static char *sm_moreply
= "; ";
50 static struct smtp sm_reply
;
55 static char *EHLOkeys
[MAXEHLO
+ 1];
60 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
61 const char *, const char *, int);
62 static int sendmail_init (char *, int, int, int, int, const char *,
65 static int rclient (char *, char *, char **);
66 static int sm_ierror (const char *fmt
, ...) CHECK_PRINTF(1, 2);
67 static int sm_nerror (char *);
68 static int smtalk (int time
, char *fmt
, ...) CHECK_PRINTF(2, 3);
69 static int sm_wstream (char *, int);
70 static int smhear (void);
71 static char *EHLOset (char *) PURE
;
72 static int sm_sasl_callback(enum sasl_message_type
, unsigned const char *,
73 unsigned int, unsigned char **, unsigned int *,
77 sm_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
78 int debug
, int sasl
, const char *saslmech
, const char *user
,
79 const char *oauth_svc
, int tls
)
81 if (sm_mts
== MTS_SMTP
)
82 return smtp_init (client
, server
, port
, watch
, verbose
,
83 debug
, sasl
, saslmech
, user
, oauth_svc
, tls
);
85 return sendmail_init (client
, watch
, verbose
, debug
, sasl
,
90 smtp_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
91 int debug
, int sasl
, const char *saslmech
, const char *user
,
92 const char *oauth_svc
, int tls
)
95 char *errstr
, *chosen_server
;
100 sm_verbose
= verbose
;
106 if (client
== NULL
|| *client
== '\0') {
110 client
= LocalName(1); /* no clientname -> LocalName */
114 * Last-ditch check just in case client still isn't set to anything
116 if (client
== NULL
|| *client
== '\0')
117 client
= "localhost";
123 netsec_set_userid(nsc
, user
);
125 if ((sd1
= rclient (server
, port
, &chosen_server
)) == NOTOK
)
128 SIGNAL (SIGPIPE
, SIG_IGN
);
130 netsec_set_fd(nsc
, sd1
, sd1
);
132 netsec_set_hostname(nsc
, chosen_server
);
135 netsec_set_snoop(nsc
, 1);
138 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
139 NULL
, &errstr
) != OK
)
140 return sm_nerror(errstr
);
144 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
145 return sm_ierror("OAuth2 not supported");
148 if (tls
& S_TLSENABLEMASK
) {
149 if (netsec_set_tls(nsc
, 1, tls
& S_NOVERIFY
, &errstr
) != OK
)
150 return sm_nerror(errstr
);
154 * If tls == S_INITTLS, that means that the user requested
155 * "initial" TLS, which happens right after the connection has
156 * opened. Do that negotiation now
159 if (tls
& S_INITTLS
) {
160 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
162 return sm_nerror(errstr
);
166 netsec_set_timeout(nsc
, SM_OPEN
);
179 * Give EHLO or HELO command
183 result
= smtalk (SM_HELO
, "EHLO %s", client
);
186 if (result
>= 500 && result
<= 599)
187 result
= smtalk (SM_HELO
, "HELO %s", client
);
195 * If the user requested TLS support, then try to do the STARTTLS command
196 * as part of the initial dialog. Assuming this works, we then need to
197 * restart the EHLO dialog after TLS negotiation is complete.
200 if (tls
& S_STARTTLS
) {
201 if (! EHLOset("STARTTLS")) {
203 return sm_ierror("SMTP server does not support TLS");
206 result
= smtalk(SM_HELO
, "STARTTLS");
214 * Okay, the other side should be waiting for us to start TLS
215 * negotiation. Oblige them.
218 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
220 return sm_nerror(errstr
);
224 result
= smtalk (SM_HELO
, "EHLO %s", client
);
234 * If the user asked for SASL, then check to see if the SMTP server
235 * supports it. Otherwise, error out (because the SMTP server
236 * might have been spoofed; we don't want to just silently not
242 if (! (server_mechs
= EHLOset("AUTH"))) {
244 return sm_ierror("SMTP server does not support SASL");
247 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
249 return sm_nerror(errstr
);
254 if (watch
&& EHLOset ("XVRB"))
255 smtalk (SM_HELO
, "VERB on");
261 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
262 const char *saslmech
, const char *user
)
264 unsigned int i
, result
, vecp
;
266 char *vec
[15], *errstr
;
271 sm_verbose
= verbose
;
276 if (client
== NULL
|| *client
== '\0') {
280 client
= LocalName(1); /* no clientname -> LocalName */
283 * Last-ditch check just in case client still isn't set to anything
285 if (client
== NULL
|| *client
== '\0')
286 client
= "localhost";
292 netsec_set_userid(nsc
, user
);
294 netsec_set_hostname(nsc
, client
);
297 netsec_set_snoop(nsc
, 1);
300 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
301 NULL
, &errstr
) != OK
)
302 return sm_nerror(errstr
);
305 if (pipe (pdi
) == NOTOK
)
306 return sm_ierror ("no pipes");
307 if (pipe (pdo
) == NOTOK
) {
310 return sm_ierror ("no pipes");
320 return sm_ierror ("unable to fork");
323 if (pdo
[0] != fileno (stdin
))
324 dup2 (pdo
[0], fileno (stdin
));
325 if (pdi
[1] != fileno (stdout
))
326 dup2 (pdi
[1], fileno (stdout
));
327 if (pdi
[1] != fileno (stderr
))
328 dup2 (pdi
[1], fileno (stderr
));
329 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
333 vec
[vecp
++] = r1bindex (sendmail
, '/');
335 vec
[vecp
++] = watch
? "-odi" : "-odb";
336 vec
[vecp
++] = "-oem";
342 execvp (sendmail
, vec
);
343 fprintf (stderr
, "unable to exec ");
345 _exit(1); /* NOTREACHED */
348 SIGNAL (SIGPIPE
, SIG_IGN
);
353 netsec_set_fd(nsc
, pdi
[0], pdo
[1]);
354 netsec_set_timeout(nsc
, SM_OPEN
);
366 result
= smtalk (SM_HELO
, "EHLO %s", client
);
369 if (500 <= result
&& result
<= 599)
370 result
= smtalk (SM_HELO
, "HELO %s", client
);
382 * If the user asked for SASL, then check to see if the SMTP server
383 * supports it. Otherwise, error out (because the SMTP server
384 * might have been spoofed; we don't want to just silently not
390 if (! (server_mechs
= EHLOset("AUTH"))) {
392 return sm_ierror("SMTP server does not support SASL");
394 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
396 return sm_nerror(errstr
);
401 smtalk (SM_HELO
, "VERB on");
408 rclient (char *server
, char *service
, char **chosen_server
)
411 char response
[BUFSIZ
];
416 *chosen_server
= server
;
418 if ((sd
= client (server
, service
, response
, sizeof(response
),
422 sm_ierror ("%s", response
);
427 sm_winit (char *from
, int smtputf8
, int eightbit
)
429 const char *mail_parameters
= "";
432 /* Just for information, if an attempt is made to send to an 8-bit
433 address without specifying SMTPUTF8, Gmail responds with
434 555 5.5.2 Syntax error.
435 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
436 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
437 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
439 inform("SMTP server does not support %s, not sending.\n"
440 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
441 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
445 } else if (eightbit
) {
446 /* Comply with RFC 6152, for messages that have any 8-bit characters
448 if (EHLOset ("8BITMIME")) {
449 mail_parameters
= " BODY=8BITMIME";
451 inform("SMTP server does not support 8BITMIME, not sending.\n"
452 "Suggest encoding message for 7-bit transport by setting your\n"
453 "locale to C, and/or specifying *b64 in mhbuild directives.");
459 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
476 sm_wadr (char *mbox
, char *host
, char *path
)
478 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
480 FENDNULL(path
), mbox
, host
)) {
511 switch (smtalk (SM_DATA
, "DATA")) {
533 sm_wtxt (char *buffer
, int len
)
535 int result
, snoopstate
;
537 if ((snoopstate
= netsec_get_snoop(nsc
)))
538 netsec_set_snoop(nsc
, 0);
540 result
= sm_wstream (buffer
, len
);
542 netsec_set_snoop(nsc
, snoopstate
);
543 return result
== NOTOK
? RP_BHST
: RP_OK
;
552 if ((snoopstate
= netsec_get_snoop(nsc
)))
553 netsec_set_snoop(nsc
, 0);
555 if (sm_wstream(NULL
, 0) == NOTOK
)
559 * Because snoop output now happens at flush time, if we are using snoop
560 * we force an extra flush here. While this introduces some extra network
561 * traffic, we're only doing it when snoop is in effect so I think it's
567 if (netsec_flush(nsc
, &errstr
) != OK
) {
571 netsec_set_snoop(nsc
, snoopstate
);
574 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
597 if (sm_mts
== MTS_SENDMAIL_SMTP
) {
613 smtalk (SM_QUIT
, "QUIT");
617 sm_note
.code
= sm_reply
.code
;
618 sm_note
.length
= sm_reply
.length
;
619 memcpy (sm_note
.text
, sm_reply
.text
, sm_reply
.length
+ 1);
622 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
624 if (sm_mts
== MTS_SMTP
)
625 smtalk (SM_QUIT
, "QUIT");
627 /* The SIGPIPE block replaces old calls to discard ().
628 We're not sure what the discard () calls were for,
629 maybe to prevent deadlock on old systems. In any
630 case, blocking SIGPIPE should be harmless.
631 Because the file handles are closed below, leave it
635 sigaddset (&set
, SIGPIPE
);
636 sigprocmask (SIG_BLOCK
, &set
, &oset
);
638 kill (sm_child
, SIGKILL
);
642 sm_reply
.code
= sm_note
.code
;
643 sm_reply
.length
= sm_note
.length
;
644 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
650 netsec_shutdown(nsc
);
654 if (sm_mts
== MTS_SMTP
) {
656 } else if (sm_child
!= NOTOK
) {
657 status
= pidwait (sm_child
, OK
);
663 return status
? RP_BHST
: RP_OK
;
668 sm_ierror (const char *fmt
, ...)
673 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
676 sm_reply
.length
= strlen (sm_reply
.text
);
677 sm_reply
.code
= NOTOK
;
683 * Like sm_ierror, but assume it's an allocated error string we need to free.
687 sm_nerror (char *str
)
689 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
690 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
691 sm_reply
.length
= strlen(sm_reply
.text
);
692 sm_reply
.code
= NOTOK
;
699 smtalk (int time
, char *fmt
, ...)
706 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
710 return sm_nerror(errstr
);
712 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
713 return sm_nerror(errstr
);
715 if (netsec_flush (nsc
, &errstr
) != OK
)
716 return sm_nerror(errstr
);
718 netsec_set_timeout(nsc
, time
);
725 sm_wstream (char *buffer
, int len
)
728 static char lc
= '\0';
732 sm_ierror("No socket opened");
736 if (buffer
== NULL
&& len
== 0) {
739 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
746 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
750 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
758 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
767 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
788 char **ehlo
= EHLOkeys
, *buffer
;
791 static bool at_least_once
;
796 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
801 at_least_once
= true;
811 sm_reply
.text
[0] = 0;
813 rc
= sizeof(sm_reply
.text
) - 1;
815 for (more
= false; (buffer
= netsec_readline(nsc
, &buflen
,
816 &errstr
)) != NULL
; ) {
819 && has_prefix(buffer
, "250")
820 && (buffer
[3] == '-' || doingEHLO
== 2)
822 if (doingEHLO
== 2) {
823 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
824 strcpy (*ehlo
++, buffer
+ 4);
826 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
836 bp
= (unsigned char *) buffer
;
838 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
842 code
= atoi ((char *) bp
);
843 bp
+= 3, buflen
-= 3;
844 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
846 if (buflen
> 0 && *bp
== '-') {
849 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
854 if (code
!= sm_reply
.code
|| cont
)
858 sm_reply
.code
= code
;
861 bp
= (unsigned char *) sm_noreply
;
862 buflen
= strlen (sm_noreply
);
866 if ((i
= min (buflen
, rc
)) > 0) {
870 i
= strlen(sm_moreply
);
871 if (more
&& (int) rc
> i
+ 1) {
872 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
879 if (sm_reply
.code
< 100) {
887 sm_reply
.length
= rp
- sm_reply
.text
;
888 sm_reply
.text
[sm_reply
.length
] = 0;
889 return sm_reply
.code
;
901 /* The additional space is to avoid warning from gcc -Wformat-truncation. */
902 static char buffer
[BUFSIZ
+ 19];
904 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
924 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
944 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
945 text
, sm_reply
.code
, sm_reply
.text
);
957 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
959 if (has_prefix(ep
, s
)) {
960 for (ep
+= len
; *ep
== ' '; ep
++)
970 * Our SASL callback; we are either given SASL tokens to generate network
971 * protocols messages for, or we decode incoming protocol messages and
972 * convert them to binary SASL tokens to pass up into the SASL library.
976 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
977 unsigned int indatalen
, unsigned char **outdata
,
978 unsigned int *outdatalen
, void *context
, char **errstr
)
986 case NETSEC_SASL_START
:
988 * Generate an AUTH message; if we were given an input token
989 * then generate a an AUTH message that includes the initial
993 mech
= netsec_get_sasl_mechanism(nsc
);
998 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
999 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
1001 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
1003 snoopoffset
= 6 + strlen(mech
);
1004 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
1007 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1011 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1015 rc
= netsec_flush(nsc
, errstr
);
1016 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1023 case NETSEC_SASL_READ
:
1025 * Read in a line that should contain a 334 response code, followed
1026 * by base64 response data.
1029 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1031 line
= netsec_readline(nsc
, &len
, errstr
);
1032 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1038 netsec_err(errstr
, "Invalid format for SASL response");
1042 if (!has_prefix(line
, "334 ")) {
1043 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1051 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1053 netsec_err(errstr
, "Unable to decode base64 response");
1060 case NETSEC_SASL_WRITE
:
1062 * The output encoding is pretty simple, so this is easy.
1064 if (indatalen
== 0) {
1065 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1067 unsigned char *b64data
;
1068 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1069 writeBase64raw(indata
, indatalen
, b64data
);
1070 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1071 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1078 rc
= netsec_flush(nsc
, errstr
);
1079 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1085 case NETSEC_SASL_FINISH
:
1087 * Finish the protocol; we're looking for a 235 message.
1089 line
= netsec_readline(nsc
, &len
, errstr
);
1093 if (!has_prefix(line
, "235 ")) {
1095 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1097 netsec_err(errstr
, "Authentication failed: %s", line
);
1102 case NETSEC_SASL_CANCEL
:
1104 * Cancel the SASL exchange; this is done by sending a single "*".
1106 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1108 rc
= netsec_flush(nsc
, errstr
);