]>
diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
66549a5503630c4f00cc698f842c8c6335cf8ac4
1 /* smtp.c -- nmh SMTP interface
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
11 #include <h/signals.h>
15 #include <sys/socket.h>
18 * This module implements an interface to SendMail very similar
19 * to the MMDF mm_(3) routines. The sm_() routines herein talk
20 * SMTP to a sendmail process, mapping SMTP reply codes into
26 * It appears that some versions of Sendmail will return Code 451
27 * when they don't really want to indicate a failure.
28 * "Code 451 almost always means sendmail has deferred; we don't
29 * really want bomb out at this point since sendmail will rectify
30 * things later." So, if you define SENDMAILBUG, Code 451 is
31 * considered the same as Code 250. Yuck!
35 #define NBITS ((sizeof (int)) * 8)
37 /* Timeout in seconds for SMTP commands.
38 * Lore has it they must be distinct. */
39 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
42 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
43 #define SM_RCPT 302 /* see above */
44 #define SM_DATA 120 /* see above */
45 #define SM_DOT 600 /* see above */
48 static int sm_addrs
= 0;
49 static int sm_child
= NOTOK
;
50 static int sm_debug
= 0;
51 static int sm_nl
= TRUE
;
52 static int sm_verbose
= 0;
53 static netsec_context
*nsc
= NULL
;
55 static char *sm_noreply
= "No reply text given";
56 static char *sm_moreply
= "; ";
57 static struct smtp sm_reply
;
62 static char *EHLOkeys
[MAXEHLO
+ 1];
67 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
68 const char *, const char *, int);
69 static int sendmail_init (char *, int, int, int, int, const char *,
72 static int rclient (char *, char *, char **);
73 static int sm_ierror (const char *fmt
, ...);
74 static int sm_nerror (char *);
75 static int smtalk (int time
, char *fmt
, ...);
76 static int sm_wstream (char *, int);
77 static int smhear (void);
78 static char *EHLOset (char *);
79 static int sm_sasl_callback(enum sasl_message_type
, unsigned const char *,
80 unsigned int, unsigned char **, unsigned int *,
84 sm_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
85 int debug
, int sasl
, const char *saslmech
, const char *user
,
86 const char *oauth_svc
, int tls
)
88 if (sm_mts
== MTS_SMTP
)
89 return smtp_init (client
, server
, port
, watch
, verbose
,
90 debug
, sasl
, saslmech
, user
, oauth_svc
, tls
);
92 return sendmail_init (client
, watch
, verbose
, debug
, sasl
,
97 smtp_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
98 int debug
, int sasl
, const char *saslmech
, const char *user
,
99 const char *oauth_svc
, int tls
)
102 char *errstr
, *chosen_server
;
107 sm_verbose
= verbose
;
113 if (client
== NULL
|| *client
== '\0') {
117 client
= LocalName(1); /* no clientname -> LocalName */
121 * Last-ditch check just in case client still isn't set to anything
123 if (client
== NULL
|| *client
== '\0')
124 client
= "localhost";
130 netsec_set_userid(nsc
, user
);
132 if ((sd1
= rclient (server
, port
, &chosen_server
)) == NOTOK
)
135 SIGNAL (SIGPIPE
, SIG_IGN
);
137 netsec_set_fd(nsc
, sd1
, sd1
);
139 netsec_set_hostname(nsc
, chosen_server
);
142 netsec_set_snoop(nsc
, 1);
145 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
147 return sm_nerror(errstr
);
151 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
152 return sm_ierror("OAuth2 not supported");
155 if (tls
& S_TLSENABLEMASK
) {
156 if (netsec_set_tls(nsc
, 1, tls
& S_NOVERIFY
, &errstr
) != OK
)
157 return sm_nerror(errstr
);
161 * If tls == S_INITTLS, that means that the user requested
162 * "initial" TLS, which happens right after the connection has
163 * opened. Do that negotiation now
166 if (tls
& S_INITTLS
) {
167 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
169 return sm_nerror(errstr
);
173 netsec_set_timeout(nsc
, SM_OPEN
);
186 * Give EHLO or HELO command
190 result
= smtalk (SM_HELO
, "EHLO %s", client
);
193 if (result
>= 500 && result
<= 599)
194 result
= smtalk (SM_HELO
, "HELO %s", client
);
202 * If the user requested TLS support, then try to do the STARTTLS command
203 * as part of the initial dialog. Assuming this works, we then need to
204 * restart the EHLO dialog after TLS negotiation is complete.
207 if (tls
& S_STARTTLS
) {
208 if (! EHLOset("STARTTLS")) {
210 return sm_ierror("SMTP server does not support TLS");
213 result
= smtalk(SM_HELO
, "STARTTLS");
221 * Okay, the other side should be waiting for us to start TLS
222 * negotiation. Oblige them.
225 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
227 return sm_nerror(errstr
);
231 result
= smtalk (SM_HELO
, "EHLO %s", client
);
241 * If the user asked for SASL, then check to see if the SMTP server
242 * supports it. Otherwise, error out (because the SMTP server
243 * might have been spoofed; we don't want to just silently not
249 if (! (server_mechs
= EHLOset("AUTH"))) {
251 return sm_ierror("SMTP server does not support SASL");
254 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
256 return sm_nerror(errstr
);
261 if (watch
&& EHLOset ("XVRB"))
262 smtalk (SM_HELO
, "VERB on");
268 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
269 const char *saslmech
, const char *user
)
271 unsigned int i
, result
, vecp
;
273 char *vec
[15], *errstr
;
278 sm_verbose
= verbose
;
283 if (client
== NULL
|| *client
== '\0') {
287 client
= LocalName(1); /* no clientname -> LocalName */
290 * Last-ditch check just in case client still isn't set to anything
292 if (client
== NULL
|| *client
== '\0')
293 client
= "localhost";
299 netsec_set_userid(nsc
, user
);
301 netsec_set_hostname(nsc
, client
);
304 netsec_set_snoop(nsc
, 1);
307 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
309 return sm_nerror(errstr
);
312 if (pipe (pdi
) == NOTOK
)
313 return sm_ierror ("no pipes");
314 if (pipe (pdo
) == NOTOK
) {
317 return sm_ierror ("no pipes");
320 for (i
= 0; (sm_child
= fork ()) == NOTOK
&& i
< 5; i
++)
329 return sm_ierror ("unable to fork");
332 if (pdo
[0] != fileno (stdin
))
333 dup2 (pdo
[0], fileno (stdin
));
334 if (pdi
[1] != fileno (stdout
))
335 dup2 (pdi
[1], fileno (stdout
));
336 if (pdi
[1] != fileno (stderr
))
337 dup2 (pdi
[1], fileno (stderr
));
338 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
342 vec
[vecp
++] = r1bindex (sendmail
, '/');
344 vec
[vecp
++] = watch
? "-odi" : "-odb";
345 vec
[vecp
++] = "-oem";
351 execvp (sendmail
, vec
);
352 fprintf (stderr
, "unable to exec ");
354 _exit (-1); /* NOTREACHED */
357 SIGNAL (SIGPIPE
, SIG_IGN
);
362 netsec_set_fd(nsc
, pdi
[i
], pdo
[1]);
363 netsec_set_timeout(nsc
, SM_OPEN
);
375 result
= smtalk (SM_HELO
, "EHLO %s", client
);
378 if (500 <= result
&& result
<= 599)
379 result
= smtalk (SM_HELO
, "HELO %s", client
);
391 * If the user asked for SASL, then check to see if the SMTP server
392 * supports it. Otherwise, error out (because the SMTP server
393 * might have been spoofed; we don't want to just silently not
399 if (! (server_mechs
= EHLOset("AUTH"))) {
401 return sm_ierror("SMTP server does not support SASL");
403 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
405 return sm_nerror(errstr
);
410 smtalk (SM_HELO
, "VERB on");
417 rclient (char *server
, char *service
, char **chosen_server
)
420 char response
[BUFSIZ
];
425 *chosen_server
= server
;
427 if ((sd
= client (server
, service
, response
, sizeof(response
),
431 sm_ierror ("%s", response
);
436 sm_winit (char *from
, int smtputf8
, int eightbit
)
438 const char *mail_parameters
= "";
441 /* Just for information, if an attempt is made to send to an 8-bit
442 address without specifying SMTPUTF8, Gmail responds with
443 555 5.5.2 Syntax error.
444 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
445 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
446 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
448 inform("SMTP server does not support %s, not sending.\n"
449 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
450 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
454 } else if (eightbit
) {
455 /* Comply with RFC 6152, for messages that have any 8-bit characters
457 if (EHLOset ("8BITMIME")) {
458 mail_parameters
= " BODY=8BITMIME";
460 inform("SMTP server does not support 8BITMIME, not sending.\n"
461 "Suggest encoding message for 7-bit transport by setting your\n"
462 "locale to C, and/or specifying *b64 in mhbuild directives.");
468 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
485 sm_wadr (char *mbox
, char *host
, char *path
)
487 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
489 FENDNULL(path
), mbox
, host
)) {
499 #endif /* SENDMAILBUG */
524 switch (smtalk (SM_DATA
, "DATA")) {
533 #endif /* SENDMAILBUG */
550 sm_wtxt (char *buffer
, int len
)
554 result
= sm_wstream (buffer
, len
);
556 return (result
== NOTOK
? RP_BHST
: RP_OK
);
563 if (sm_wstream ((char *) NULL
, 0) == NOTOK
)
566 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
574 #endif /* SENDMAILBUG */
592 if (sm_mts
== MTS_SENDMAIL_SMTP
) {
608 smtalk (SM_QUIT
, "QUIT");
612 sm_note
.code
= sm_reply
.code
;
613 sm_note
.length
= sm_reply
.length
;
614 memcpy (sm_note
.text
, sm_reply
.text
, sm_reply
.length
+ 1);
617 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
619 if (sm_mts
== MTS_SMTP
)
620 smtalk (SM_QUIT
, "QUIT");
622 /* The SIGPIPE block replaces old calls to discard ().
623 We're not sure what the discard () calls were for,
624 maybe to prevent deadlock on old systems. In any
625 case, blocking SIGPIPE should be harmless.
626 Because the file handles are closed below, leave it
630 sigaddset (&set
, SIGPIPE
);
631 sigprocmask (SIG_BLOCK
, &set
, &oset
);
633 kill (sm_child
, SIGKILL
);
637 sm_reply
.code
= sm_note
.code
;
638 sm_reply
.length
= sm_note
.length
;
639 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
645 netsec_shutdown(nsc
);
649 if (sm_mts
== MTS_SMTP
) {
651 } else if (sm_child
!= NOTOK
) {
652 status
= pidwait (sm_child
, OK
);
658 return (status
? RP_BHST
: RP_OK
);
663 sm_ierror (const char *fmt
, ...)
668 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
671 sm_reply
.length
= strlen (sm_reply
.text
);
672 sm_reply
.code
= NOTOK
;
678 * Like sm_ierror, but assume it's an allocated error string we need to free.
682 sm_nerror (char *str
)
684 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
685 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
686 sm_reply
.length
= strlen(sm_reply
.text
);
687 sm_reply
.code
= NOTOK
;
694 smtalk (int time
, char *fmt
, ...)
701 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
705 return sm_nerror(errstr
);
707 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
708 return sm_nerror(errstr
);
710 if (netsec_flush (nsc
, &errstr
) != OK
)
711 return sm_nerror(errstr
);
713 netsec_set_timeout(nsc
, time
);
720 sm_wstream (char *buffer
, int len
)
723 static char lc
= '\0';
727 sm_ierror("No socket opened");
731 if (buffer
== NULL
&& len
== 0) {
734 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
741 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
745 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
753 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
762 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
777 int i
, code
, cont
, more
;
782 char **ehlo
= EHLOkeys
, *buffer
;
785 static int at_least_once
= 0;
790 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
805 sm_reply
.text
[0] = 0;
807 rc
= sizeof(sm_reply
.text
) - 1;
809 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
810 &errstr
)) != NULL
; ) {
813 && has_prefix(buffer
, "250")
814 && (buffer
[3] == '-' || doingEHLO
== 2)
816 if (doingEHLO
== 2) {
817 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
818 strcpy (*ehlo
++, buffer
+ 4);
820 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
830 bp
= (unsigned char *) buffer
;
832 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
836 code
= atoi ((char *) bp
);
837 bp
+= 3, buflen
-= 3;
838 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
840 if (buflen
> 0 && *bp
== '-') {
843 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
848 if (code
!= sm_reply
.code
|| cont
)
852 sm_reply
.code
= code
;
855 bp
= (unsigned char *) sm_noreply
;
856 buflen
= strlen (sm_noreply
);
860 if ((i
= min (buflen
, rc
)) > 0) {
864 i
= strlen(sm_moreply
);
865 if (more
&& (int) rc
> i
+ 1) {
866 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
873 if (sm_reply
.code
< 100) {
881 sm_reply
.length
= rp
- sm_reply
.text
;
882 sm_reply
.text
[sm_reply
.length
] = 0;
883 return sm_reply
.code
;
895 static char buffer
[BUFSIZ
];
897 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
917 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
937 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
938 text
, sm_reply
.code
, sm_reply
.text
);
950 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
952 if (has_prefix(ep
, s
)) {
953 for (ep
+= len
; *ep
== ' '; ep
++)
963 * Our SASL callback; we are either given SASL tokens to generate network
964 * protocols messages for, or we decode incoming protocol messages and
965 * convert them to binary SASL tokens to pass up into the SASL library.
969 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
970 unsigned int indatalen
, unsigned char **outdata
,
971 unsigned int *outdatalen
, char **errstr
)
978 case NETSEC_SASL_START
:
980 * Generate an AUTH message; if we were given an input token
981 * then generate a an AUTH message that includes the initial
985 mech
= netsec_get_sasl_mechanism(nsc
);
990 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
991 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
993 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
995 snoopoffset
= 6 + strlen(mech
);
996 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
998 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1000 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1006 if (netsec_flush(nsc
, errstr
) != OK
)
1011 case NETSEC_SASL_READ
:
1013 * Read in a line that should contain a 334 response code, followed
1014 * by base64 response data.
1017 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1019 line
= netsec_readline(nsc
, &len
, errstr
);
1020 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1026 netsec_err(errstr
, "Invalid format for SASL response");
1030 if (!has_prefix(line
, "334 ")) {
1031 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1039 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1041 netsec_err(errstr
, "Unable to decode base64 response");
1048 case NETSEC_SASL_WRITE
:
1050 * The output encoding is pretty simple, so this is easy.
1052 if (indatalen
== 0) {
1053 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1055 unsigned char *b64data
;
1056 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1057 writeBase64raw(indata
, indatalen
, b64data
);
1058 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1059 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1060 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1067 if (netsec_flush(nsc
, errstr
) != OK
)
1071 case NETSEC_SASL_FINISH
:
1073 * Finish the protocol; we're looking for a 235 message.
1075 line
= netsec_readline(nsc
, &len
, errstr
);
1079 if (!has_prefix(line
, "235 ")) {
1081 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1083 netsec_err(errstr
, "Authentication failed: %s", line
);
1088 case NETSEC_SASL_CANCEL
:
1090 * Cancel the SASL exchange; this is done by sending a single "*".
1092 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1094 rc
= netsec_flush(nsc
, errstr
);