]>
diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
77bcb6e544df97bb8fceb618a62703ee9fe9097a
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);/* fall */
628 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
630 if (sm_mts
== MTS_SMTP
)
631 smtalk (SM_QUIT
, "QUIT");
633 /* The SIGPIPE block replaces old calls to discard ().
634 We're not sure what the discard () calls were for,
635 maybe to prevent deadlock on old systems. In any
636 case, blocking SIGPIPE should be harmless.
637 Because the file handles are closed below, leave it
641 sigaddset (&set
, SIGPIPE
);
642 sigprocmask (SIG_BLOCK
, &set
, &oset
);
644 kill (sm_child
, SIGKILL
);
648 sm_reply
.code
= sm_note
.code
;
649 sm_reply
.length
= sm_note
.length
;
650 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
656 netsec_shutdown(nsc
, 1);
660 if (sm_mts
== MTS_SMTP
) {
662 } else if (sm_child
!= NOTOK
) {
663 status
= pidwait (sm_child
, OK
);
669 return (status
? RP_BHST
: RP_OK
);
674 sm_ierror (const char *fmt
, ...)
679 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
682 sm_reply
.length
= strlen (sm_reply
.text
);
683 sm_reply
.code
= NOTOK
;
689 * Like sm_ierror, but assume it's an allocated error string we need to free.
693 sm_nerror (char *str
)
695 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
696 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
697 sm_reply
.length
= strlen(sm_reply
.text
);
698 sm_reply
.code
= NOTOK
;
705 smtalk (int time
, char *fmt
, ...)
712 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
716 return sm_nerror(errstr
);
718 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
719 return sm_nerror(errstr
);
721 if (netsec_flush (nsc
, &errstr
) != OK
)
722 return sm_nerror(errstr
);
724 netsec_set_timeout(nsc
, time
);
731 sm_wstream (char *buffer
, int len
)
734 static char lc
= '\0';
738 sm_ierror("No socket opened");
742 if (buffer
== NULL
&& len
== 0) {
745 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
752 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
756 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
764 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
772 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
787 int i
, code
, cont
, more
;
792 char **ehlo
= EHLOkeys
, *buffer
;
795 static int at_least_once
= 0;
800 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
815 sm_reply
.text
[0] = 0;
817 rc
= sizeof(sm_reply
.text
) - 1;
819 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
820 &errstr
)) != NULL
; ) {
823 && has_prefix(buffer
, "250")
824 && (buffer
[3] == '-' || doingEHLO
== 2)
826 if (doingEHLO
== 2) {
827 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
828 strcpy (*ehlo
++, buffer
+ 4);
830 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
840 bp
= (unsigned char *) buffer
;
842 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
846 code
= atoi ((char *) bp
);
847 bp
+= 3, buflen
-= 3;
848 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
850 if (buflen
> 0 && *bp
== '-') {
853 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
858 if (code
!= sm_reply
.code
|| cont
)
862 sm_reply
.code
= code
;
865 bp
= (unsigned char *) sm_noreply
;
866 buflen
= strlen (sm_noreply
);
870 if ((i
= min (buflen
, rc
)) > 0) {
874 i
= strlen(sm_moreply
);
875 if (more
&& (int) rc
> i
+ 1) {
876 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
883 if (sm_reply
.code
< 100) {
891 sm_reply
.length
= rp
- sm_reply
.text
;
892 sm_reply
.text
[sm_reply
.length
] = 0;
893 return sm_reply
.code
;
905 static char buffer
[BUFSIZ
];
907 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
927 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
947 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
948 text
, sm_reply
.code
, sm_reply
.text
);
960 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
962 if (has_prefix(ep
, s
)) {
963 for (ep
+= len
; *ep
== ' '; ep
++)
973 * Our SASL callback; we are either given SASL tokens to generate network
974 * protocols messages for, or we decode incoming protocol messages and
975 * convert them to binary SASL tokens to pass up into the SASL library.
979 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
980 unsigned int indatalen
, unsigned char **outdata
,
981 unsigned int *outdatalen
, char **errstr
)
988 case NETSEC_SASL_START
:
990 * Generate an AUTH message; if we were given an input token
991 * then generate a an AUTH message that includes the initial
995 mech
= netsec_get_sasl_mechanism(nsc
);
1000 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1001 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
1003 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
1005 snoopoffset
= 6 + strlen(mech
);
1006 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
1008 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1010 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1016 if (netsec_flush(nsc
, errstr
) != OK
)
1021 case NETSEC_SASL_READ
:
1023 * Read in a line that should contain a 334 response code, followed
1024 * by base64 response data.
1027 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1029 line
= netsec_readline(nsc
, &len
, errstr
);
1030 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1036 netsec_err(errstr
, "Invalid format for SASL response");
1040 if (!has_prefix(line
, "334 ")) {
1041 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1049 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1051 netsec_err(errstr
, "Unable to decode base64 response");
1058 case NETSEC_SASL_WRITE
:
1060 * The output encoding is pretty simple, so this is easy.
1062 if (indatalen
== 0) {
1063 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1065 unsigned char *b64data
;
1066 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1067 writeBase64raw(indata
, indatalen
, b64data
);
1068 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1069 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1070 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1077 if (netsec_flush(nsc
, errstr
) != OK
)
1081 case NETSEC_SASL_FINISH
:
1083 * Finish the protocol; we're looking for a 235 message.
1085 line
= netsec_readline(nsc
, &len
, errstr
);
1089 if (!has_prefix(line
, "235 ")) {
1091 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1093 netsec_err(errstr
, "Authentication failed: %s", line
);
1098 case NETSEC_SASL_CANCEL
:
1100 * Cancel the SASL exchange; this is done by sending a single "*".
1102 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1104 rc
= netsec_flush(nsc
, errstr
);