]>
diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
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)
46 * these codes must all be different!
48 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
51 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
52 #define SM_RCPT 302 /* see above */
53 #define SM_DATA 120 /* see above */
54 #define SM_TEXT 180 /* see above */
55 #define SM_DOT 600 /* see above */
60 static int sm_addrs
= 0;
61 static int sm_child
= NOTOK
;
62 static int sm_debug
= 0;
63 static int sm_nl
= TRUE
;
64 static int sm_verbose
= 0;
65 static netsec_context
*nsc
= NULL
;
67 static char *sm_noreply
= "No reply text given";
68 static char *sm_moreply
= "; ";
69 static struct smtp sm_reply
;
74 static char *EHLOkeys
[MAXEHLO
+ 1];
79 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
80 const char *, const char *, int);
81 static int sendmail_init (char *, int, int, int, int, const char *,
84 static int rclient (char *, char *);
85 static int sm_ierror (const char *fmt
, ...);
86 static int sm_nerror (char *);
87 static int smtalk (int time
, char *fmt
, ...);
88 static int sm_wstream (char *, int);
89 static int smhear (void);
90 static char *EHLOset (char *);
91 static int sm_sasl_callback(enum sasl_message_type
, unsigned const char *,
92 unsigned int, unsigned char **, unsigned int *,
96 sm_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
97 int debug
, int sasl
, const char *saslmech
, const char *user
,
98 const char *oauth_svc
, int tls
)
100 if (sm_mts
== MTS_SMTP
)
101 return smtp_init (client
, server
, port
, watch
, verbose
,
102 debug
, sasl
, saslmech
, user
, oauth_svc
, tls
);
104 return sendmail_init (client
, watch
, verbose
, debug
, sasl
,
109 smtp_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
110 int debug
, int sasl
, const char *saslmech
, const char *user
,
111 const char *oauth_svc
, int tls
)
119 sm_verbose
= verbose
;
125 if (client
== NULL
|| *client
== '\0') {
129 client
= LocalName(1); /* no clientname -> LocalName */
133 * Last-ditch check just in case client still isn't set to anything
135 if (client
== NULL
|| *client
== '\0')
136 client
= "localhost";
142 netsec_set_userid(nsc
, user
);
145 netsec_set_snoop(nsc
, 1);
148 if (netsec_set_sasl_params(nsc
, server
, "smtp", saslmech
,
149 sm_sasl_callback
, &errstr
) != OK
)
150 return sm_nerror(errstr
);
154 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
155 return sm_ierror("OAuth2 not supported");
158 if ((sd1
= rclient (server
, port
)) == NOTOK
)
161 SIGNAL (SIGPIPE
, SIG_IGN
);
163 netsec_set_fd(nsc
, sd1
, sd1
);
166 if (netsec_set_tls(nsc
, 1, &errstr
) != OK
)
167 return sm_nerror(errstr
);
171 * If tls == 2, that means that the user requested "initial" TLS,
172 * which happens right after the connection has opened. Do that
177 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
179 return sm_nerror(errstr
);
183 netsec_set_timeout(nsc
, SM_OPEN
);
196 * Give EHLO or HELO command
200 result
= smtalk (SM_HELO
, "EHLO %s", client
);
203 if (result
>= 500 && result
<= 599)
204 result
= smtalk (SM_HELO
, "HELO %s", client
);
212 * If the user requested TLS support, then try to do the STARTTLS command
213 * as part of the initial dialog. Assuming this works, we then need to
214 * restart the EHLO dialog after TLS negotiation is complete.
218 if (! EHLOset("STARTTLS")) {
220 return sm_ierror("SMTP server does not support TLS");
223 result
= smtalk(SM_HELO
, "STARTTLS");
231 * Okay, the other side should be waiting for us to start TLS
232 * negotiation. Oblige them.
235 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
237 return sm_nerror(errstr
);
241 result
= smtalk (SM_HELO
, "EHLO %s", client
);
251 * If the user asked for SASL, then check to see if the SMTP server
252 * supports it. Otherwise, error out (because the SMTP server
253 * might have been spoofed; we don't want to just silently not
259 if (! (server_mechs
= EHLOset("AUTH"))) {
261 return sm_ierror("SMTP server does not support SASL");
264 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
266 return sm_nerror(errstr
);
271 if (watch
&& EHLOset ("XVRB"))
272 smtalk (SM_HELO
, "VERB on");
278 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
279 const char *saslmech
, const char *user
)
281 unsigned int i
, result
, vecp
;
283 char *vec
[15], *errstr
;
288 sm_verbose
= verbose
;
293 if (client
== NULL
|| *client
== '\0') {
297 client
= LocalName(1); /* no clientname -> LocalName */
300 * Last-ditch check just in case client still isn't set to anything
302 if (client
== NULL
|| *client
== '\0')
303 client
= "localhost";
309 netsec_set_userid(nsc
, user
);
312 netsec_set_snoop(nsc
, 1);
315 if (netsec_set_sasl_params(nsc
, client
, "smtp", saslmech
,
316 sm_sasl_callback
, &errstr
) != OK
)
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
)
428 char response
[BUFSIZ
];
430 if ((sd
= client (server
, service
, response
, sizeof(response
),
434 sm_ierror ("%s", response
);
439 sm_winit (char *from
, int smtputf8
, int eightbit
)
441 const char *mail_parameters
= "";
444 /* Just for information, if an attempt is made to send to an 8-bit
445 address without specifying SMTPUTF8, Gmail responds with
446 555 5.5.2 Syntax error.
447 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
448 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
449 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
451 advise (NULL
, "SMTP server does not support %s, not sending.\n"
452 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
453 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
457 } else if (eightbit
) {
458 /* Comply with RFC 6152, for messages that have any 8-bit characters
460 if (EHLOset ("8BITMIME")) {
461 mail_parameters
= " BODY=8BITMIME";
463 advise (NULL
, "SMTP server does not support 8BITMIME, not sending.\n"
464 "Suggest encoding message for 7-bit transport by setting your\n"
465 "locale to C, and/or specifying *b64 in mhbuild directives.");
471 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
488 sm_wadr (char *mbox
, char *host
, char *path
)
490 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
492 path
? path
: "", mbox
, host
)) {
502 #endif /* SENDMAILBUG */
527 switch (smtalk (SM_DATA
, "DATA")) {
536 #endif /* SENDMAILBUG */
553 sm_wtxt (char *buffer
, int len
)
557 result
= sm_wstream (buffer
, len
);
559 return (result
== NOTOK
? RP_BHST
: RP_OK
);
566 if (sm_wstream ((char *) NULL
, 0) == NOTOK
)
569 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
577 #endif /* SENDMAILBUG */
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);/* fall */
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
, 1);
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
) {
763 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
778 int i
, code
, cont
, more
;
783 char **ehlo
= EHLOkeys
, *buffer
;
786 static int at_least_once
= 0;
791 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
806 sm_reply
.text
[0] = 0;
808 rc
= sizeof(sm_reply
.text
) - 1;
810 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
811 &errstr
)) != NULL
; ) {
814 && HasPrefix(buffer
, "250")
815 && (buffer
[3] == '-' || doingEHLO
== 2)
817 if (doingEHLO
== 2) {
818 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
819 strcpy (*ehlo
++, buffer
+ 4);
821 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
831 bp
= (unsigned char *) buffer
;
833 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
837 code
= atoi ((char *) bp
);
838 bp
+= 3, buflen
-= 3;
839 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
841 if (buflen
> 0 && *bp
== '-') {
844 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
849 if (code
!= sm_reply
.code
|| cont
)
853 sm_reply
.code
= code
;
856 bp
= (unsigned char *) sm_noreply
;
857 buflen
= strlen (sm_noreply
);
861 if ((i
= min (buflen
, rc
)) > 0) {
865 i
= strlen(sm_moreply
);
866 if (more
&& (int) rc
> i
+ 1) {
867 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
874 if (sm_reply
.code
< 100) {
876 printf ("%s\n", sm_reply
.text
);
882 sm_reply
.length
= rp
- sm_reply
.text
;
883 sm_reply
.text
[sm_reply
.length
] = 0;
884 return sm_reply
.code
;
896 static char buffer
[BUFSIZ
];
898 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
918 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
938 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
939 text
, sm_reply
.code
, sm_reply
.text
);
951 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
953 if (HasPrefix(ep
, s
)) {
954 for (ep
+= len
; *ep
== ' '; ep
++)
964 * Our SASL callback; we are either given SASL tokens to generate network
965 * protocols messages for, or we decode incoming protocol messages and
966 * convert them to binary SASL tokens to pass up into the SASL library.
970 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
971 unsigned int indatalen
, unsigned char **outdata
,
972 unsigned int *outdatalen
, char **errstr
)
979 case NETSEC_SASL_START
:
981 * Generate an AUTH message; if we were given an input token
982 * then generate a an AUTH message that includes the initial
986 mech
= netsec_get_sasl_mechanism(nsc
);
991 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
992 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
994 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
996 snoopoffset
= 6 + strlen(mech
);
997 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
999 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1001 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1007 if (netsec_flush(nsc
, errstr
) != OK
)
1012 case NETSEC_SASL_READ
:
1014 * Read in a line that should contain a 334 response code, followed
1015 * by base64 response data.
1018 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1020 line
= netsec_readline(nsc
, &len
, errstr
);
1021 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1027 netsec_err(errstr
, "Invalid format for SASL response");
1031 if (!HasPrefix(line
, "334 ")) {
1032 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1040 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1042 netsec_err(errstr
, "Unable to decode base64 response");
1049 case NETSEC_SASL_WRITE
:
1051 * The output encoding is pretty simple, so this is easy.
1053 if (indatalen
== 0) {
1054 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1056 unsigned char *b64data
;
1057 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1058 writeBase64raw(indata
, indatalen
, b64data
);
1059 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1060 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1061 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1068 if (netsec_flush(nsc
, errstr
) != OK
)
1072 case NETSEC_SASL_FINISH
:
1074 * Finish the protocol; we're looking for a 235 message.
1076 line
= netsec_readline(nsc
, &len
, errstr
);
1080 if (!HasPrefix(line
, "235 ")) {
1082 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1084 netsec_err(errstr
, "Authentication failed: %s", line
);
1089 case NETSEC_SASL_CANCEL
:
1091 * Cancel the SASL exchange; this is done by sending a single "*".
1093 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1095 rc
= netsec_flush(nsc
, errstr
);