]>
diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
97f2f5b63658fa1f5b55cff5fd3080af782ea28d
2 * smtp.c -- nmh SMTP interface
4 * This code is Copyright (c) 2002, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
12 #include <h/signals.h>
16 #include <sys/socket.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
26 * On older 4.2BSD machines without the POSIX function `sigaction',
27 * the alarm handing stuff for time-outs will NOT work due to the way
28 * syscalls get restarted. This is not really crucial, since SendMail
29 * is generally well-behaved in this area.
34 * It appears that some versions of Sendmail will return Code 451
35 * when they don't really want to indicate a failure.
36 * "Code 451 almost always means sendmail has deferred; we don't
37 * really want bomb out at this point since sendmail will rectify
38 * things later." So, if you define SENDMAILBUG, Code 451 is
39 * considered the same as Code 250. Yuck!
43 #define NBITS ((sizeof (int)) * 8)
45 /* Timeout in seconds for SMTP commands.
46 * Lore has it they must be distinct. */
47 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
50 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
51 #define SM_RCPT 302 /* see above */
52 #define SM_DATA 120 /* see above */
53 #define SM_DOT 600 /* see above */
56 static int sm_addrs
= 0;
57 static int sm_child
= NOTOK
;
58 static int sm_debug
= 0;
59 static int sm_nl
= TRUE
;
60 static int sm_verbose
= 0;
61 static netsec_context
*nsc
= NULL
;
63 static char *sm_noreply
= "No reply text given";
64 static char *sm_moreply
= "; ";
65 static struct smtp sm_reply
;
70 static char *EHLOkeys
[MAXEHLO
+ 1];
75 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
76 const char *, const char *, int);
77 static int sendmail_init (char *, int, int, int, int, const char *,
80 static int rclient (char *, char *, char **);
81 static int sm_ierror (const char *fmt
, ...);
82 static int sm_nerror (char *);
83 static int smtalk (int time
, char *fmt
, ...);
84 static int sm_wstream (char *, int);
85 static int smhear (void);
86 static char *EHLOset (char *);
87 static int sm_sasl_callback(enum sasl_message_type
, unsigned const char *,
88 unsigned int, unsigned char **, unsigned int *,
92 sm_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
93 int debug
, int sasl
, const char *saslmech
, const char *user
,
94 const char *oauth_svc
, int tls
)
96 if (sm_mts
== MTS_SMTP
)
97 return smtp_init (client
, server
, port
, watch
, verbose
,
98 debug
, sasl
, saslmech
, user
, oauth_svc
, tls
);
100 return sendmail_init (client
, watch
, verbose
, debug
, sasl
,
105 smtp_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
106 int debug
, int sasl
, const char *saslmech
, const char *user
,
107 const char *oauth_svc
, int tls
)
110 char *errstr
, *chosen_server
;
115 sm_verbose
= verbose
;
121 if (client
== NULL
|| *client
== '\0') {
125 client
= LocalName(1); /* no clientname -> LocalName */
129 * Last-ditch check just in case client still isn't set to anything
131 if (client
== NULL
|| *client
== '\0')
132 client
= "localhost";
138 netsec_set_userid(nsc
, user
);
140 if ((sd1
= rclient (server
, port
, &chosen_server
)) == NOTOK
)
143 SIGNAL (SIGPIPE
, SIG_IGN
);
145 netsec_set_fd(nsc
, sd1
, sd1
);
147 netsec_set_hostname(nsc
, chosen_server
);
150 netsec_set_snoop(nsc
, 1);
153 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
155 return sm_nerror(errstr
);
159 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
160 return sm_ierror("OAuth2 not supported");
163 if (tls
& S_TLSENABLEMASK
) {
164 if (netsec_set_tls(nsc
, 1, tls
& S_NOVERIFY
, &errstr
) != OK
)
165 return sm_nerror(errstr
);
169 * If tls == S_INITTLS, that means that the user requested
170 * "initial" TLS, which happens right after the connection has
171 * opened. Do that negotiation now
174 if (tls
& S_INITTLS
) {
175 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
177 return sm_nerror(errstr
);
181 netsec_set_timeout(nsc
, SM_OPEN
);
194 * Give EHLO or HELO command
198 result
= smtalk (SM_HELO
, "EHLO %s", client
);
201 if (result
>= 500 && result
<= 599)
202 result
= smtalk (SM_HELO
, "HELO %s", client
);
210 * If the user requested TLS support, then try to do the STARTTLS command
211 * as part of the initial dialog. Assuming this works, we then need to
212 * restart the EHLO dialog after TLS negotiation is complete.
215 if (tls
& S_STARTTLS
) {
216 if (! EHLOset("STARTTLS")) {
218 return sm_ierror("SMTP server does not support TLS");
221 result
= smtalk(SM_HELO
, "STARTTLS");
229 * Okay, the other side should be waiting for us to start TLS
230 * negotiation. Oblige them.
233 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
235 return sm_nerror(errstr
);
239 result
= smtalk (SM_HELO
, "EHLO %s", client
);
249 * If the user asked for SASL, then check to see if the SMTP server
250 * supports it. Otherwise, error out (because the SMTP server
251 * might have been spoofed; we don't want to just silently not
257 if (! (server_mechs
= EHLOset("AUTH"))) {
259 return sm_ierror("SMTP server does not support SASL");
262 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
264 return sm_nerror(errstr
);
269 if (watch
&& EHLOset ("XVRB"))
270 smtalk (SM_HELO
, "VERB on");
276 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
277 const char *saslmech
, const char *user
)
279 unsigned int i
, result
, vecp
;
281 char *vec
[15], *errstr
;
286 sm_verbose
= verbose
;
291 if (client
== NULL
|| *client
== '\0') {
295 client
= LocalName(1); /* no clientname -> LocalName */
298 * Last-ditch check just in case client still isn't set to anything
300 if (client
== NULL
|| *client
== '\0')
301 client
= "localhost";
307 netsec_set_userid(nsc
, user
);
309 netsec_set_hostname(nsc
, client
);
312 netsec_set_snoop(nsc
, 1);
315 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
317 return sm_nerror(errstr
);
320 if (pipe (pdi
) == NOTOK
)
321 return sm_ierror ("no pipes");
322 if (pipe (pdo
) == NOTOK
) {
325 return sm_ierror ("no pipes");
328 for (i
= 0; (sm_child
= fork ()) == NOTOK
&& i
< 5; i
++)
337 return sm_ierror ("unable to fork");
340 if (pdo
[0] != fileno (stdin
))
341 dup2 (pdo
[0], fileno (stdin
));
342 if (pdi
[1] != fileno (stdout
))
343 dup2 (pdi
[1], fileno (stdout
));
344 if (pdi
[1] != fileno (stderr
))
345 dup2 (pdi
[1], fileno (stderr
));
346 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
350 vec
[vecp
++] = r1bindex (sendmail
, '/');
352 vec
[vecp
++] = watch
? "-odi" : "-odb";
353 vec
[vecp
++] = "-oem";
359 execvp (sendmail
, vec
);
360 fprintf (stderr
, "unable to exec ");
362 _exit (-1); /* NOTREACHED */
365 SIGNAL (SIGPIPE
, SIG_IGN
);
370 netsec_set_fd(nsc
, pdi
[i
], pdo
[1]);
371 netsec_set_timeout(nsc
, SM_OPEN
);
383 result
= smtalk (SM_HELO
, "EHLO %s", client
);
386 if (500 <= result
&& result
<= 599)
387 result
= smtalk (SM_HELO
, "HELO %s", client
);
399 * If the user asked for SASL, then check to see if the SMTP server
400 * supports it. Otherwise, error out (because the SMTP server
401 * might have been spoofed; we don't want to just silently not
407 if (! (server_mechs
= EHLOset("AUTH"))) {
409 return sm_ierror("SMTP server does not support SASL");
411 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
413 return sm_nerror(errstr
);
418 smtalk (SM_HELO
, "VERB on");
425 rclient (char *server
, char *service
, char **chosen_server
)
428 char response
[BUFSIZ
];
433 *chosen_server
= server
;
435 if ((sd
= client (server
, service
, response
, sizeof(response
),
439 sm_ierror ("%s", response
);
444 sm_winit (char *from
, int smtputf8
, int eightbit
)
446 const char *mail_parameters
= "";
449 /* Just for information, if an attempt is made to send to an 8-bit
450 address without specifying SMTPUTF8, Gmail responds with
451 555 5.5.2 Syntax error.
452 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
453 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
454 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
456 inform("SMTP server does not support %s, not sending.\n"
457 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
458 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
462 } else if (eightbit
) {
463 /* Comply with RFC 6152, for messages that have any 8-bit characters
465 if (EHLOset ("8BITMIME")) {
466 mail_parameters
= " BODY=8BITMIME";
468 inform("SMTP server does not support 8BITMIME, not sending.\n"
469 "Suggest encoding message for 7-bit transport by setting your\n"
470 "locale to C, and/or specifying *b64 in mhbuild directives.");
476 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
493 sm_wadr (char *mbox
, char *host
, char *path
)
495 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
497 path
? path
: "", mbox
, host
)) {
507 #endif /* SENDMAILBUG */
532 switch (smtalk (SM_DATA
, "DATA")) {
541 #endif /* SENDMAILBUG */
558 sm_wtxt (char *buffer
, int len
)
562 result
= sm_wstream (buffer
, len
);
564 return (result
== NOTOK
? RP_BHST
: RP_OK
);
571 if (sm_wstream ((char *) NULL
, 0) == NOTOK
)
574 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
582 #endif /* SENDMAILBUG */
600 if (sm_mts
== MTS_SENDMAIL_SMTP
) {
616 smtalk (SM_QUIT
, "QUIT");
620 sm_note
.code
= sm_reply
.code
;
621 sm_note
.length
= sm_reply
.length
;
622 memcpy (sm_note
.text
, sm_reply
.text
, sm_reply
.length
+ 1);
625 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
627 if (sm_mts
== MTS_SMTP
)
628 smtalk (SM_QUIT
, "QUIT");
630 /* The SIGPIPE block replaces old calls to discard ().
631 We're not sure what the discard () calls were for,
632 maybe to prevent deadlock on old systems. In any
633 case, blocking SIGPIPE should be harmless.
634 Because the file handles are closed below, leave it
638 sigaddset (&set
, SIGPIPE
);
639 sigprocmask (SIG_BLOCK
, &set
, &oset
);
641 kill (sm_child
, SIGKILL
);
645 sm_reply
.code
= sm_note
.code
;
646 sm_reply
.length
= sm_note
.length
;
647 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
653 netsec_shutdown(nsc
, 1);
657 if (sm_mts
== MTS_SMTP
) {
659 } else if (sm_child
!= NOTOK
) {
660 status
= pidwait (sm_child
, OK
);
666 return (status
? RP_BHST
: RP_OK
);
671 sm_ierror (const char *fmt
, ...)
676 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
679 sm_reply
.length
= strlen (sm_reply
.text
);
680 sm_reply
.code
= NOTOK
;
686 * Like sm_ierror, but assume it's an allocated error string we need to free.
690 sm_nerror (char *str
)
692 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
693 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
694 sm_reply
.length
= strlen(sm_reply
.text
);
695 sm_reply
.code
= NOTOK
;
702 smtalk (int time
, char *fmt
, ...)
709 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
713 return sm_nerror(errstr
);
715 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
716 return sm_nerror(errstr
);
718 if (netsec_flush (nsc
, &errstr
) != OK
)
719 return sm_nerror(errstr
);
721 netsec_set_timeout(nsc
, time
);
728 sm_wstream (char *buffer
, int len
)
731 static char lc
= '\0';
735 sm_ierror("No socket opened");
739 if (buffer
== NULL
&& len
== 0) {
742 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
749 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
753 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
761 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
770 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
785 int i
, code
, cont
, more
;
790 char **ehlo
= EHLOkeys
, *buffer
;
793 static int at_least_once
= 0;
798 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
813 sm_reply
.text
[0] = 0;
815 rc
= sizeof(sm_reply
.text
) - 1;
817 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
818 &errstr
)) != NULL
; ) {
821 && has_prefix(buffer
, "250")
822 && (buffer
[3] == '-' || doingEHLO
== 2)
824 if (doingEHLO
== 2) {
825 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
826 strcpy (*ehlo
++, buffer
+ 4);
828 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
838 bp
= (unsigned char *) buffer
;
840 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
844 code
= atoi ((char *) bp
);
845 bp
+= 3, buflen
-= 3;
846 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
848 if (buflen
> 0 && *bp
== '-') {
851 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
856 if (code
!= sm_reply
.code
|| cont
)
860 sm_reply
.code
= code
;
863 bp
= (unsigned char *) sm_noreply
;
864 buflen
= strlen (sm_noreply
);
868 if ((i
= min (buflen
, rc
)) > 0) {
872 i
= strlen(sm_moreply
);
873 if (more
&& (int) rc
> i
+ 1) {
874 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
881 if (sm_reply
.code
< 100) {
889 sm_reply
.length
= rp
- sm_reply
.text
;
890 sm_reply
.text
[sm_reply
.length
] = 0;
891 return sm_reply
.code
;
903 static char buffer
[BUFSIZ
];
905 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
925 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
945 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
946 text
, sm_reply
.code
, sm_reply
.text
);
958 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
960 if (has_prefix(ep
, s
)) {
961 for (ep
+= len
; *ep
== ' '; ep
++)
971 * Our SASL callback; we are either given SASL tokens to generate network
972 * protocols messages for, or we decode incoming protocol messages and
973 * convert them to binary SASL tokens to pass up into the SASL library.
977 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
978 unsigned int indatalen
, unsigned char **outdata
,
979 unsigned int *outdatalen
, 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
);
1006 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1008 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1014 if (netsec_flush(nsc
, errstr
) != OK
)
1019 case NETSEC_SASL_READ
:
1021 * Read in a line that should contain a 334 response code, followed
1022 * by base64 response data.
1025 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1027 line
= netsec_readline(nsc
, &len
, errstr
);
1028 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1034 netsec_err(errstr
, "Invalid format for SASL response");
1038 if (!has_prefix(line
, "334 ")) {
1039 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1047 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1049 netsec_err(errstr
, "Unable to decode base64 response");
1056 case NETSEC_SASL_WRITE
:
1058 * The output encoding is pretty simple, so this is easy.
1060 if (indatalen
== 0) {
1061 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1063 unsigned char *b64data
;
1064 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1065 writeBase64raw(indata
, indatalen
, b64data
);
1066 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1067 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1068 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1075 if (netsec_flush(nsc
, errstr
) != OK
)
1079 case NETSEC_SASL_FINISH
:
1081 * Finish the protocol; we're looking for a 235 message.
1083 line
= netsec_readline(nsc
, &len
, errstr
);
1087 if (!has_prefix(line
, "235 ")) {
1089 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1091 netsec_err(errstr
, "Authentication failed: %s", line
);
1096 case NETSEC_SASL_CANCEL
:
1098 * Cancel the SASL exchange; this is done by sending a single "*".
1100 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1102 rc
= netsec_flush(nsc
, errstr
);