]>
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 *, 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
)
114 char *errstr
, *chosen_server
;
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
);
144 if ((sd1
= rclient (server
, port
, &chosen_server
)) == NOTOK
)
147 SIGNAL (SIGPIPE
, SIG_IGN
);
149 netsec_set_fd(nsc
, sd1
, sd1
);
151 netsec_set_hostname(nsc
, chosen_server
);
154 netsec_set_snoop(nsc
, 1);
157 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
159 return sm_nerror(errstr
);
163 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
164 return sm_ierror("OAuth2 not supported");
167 if (tls
& S_TLSENABLEMASK
) {
168 if (netsec_set_tls(nsc
, 1, tls
& S_NOVERIFY
, &errstr
) != OK
)
169 return sm_nerror(errstr
);
173 * If tls == S_INITTLS, that means that the user requested
174 * "initial" TLS, which happens right after the connection has
175 * opened. Do that negotiation now
178 if (tls
& S_INITTLS
) {
179 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
181 return sm_nerror(errstr
);
185 netsec_set_timeout(nsc
, SM_OPEN
);
198 * Give EHLO or HELO command
202 result
= smtalk (SM_HELO
, "EHLO %s", client
);
205 if (result
>= 500 && result
<= 599)
206 result
= smtalk (SM_HELO
, "HELO %s", client
);
214 * If the user requested TLS support, then try to do the STARTTLS command
215 * as part of the initial dialog. Assuming this works, we then need to
216 * restart the EHLO dialog after TLS negotiation is complete.
219 if (tls
& S_STARTTLS
) {
220 if (! EHLOset("STARTTLS")) {
222 return sm_ierror("SMTP server does not support TLS");
225 result
= smtalk(SM_HELO
, "STARTTLS");
233 * Okay, the other side should be waiting for us to start TLS
234 * negotiation. Oblige them.
237 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
239 return sm_nerror(errstr
);
243 result
= smtalk (SM_HELO
, "EHLO %s", client
);
253 * If the user asked for SASL, then check to see if the SMTP server
254 * supports it. Otherwise, error out (because the SMTP server
255 * might have been spoofed; we don't want to just silently not
261 if (! (server_mechs
= EHLOset("AUTH"))) {
263 return sm_ierror("SMTP server does not support SASL");
266 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
268 return sm_nerror(errstr
);
273 if (watch
&& EHLOset ("XVRB"))
274 smtalk (SM_HELO
, "VERB on");
280 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
281 const char *saslmech
, const char *user
)
283 unsigned int i
, result
, vecp
;
285 char *vec
[15], *errstr
;
290 sm_verbose
= verbose
;
295 if (client
== NULL
|| *client
== '\0') {
299 client
= LocalName(1); /* no clientname -> LocalName */
302 * Last-ditch check just in case client still isn't set to anything
304 if (client
== NULL
|| *client
== '\0')
305 client
= "localhost";
311 netsec_set_userid(nsc
, user
);
313 netsec_set_hostname(nsc
, client
);
316 netsec_set_snoop(nsc
, 1);
319 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
321 return sm_nerror(errstr
);
324 if (pipe (pdi
) == NOTOK
)
325 return sm_ierror ("no pipes");
326 if (pipe (pdo
) == NOTOK
) {
329 return sm_ierror ("no pipes");
332 for (i
= 0; (sm_child
= fork ()) == NOTOK
&& i
< 5; i
++)
341 return sm_ierror ("unable to fork");
344 if (pdo
[0] != fileno (stdin
))
345 dup2 (pdo
[0], fileno (stdin
));
346 if (pdi
[1] != fileno (stdout
))
347 dup2 (pdi
[1], fileno (stdout
));
348 if (pdi
[1] != fileno (stderr
))
349 dup2 (pdi
[1], fileno (stderr
));
350 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
354 vec
[vecp
++] = r1bindex (sendmail
, '/');
356 vec
[vecp
++] = watch
? "-odi" : "-odb";
357 vec
[vecp
++] = "-oem";
363 execvp (sendmail
, vec
);
364 fprintf (stderr
, "unable to exec ");
366 _exit (-1); /* NOTREACHED */
369 SIGNAL (SIGPIPE
, SIG_IGN
);
374 netsec_set_fd(nsc
, pdi
[i
], pdo
[1]);
375 netsec_set_timeout(nsc
, SM_OPEN
);
387 result
= smtalk (SM_HELO
, "EHLO %s", client
);
390 if (500 <= result
&& result
<= 599)
391 result
= smtalk (SM_HELO
, "HELO %s", client
);
403 * If the user asked for SASL, then check to see if the SMTP server
404 * supports it. Otherwise, error out (because the SMTP server
405 * might have been spoofed; we don't want to just silently not
411 if (! (server_mechs
= EHLOset("AUTH"))) {
413 return sm_ierror("SMTP server does not support SASL");
415 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
417 return sm_nerror(errstr
);
422 smtalk (SM_HELO
, "VERB on");
429 rclient (char *server
, char *service
, char **chosen_server
)
432 char response
[BUFSIZ
];
437 *chosen_server
= server
;
439 if ((sd
= client (server
, service
, response
, sizeof(response
),
443 sm_ierror ("%s", response
);
448 sm_winit (char *from
, int smtputf8
, int eightbit
)
450 const char *mail_parameters
= "";
453 /* Just for information, if an attempt is made to send to an 8-bit
454 address without specifying SMTPUTF8, Gmail responds with
455 555 5.5.2 Syntax error.
456 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
457 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
458 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
460 advise (NULL
, "SMTP server does not support %s, not sending.\n"
461 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
462 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
466 } else if (eightbit
) {
467 /* Comply with RFC 6152, for messages that have any 8-bit characters
469 if (EHLOset ("8BITMIME")) {
470 mail_parameters
= " BODY=8BITMIME";
472 advise (NULL
, "SMTP server does not support 8BITMIME, not sending.\n"
473 "Suggest encoding message for 7-bit transport by setting your\n"
474 "locale to C, and/or specifying *b64 in mhbuild directives.");
480 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
497 sm_wadr (char *mbox
, char *host
, char *path
)
499 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
501 path
? path
: "", mbox
, host
)) {
511 #endif /* SENDMAILBUG */
536 switch (smtalk (SM_DATA
, "DATA")) {
545 #endif /* SENDMAILBUG */
562 sm_wtxt (char *buffer
, int len
)
566 result
= sm_wstream (buffer
, len
);
568 return (result
== NOTOK
? RP_BHST
: RP_OK
);
575 if (sm_wstream ((char *) NULL
, 0) == NOTOK
)
578 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
586 #endif /* SENDMAILBUG */
604 if (sm_mts
== MTS_SENDMAIL_SMTP
) {
620 smtalk (SM_QUIT
, "QUIT");
624 sm_note
.code
= sm_reply
.code
;
625 sm_note
.length
= sm_reply
.length
;
626 memcpy (sm_note
.text
, sm_reply
.text
, sm_reply
.length
+ 1);
629 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
631 if (sm_mts
== MTS_SMTP
)
632 smtalk (SM_QUIT
, "QUIT");
634 /* The SIGPIPE block replaces old calls to discard ().
635 We're not sure what the discard () calls were for,
636 maybe to prevent deadlock on old systems. In any
637 case, blocking SIGPIPE should be harmless.
638 Because the file handles are closed below, leave it
642 sigaddset (&set
, SIGPIPE
);
643 sigprocmask (SIG_BLOCK
, &set
, &oset
);
645 kill (sm_child
, SIGKILL
);
649 sm_reply
.code
= sm_note
.code
;
650 sm_reply
.length
= sm_note
.length
;
651 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
657 netsec_shutdown(nsc
, 1);
661 if (sm_mts
== MTS_SMTP
) {
663 } else if (sm_child
!= NOTOK
) {
664 status
= pidwait (sm_child
, OK
);
670 return (status
? RP_BHST
: RP_OK
);
675 sm_ierror (const char *fmt
, ...)
680 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
683 sm_reply
.length
= strlen (sm_reply
.text
);
684 sm_reply
.code
= NOTOK
;
690 * Like sm_ierror, but assume it's an allocated error string we need to free.
694 sm_nerror (char *str
)
696 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
697 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
698 sm_reply
.length
= strlen(sm_reply
.text
);
699 sm_reply
.code
= NOTOK
;
706 smtalk (int time
, char *fmt
, ...)
713 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
717 return sm_nerror(errstr
);
719 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
720 return sm_nerror(errstr
);
722 if (netsec_flush (nsc
, &errstr
) != OK
)
723 return sm_nerror(errstr
);
725 netsec_set_timeout(nsc
, time
);
732 sm_wstream (char *buffer
, int len
)
735 static char lc
= '\0';
739 sm_ierror("No socket opened");
743 if (buffer
== NULL
&& len
== 0) {
746 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
753 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
757 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
765 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
774 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
789 int i
, code
, cont
, more
;
794 char **ehlo
= EHLOkeys
, *buffer
;
797 static int at_least_once
= 0;
802 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
817 sm_reply
.text
[0] = 0;
819 rc
= sizeof(sm_reply
.text
) - 1;
821 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
822 &errstr
)) != NULL
; ) {
825 && has_prefix(buffer
, "250")
826 && (buffer
[3] == '-' || doingEHLO
== 2)
828 if (doingEHLO
== 2) {
829 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
830 strcpy (*ehlo
++, buffer
+ 4);
832 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
842 bp
= (unsigned char *) buffer
;
844 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
848 code
= atoi ((char *) bp
);
849 bp
+= 3, buflen
-= 3;
850 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
852 if (buflen
> 0 && *bp
== '-') {
855 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
860 if (code
!= sm_reply
.code
|| cont
)
864 sm_reply
.code
= code
;
867 bp
= (unsigned char *) sm_noreply
;
868 buflen
= strlen (sm_noreply
);
872 if ((i
= min (buflen
, rc
)) > 0) {
876 i
= strlen(sm_moreply
);
877 if (more
&& (int) rc
> i
+ 1) {
878 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
885 if (sm_reply
.code
< 100) {
893 sm_reply
.length
= rp
- sm_reply
.text
;
894 sm_reply
.text
[sm_reply
.length
] = 0;
895 return sm_reply
.code
;
907 static char buffer
[BUFSIZ
];
909 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
929 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
949 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
950 text
, sm_reply
.code
, sm_reply
.text
);
962 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
964 if (has_prefix(ep
, s
)) {
965 for (ep
+= len
; *ep
== ' '; ep
++)
975 * Our SASL callback; we are either given SASL tokens to generate network
976 * protocols messages for, or we decode incoming protocol messages and
977 * convert them to binary SASL tokens to pass up into the SASL library.
981 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
982 unsigned int indatalen
, unsigned char **outdata
,
983 unsigned int *outdatalen
, char **errstr
)
990 case NETSEC_SASL_START
:
992 * Generate an AUTH message; if we were given an input token
993 * then generate a an AUTH message that includes the initial
997 mech
= netsec_get_sasl_mechanism(nsc
);
1002 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1003 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
1005 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
1007 snoopoffset
= 6 + strlen(mech
);
1008 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
1010 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1012 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1018 if (netsec_flush(nsc
, errstr
) != OK
)
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
);
1072 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1079 if (netsec_flush(nsc
, errstr
) != OK
)
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
);