]>
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!
46 #define NBITS ((sizeof (int)) * 8)
49 * these codes must all be different!
51 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
54 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
55 #define SM_RCPT 302 /* see above */
56 #define SM_DATA 120 /* see above */
57 #define SM_TEXT 180 /* see above */
58 #define SM_DOT 600 /* see above */
63 static int sm_addrs
= 0;
64 static int sm_child
= NOTOK
;
65 static int sm_debug
= 0;
66 static int sm_nl
= TRUE
;
67 static int sm_verbose
= 0;
68 static netsec_context
*nsc
= NULL
;
70 static char *sm_noreply
= "No reply text given";
71 static char *sm_moreply
= "; ";
72 static struct smtp sm_reply
;
77 static char *EHLOkeys
[MAXEHLO
+ 1];
82 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
83 const char *, const char *, int);
84 static int sendmail_init (char *, int, int, int, int, const char *,
87 static int rclient (char *, char *);
88 static int sm_ierror (const char *fmt
, ...);
89 static int sm_nerror (char *);
90 static int smtalk (int time
, char *fmt
, ...);
91 static int sm_wstream (char *, int);
92 static int smhear (void);
93 static char *EHLOset (char *);
94 static int sm_sasl_callback(enum sasl_message_type
, unsigned const char *,
95 unsigned int, unsigned char **, unsigned int *,
99 sm_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
100 int debug
, int sasl
, const char *saslmech
, const char *user
,
101 const char *oauth_svc
, int tls
)
103 if (sm_mts
== MTS_SMTP
)
104 return smtp_init (client
, server
, port
, watch
, verbose
,
105 debug
, sasl
, saslmech
, user
, oauth_svc
, tls
);
107 return sendmail_init (client
, watch
, verbose
, debug
, sasl
,
112 smtp_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
113 int debug
, int sasl
, const char *saslmech
, const char *user
,
114 const char *oauth_svc
, int tls
)
122 sm_verbose
= verbose
;
128 if (client
== NULL
|| *client
== '\0') {
132 client
= LocalName(1); /* no clientname -> LocalName */
137 * Last-ditch check just in case client still isn't set to anything
140 if (client
== NULL
|| *client
== '\0')
141 client
= "localhost";
146 netsec_set_userid(nsc
, user
);
149 netsec_set_snoop(nsc
, 1);
152 if (netsec_set_sasl_params(nsc
, server
, "smtp", saslmech
,
153 sm_sasl_callback
, &errstr
) != OK
)
154 return sm_nerror(errstr
);
158 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
159 return sm_ierror("OAuth2 not supported");
162 if ((sd1
= rclient (server
, port
)) == NOTOK
)
165 SIGNAL (SIGPIPE
, SIG_IGN
);
167 netsec_set_fd(nsc
, sd1
, sd1
);
170 if (netsec_set_tls(nsc
, 1, &errstr
) != OK
)
171 return sm_nerror(errstr
);
175 * If tls == 2, that means that the user requested "initial" TLS,
176 * which happens right after the connection has opened. Do that
181 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
183 return sm_nerror(errstr
);
187 netsec_set_timeout(nsc
, SM_OPEN
);
200 * Give EHLO or HELO command
204 result
= smtalk (SM_HELO
, "EHLO %s", client
);
207 if (result
>= 500 && result
<= 599)
208 result
= smtalk (SM_HELO
, "HELO %s", client
);
216 * If the user requested TLS support, then try to do the STARTTLS command
217 * as part of the initial dialog. Assuming this works, we then need to
218 * restart the EHLO dialog after TLS negotiation is complete.
222 if (! EHLOset("STARTTLS")) {
224 return sm_ierror("SMTP server does not support TLS");
227 result
= smtalk(SM_HELO
, "STARTTLS");
235 * Okay, the other side should be waiting for us to start TLS
236 * negotiation. Oblige them.
239 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
241 return sm_nerror(errstr
);
245 result
= smtalk (SM_HELO
, "EHLO %s", client
);
255 * If the user asked for SASL, then check to see if the SMTP server
256 * supports it. Otherwise, error out (because the SMTP server
257 * might have been spoofed; we don't want to just silently not
263 if (! (server_mechs
= EHLOset("AUTH"))) {
265 return sm_ierror("SMTP server does not support SASL");
268 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
270 return sm_nerror(errstr
);
275 if (watch
&& EHLOset ("XVRB"))
276 smtalk (SM_HELO
, "VERB on");
282 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
283 const char *saslmech
, const char *user
)
285 unsigned int i
, result
, vecp
;
287 char *vec
[15], *errstr
;
292 sm_verbose
= verbose
;
297 if (client
== NULL
|| *client
== '\0') {
301 client
= LocalName(1); /* no clientname -> LocalName */
305 * Last-ditch check just in case client still isn't set to anything
308 if (client
== NULL
|| *client
== '\0')
309 client
= "localhost";
314 netsec_set_userid(nsc
, user
);
317 netsec_set_snoop(nsc
, 1);
320 if (netsec_set_sasl_params(nsc
, client
, "smtp", saslmech
,
321 sm_sasl_callback
, &errstr
) != OK
)
322 return sm_nerror(errstr
);
325 if (pipe (pdi
) == NOTOK
)
326 return sm_ierror ("no pipes");
327 if (pipe (pdo
) == NOTOK
) {
330 return sm_ierror ("no pipes");
333 for (i
= 0; (sm_child
= fork ()) == NOTOK
&& i
< 5; i
++)
342 return sm_ierror ("unable to fork");
345 if (pdo
[0] != fileno (stdin
))
346 dup2 (pdo
[0], fileno (stdin
));
347 if (pdi
[1] != fileno (stdout
))
348 dup2 (pdi
[1], fileno (stdout
));
349 if (pdi
[1] != fileno (stderr
))
350 dup2 (pdi
[1], fileno (stderr
));
351 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
355 vec
[vecp
++] = r1bindex (sendmail
, '/');
357 vec
[vecp
++] = watch
? "-odi" : "-odb";
358 vec
[vecp
++] = "-oem";
364 execvp (sendmail
, vec
);
365 fprintf (stderr
, "unable to exec ");
367 _exit (-1); /* NOTREACHED */
370 SIGNAL (SIGPIPE
, SIG_IGN
);
375 netsec_set_fd(nsc
, pdi
[i
], pdo
[1]);
376 netsec_set_timeout(nsc
, SM_OPEN
);
388 result
= smtalk (SM_HELO
, "EHLO %s", client
);
391 if (500 <= result
&& result
<= 599)
392 result
= smtalk (SM_HELO
, "HELO %s", client
);
404 * If the user asked for SASL, then check to see if the SMTP server
405 * supports it. Otherwise, error out (because the SMTP server
406 * might have been spoofed; we don't want to just silently not
412 if (! (server_mechs
= EHLOset("AUTH"))) {
414 return sm_ierror("SMTP server does not support SASL");
416 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
418 return sm_nerror(errstr
);
423 smtalk (SM_HELO
, "VERB on");
430 rclient (char *server
, char *service
)
433 char response
[BUFSIZ
];
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";
459 } else if (eightbit
) {
460 /* Comply with RFC 6152, for messages that have any 8-bit characters
462 if (EHLOset ("8BITMIME")) {
463 mail_parameters
= " BODY=8BITMIME";
465 advise (NULL
, "SMTP server does not support 8BITMIME, not sending.\n"
466 "Suggest encoding message for 7-bit transport by setting your\n"
467 "locale to C, and/or specifying *b64 in mhbuild directives.\n");
473 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
490 sm_wadr (char *mbox
, char *host
, char *path
)
492 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
494 path
? path
: "", mbox
, host
)) {
504 #endif /* SENDMAILBUG */
529 switch (smtalk (SM_DATA
, "DATA")) {
538 #endif /* SENDMAILBUG */
555 sm_wtxt (char *buffer
, int len
)
559 result
= sm_wstream (buffer
, len
);
561 return (result
== NOTOK
? RP_BHST
: RP_OK
);
568 if (sm_wstream ((char *) NULL
, 0) == NOTOK
)
571 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
579 #endif /* SENDMAILBUG */
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);/* fall */
621 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
623 if (sm_mts
== MTS_SMTP
)
624 smtalk (SM_QUIT
, "QUIT");
626 /* The SIGPIPE block replaces old calls to discard ().
627 We're not sure what the discard () calls were for,
628 maybe to prevent deadlock on old systems. In any
629 case, blocking SIGPIPE should be harmless.
630 Because the file handles are closed below, leave it
634 sigaddset (&set
, SIGPIPE
);
635 sigprocmask (SIG_BLOCK
, &set
, &oset
);
637 kill (sm_child
, SIGKILL
);
641 sm_reply
.code
= sm_note
.code
;
642 sm_reply
.length
= sm_note
.length
;
643 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
649 netsec_shutdown(nsc
, 1);
653 if (sm_mts
== MTS_SMTP
) {
655 } else if (sm_child
!= NOTOK
) {
656 status
= pidwait (sm_child
, OK
);
662 return (status
? RP_BHST
: RP_OK
);
667 sm_ierror (const char *fmt
, ...)
672 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
675 sm_reply
.length
= strlen (sm_reply
.text
);
676 sm_reply
.code
= NOTOK
;
682 * Like sm_ierror, but assume it's an allocated error string we need to free.
686 sm_nerror (char *str
)
688 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
689 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
690 sm_reply
.length
= strlen(sm_reply
.text
);
691 sm_reply
.code
= NOTOK
;
698 smtalk (int time
, char *fmt
, ...)
705 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
709 return sm_nerror(errstr
);
711 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
712 return sm_nerror(errstr
);
714 if (netsec_flush (nsc
, &errstr
) != OK
)
715 return sm_nerror(errstr
);
717 netsec_set_timeout(nsc
, time
);
724 sm_wstream (char *buffer
, int len
)
727 static char lc
= '\0';
731 sm_ierror("No socket opened");
735 if (buffer
== NULL
&& len
== 0) {
738 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
745 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
749 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
757 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
765 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
780 int i
, code
, cont
, more
;
785 char **ehlo
= EHLOkeys
, *buffer
;
788 static int at_least_once
= 0;
793 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
808 sm_reply
.text
[0] = 0;
810 rc
= sizeof(sm_reply
.text
) - 1;
812 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
813 &errstr
)) != NULL
; ) {
816 && strncmp (buffer
, "250", sizeof("250") - 1) == 0
817 && (buffer
[3] == '-' || doingEHLO
== 2)
819 if (doingEHLO
== 2) {
820 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
821 strcpy (*ehlo
++, buffer
+ 4);
823 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
833 bp
= (unsigned char *) buffer
;
835 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
839 code
= atoi ((char *) bp
);
840 bp
+= 3, buflen
-= 3;
841 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
843 if (buflen
> 0 && *bp
== '-') {
846 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
851 if (code
!= sm_reply
.code
|| cont
)
855 sm_reply
.code
= code
;
858 bp
= (unsigned char *) sm_noreply
;
859 buflen
= strlen (sm_noreply
);
863 if ((i
= min (buflen
, rc
)) > 0) {
867 i
= strlen(sm_moreply
);
868 if (more
&& (int) rc
> i
+ 1) {
869 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
876 if (sm_reply
.code
< 100) {
878 printf ("%s\n", sm_reply
.text
);
884 sm_reply
.length
= rp
- sm_reply
.text
;
885 sm_reply
.text
[sm_reply
.length
] = 0;
886 return sm_reply
.code
;
898 static char buffer
[BUFSIZ
];
900 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
920 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
940 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
941 text
, sm_reply
.code
, sm_reply
.text
);
953 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
955 if (strncmp (ep
, s
, len
) == 0) {
956 for (ep
+= len
; *ep
== ' '; ep
++)
966 * Our SASL callback; we are either given SASL tokens to generate network
967 * protocols messages for, or we decode incoming protocol messages and
968 * convert them to binary SASL tokens to pass up into the SASL library.
972 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
973 unsigned int indatalen
, unsigned char **outdata
,
974 unsigned int *outdatalen
, char **errstr
)
981 case NETSEC_SASL_START
:
983 * Generate an AUTH message; if we were given an input token
984 * then generate a an AUTH message that includes the initial
988 mech
= netsec_get_sasl_mechanism(nsc
);
993 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
994 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
996 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
998 snoopoffset
= 6 + strlen(mech
);
999 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
1001 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1003 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1009 if (netsec_flush(nsc
, errstr
) != OK
)
1014 case NETSEC_SASL_READ
:
1016 * Read in a line that should contain a 334 response code, followed
1017 * by base64 response data.
1020 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1022 line
= netsec_readline(nsc
, &len
, errstr
);
1023 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1029 netsec_err(errstr
, "Invalid format for SASL response");
1033 if (strncmp(line
, "334 ", 4) != 0) {
1034 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1042 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1044 netsec_err(errstr
, "Unable to decode base64 response");
1051 case NETSEC_SASL_WRITE
:
1053 * The output encoding is pretty simple, so this is easy.
1055 if (indatalen
== 0) {
1056 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1058 unsigned char *b64data
;
1059 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1060 writeBase64raw(indata
, indatalen
, b64data
);
1061 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1062 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1063 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1070 if (netsec_flush(nsc
, errstr
) != OK
)
1074 case NETSEC_SASL_FINISH
:
1076 * Finish the protocol; we're looking for a 235 message.
1078 line
= netsec_readline(nsc
, &len
, errstr
);
1082 if (strncmp(line
, "235 ", 4) != 0) {
1084 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1086 netsec_err(errstr
, "Authentication failed: %s", line
);
1091 case NETSEC_SASL_CANCEL
:
1093 * Cancel the SASL exchange; this is done by sending a single "*".
1095 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1097 rc
= netsec_flush(nsc
, errstr
);