]>
diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
7b706e010e8e7926d969a69af5c31abda26f9544
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 */
301 * Last-ditch check just in case client still isn't set to anything
304 if (client
== NULL
|| *client
== '\0')
305 client
= "localhost";
310 netsec_set_userid(nsc
, user
);
313 netsec_set_snoop(nsc
, 1);
316 if (netsec_set_sasl_params(nsc
, client
, "smtp", saslmech
,
317 sm_sasl_callback
, &errstr
) != OK
)
318 return sm_nerror(errstr
);
321 if (pipe (pdi
) == NOTOK
)
322 return sm_ierror ("no pipes");
323 if (pipe (pdo
) == NOTOK
) {
326 return sm_ierror ("no pipes");
329 for (i
= 0; (sm_child
= fork ()) == NOTOK
&& i
< 5; i
++)
338 return sm_ierror ("unable to fork");
341 if (pdo
[0] != fileno (stdin
))
342 dup2 (pdo
[0], fileno (stdin
));
343 if (pdi
[1] != fileno (stdout
))
344 dup2 (pdi
[1], fileno (stdout
));
345 if (pdi
[1] != fileno (stderr
))
346 dup2 (pdi
[1], fileno (stderr
));
347 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
351 vec
[vecp
++] = r1bindex (sendmail
, '/');
353 vec
[vecp
++] = watch
? "-odi" : "-odb";
354 vec
[vecp
++] = "-oem";
360 execvp (sendmail
, vec
);
361 fprintf (stderr
, "unable to exec ");
363 _exit (-1); /* NOTREACHED */
366 SIGNAL (SIGPIPE
, SIG_IGN
);
371 netsec_set_fd(nsc
, pdi
[i
], pdo
[1]);
372 netsec_set_timeout(nsc
, SM_OPEN
);
384 result
= smtalk (SM_HELO
, "EHLO %s", client
);
387 if (500 <= result
&& result
<= 599)
388 result
= smtalk (SM_HELO
, "HELO %s", client
);
400 * If the user asked for SASL, then check to see if the SMTP server
401 * supports it. Otherwise, error out (because the SMTP server
402 * might have been spoofed; we don't want to just silently not
408 if (! (server_mechs
= EHLOset("AUTH"))) {
410 return sm_ierror("SMTP server does not support SASL");
412 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
414 return sm_nerror(errstr
);
419 smtalk (SM_HELO
, "VERB on");
426 rclient (char *server
, char *service
)
429 char response
[BUFSIZ
];
431 if ((sd
= client (server
, service
, response
, sizeof(response
),
435 sm_ierror ("%s", response
);
440 sm_winit (char *from
, int smtputf8
, int eightbit
)
442 const char *mail_parameters
= "";
445 /* Just for information, if an attempt is made to send to an 8-bit
446 address without specifying SMTPUTF8, Gmail responds with
447 555 5.5.2 Syntax error.
448 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
449 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
450 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
452 advise (NULL
, "SMTP server does not support %s, not sending.\n"
453 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
454 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
458 } else if (eightbit
) {
459 /* Comply with RFC 6152, for messages that have any 8-bit characters
461 if (EHLOset ("8BITMIME")) {
462 mail_parameters
= " BODY=8BITMIME";
464 advise (NULL
, "SMTP server does not support 8BITMIME, not sending.\n"
465 "Suggest encoding message for 7-bit transport by setting your\n"
466 "locale to C, and/or specifying *b64 in mhbuild directives.");
472 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
489 sm_wadr (char *mbox
, char *host
, char *path
)
491 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
493 path
? path
: "", mbox
, host
)) {
503 #endif /* SENDMAILBUG */
528 switch (smtalk (SM_DATA
, "DATA")) {
537 #endif /* SENDMAILBUG */
554 sm_wtxt (char *buffer
, int len
)
558 result
= sm_wstream (buffer
, len
);
560 return (result
== NOTOK
? RP_BHST
: RP_OK
);
567 if (sm_wstream ((char *) NULL
, 0) == NOTOK
)
570 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
578 #endif /* SENDMAILBUG */
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);/* fall */
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
, 1);
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
) {
764 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
779 int i
, code
, cont
, more
;
784 char **ehlo
= EHLOkeys
, *buffer
;
787 static int at_least_once
= 0;
792 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
807 sm_reply
.text
[0] = 0;
809 rc
= sizeof(sm_reply
.text
) - 1;
811 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
812 &errstr
)) != NULL
; ) {
815 && strncmp (buffer
, "250", LEN("250")) == 0
816 && (buffer
[3] == '-' || doingEHLO
== 2)
818 if (doingEHLO
== 2) {
819 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
820 strcpy (*ehlo
++, buffer
+ 4);
822 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
832 bp
= (unsigned char *) buffer
;
834 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
838 code
= atoi ((char *) bp
);
839 bp
+= 3, buflen
-= 3;
840 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
842 if (buflen
> 0 && *bp
== '-') {
845 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
850 if (code
!= sm_reply
.code
|| cont
)
854 sm_reply
.code
= code
;
857 bp
= (unsigned char *) sm_noreply
;
858 buflen
= strlen (sm_noreply
);
862 if ((i
= min (buflen
, rc
)) > 0) {
866 i
= strlen(sm_moreply
);
867 if (more
&& (int) rc
> i
+ 1) {
868 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
875 if (sm_reply
.code
< 100) {
877 printf ("%s\n", sm_reply
.text
);
883 sm_reply
.length
= rp
- sm_reply
.text
;
884 sm_reply
.text
[sm_reply
.length
] = 0;
885 return sm_reply
.code
;
897 static char buffer
[BUFSIZ
];
899 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
919 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
939 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
940 text
, sm_reply
.code
, sm_reply
.text
);
952 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
954 if (strncmp (ep
, s
, len
) == 0) {
955 for (ep
+= len
; *ep
== ' '; ep
++)
965 * Our SASL callback; we are either given SASL tokens to generate network
966 * protocols messages for, or we decode incoming protocol messages and
967 * convert them to binary SASL tokens to pass up into the SASL library.
971 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
972 unsigned int indatalen
, unsigned char **outdata
,
973 unsigned int *outdatalen
, char **errstr
)
980 case NETSEC_SASL_START
:
982 * Generate an AUTH message; if we were given an input token
983 * then generate a an AUTH message that includes the initial
987 mech
= netsec_get_sasl_mechanism(nsc
);
992 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
993 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
995 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
997 snoopoffset
= 6 + strlen(mech
);
998 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
1000 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1002 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1008 if (netsec_flush(nsc
, errstr
) != OK
)
1013 case NETSEC_SASL_READ
:
1015 * Read in a line that should contain a 334 response code, followed
1016 * by base64 response data.
1019 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1021 line
= netsec_readline(nsc
, &len
, errstr
);
1022 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1028 netsec_err(errstr
, "Invalid format for SASL response");
1032 if (strncmp(line
, "334 ", 4) != 0) {
1033 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1041 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1043 netsec_err(errstr
, "Unable to decode base64 response");
1050 case NETSEC_SASL_WRITE
:
1052 * The output encoding is pretty simple, so this is easy.
1054 if (indatalen
== 0) {
1055 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1057 unsigned char *b64data
;
1058 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1059 writeBase64raw(indata
, indatalen
, b64data
);
1060 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1061 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1062 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1069 if (netsec_flush(nsc
, errstr
) != OK
)
1073 case NETSEC_SASL_FINISH
:
1075 * Finish the protocol; we're looking for a 235 message.
1077 line
= netsec_readline(nsc
, &len
, errstr
);
1081 if (strncmp(line
, "235 ", 4) != 0) {
1083 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1085 netsec_err(errstr
, "Authentication failed: %s", line
);
1090 case NETSEC_SASL_CANCEL
:
1092 * Cancel the SASL exchange; this is done by sending a single "*".
1094 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1096 rc
= netsec_flush(nsc
, errstr
);