]>
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 */
134 * Last-ditch check just in case client still isn't set to anything
137 if (client
== NULL
|| *client
== '\0')
138 client
= "localhost";
143 netsec_set_userid(nsc
, user
);
146 netsec_set_snoop(nsc
, 1);
149 if (netsec_set_sasl_params(nsc
, server
, "smtp", saslmech
,
150 sm_sasl_callback
, &errstr
) != OK
)
151 return sm_nerror(errstr
);
155 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
156 return sm_ierror("OAuth2 not supported");
159 if ((sd1
= rclient (server
, port
)) == NOTOK
)
162 SIGNAL (SIGPIPE
, SIG_IGN
);
164 netsec_set_fd(nsc
, sd1
, sd1
);
167 if (netsec_set_tls(nsc
, 1, &errstr
) != OK
)
168 return sm_nerror(errstr
);
172 * If tls == 2, that means that the user requested "initial" TLS,
173 * which happens right after the connection has opened. Do that
178 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
180 return sm_nerror(errstr
);
184 netsec_set_timeout(nsc
, SM_OPEN
);
197 * Give EHLO or HELO command
201 result
= smtalk (SM_HELO
, "EHLO %s", client
);
204 if (result
>= 500 && result
<= 599)
205 result
= smtalk (SM_HELO
, "HELO %s", client
);
213 * If the user requested TLS support, then try to do the STARTTLS command
214 * as part of the initial dialog. Assuming this works, we then need to
215 * restart the EHLO dialog after TLS negotiation is complete.
219 if (! EHLOset("STARTTLS")) {
221 return sm_ierror("SMTP server does not support TLS");
224 result
= smtalk(SM_HELO
, "STARTTLS");
232 * Okay, the other side should be waiting for us to start TLS
233 * negotiation. Oblige them.
236 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
238 return sm_nerror(errstr
);
242 result
= smtalk (SM_HELO
, "EHLO %s", client
);
252 * If the user asked for SASL, then check to see if the SMTP server
253 * supports it. Otherwise, error out (because the SMTP server
254 * might have been spoofed; we don't want to just silently not
260 if (! (server_mechs
= EHLOset("AUTH"))) {
262 return sm_ierror("SMTP server does not support SASL");
265 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
267 return sm_nerror(errstr
);
272 if (watch
&& EHLOset ("XVRB"))
273 smtalk (SM_HELO
, "VERB on");
279 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
280 const char *saslmech
, const char *user
)
282 unsigned int i
, result
, vecp
;
284 char *vec
[15], *errstr
;
289 sm_verbose
= verbose
;
294 if (client
== NULL
|| *client
== '\0') {
298 client
= LocalName(1); /* no clientname -> LocalName */
302 * Last-ditch check just in case client still isn't set to anything
305 if (client
== NULL
|| *client
== '\0')
306 client
= "localhost";
311 netsec_set_userid(nsc
, user
);
314 netsec_set_snoop(nsc
, 1);
317 if (netsec_set_sasl_params(nsc
, client
, "smtp", saslmech
,
318 sm_sasl_callback
, &errstr
) != OK
)
319 return sm_nerror(errstr
);
322 if (pipe (pdi
) == NOTOK
)
323 return sm_ierror ("no pipes");
324 if (pipe (pdo
) == NOTOK
) {
327 return sm_ierror ("no pipes");
330 for (i
= 0; (sm_child
= fork ()) == NOTOK
&& i
< 5; i
++)
339 return sm_ierror ("unable to fork");
342 if (pdo
[0] != fileno (stdin
))
343 dup2 (pdo
[0], fileno (stdin
));
344 if (pdi
[1] != fileno (stdout
))
345 dup2 (pdi
[1], fileno (stdout
));
346 if (pdi
[1] != fileno (stderr
))
347 dup2 (pdi
[1], fileno (stderr
));
348 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
352 vec
[vecp
++] = r1bindex (sendmail
, '/');
354 vec
[vecp
++] = watch
? "-odi" : "-odb";
355 vec
[vecp
++] = "-oem";
361 execvp (sendmail
, vec
);
362 fprintf (stderr
, "unable to exec ");
364 _exit (-1); /* NOTREACHED */
367 SIGNAL (SIGPIPE
, SIG_IGN
);
372 netsec_set_fd(nsc
, pdi
[i
], pdo
[1]);
373 netsec_set_timeout(nsc
, SM_OPEN
);
385 result
= smtalk (SM_HELO
, "EHLO %s", client
);
388 if (500 <= result
&& result
<= 599)
389 result
= smtalk (SM_HELO
, "HELO %s", client
);
401 * If the user asked for SASL, then check to see if the SMTP server
402 * supports it. Otherwise, error out (because the SMTP server
403 * might have been spoofed; we don't want to just silently not
409 if (! (server_mechs
= EHLOset("AUTH"))) {
411 return sm_ierror("SMTP server does not support SASL");
413 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
415 return sm_nerror(errstr
);
420 smtalk (SM_HELO
, "VERB on");
427 rclient (char *server
, char *service
)
430 char response
[BUFSIZ
];
432 if ((sd
= client (server
, service
, response
, sizeof(response
),
436 sm_ierror ("%s", response
);
441 sm_winit (char *from
, int smtputf8
, int eightbit
)
443 const char *mail_parameters
= "";
446 /* Just for information, if an attempt is made to send to an 8-bit
447 address without specifying SMTPUTF8, Gmail responds with
448 555 5.5.2 Syntax error.
449 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
450 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
451 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
453 advise (NULL
, "SMTP server does not support %s, not sending.\n"
454 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
455 EHLOset ("SMTPUTF8") ? "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.");
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", LEN("250")) == 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
);