]>
diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
80bff194aa01d7c0a41876d77afa4aeddb67cea1
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 */
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 netsec_set_hostname(nsc
, server
);
147 netsec_set_snoop(nsc
, 1);
150 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
152 return sm_nerror(errstr
);
156 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
157 return sm_ierror("OAuth2 not supported");
160 if ((sd1
= rclient (server
, port
)) == NOTOK
)
163 SIGNAL (SIGPIPE
, SIG_IGN
);
165 netsec_set_fd(nsc
, sd1
, sd1
);
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
)
432 char response
[BUFSIZ
];
434 if ((sd
= client (server
, service
, response
, sizeof(response
),
438 sm_ierror ("%s", response
);
443 sm_winit (char *from
, int smtputf8
, int eightbit
)
445 const char *mail_parameters
= "";
448 /* Just for information, if an attempt is made to send to an 8-bit
449 address without specifying SMTPUTF8, Gmail responds with
450 555 5.5.2 Syntax error.
451 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
452 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
453 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
455 advise (NULL
, "SMTP server does not support %s, not sending.\n"
456 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
457 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
461 } else if (eightbit
) {
462 /* Comply with RFC 6152, for messages that have any 8-bit characters
464 if (EHLOset ("8BITMIME")) {
465 mail_parameters
= " BODY=8BITMIME";
467 advise (NULL
, "SMTP server does not support 8BITMIME, not sending.\n"
468 "Suggest encoding message for 7-bit transport by setting your\n"
469 "locale to C, and/or specifying *b64 in mhbuild directives.");
475 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
492 sm_wadr (char *mbox
, char *host
, char *path
)
494 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
496 path
? path
: "", mbox
, host
)) {
506 #endif /* SENDMAILBUG */
531 switch (smtalk (SM_DATA
, "DATA")) {
540 #endif /* SENDMAILBUG */
557 sm_wtxt (char *buffer
, int len
)
561 result
= sm_wstream (buffer
, len
);
563 return (result
== NOTOK
? RP_BHST
: RP_OK
);
570 if (sm_wstream ((char *) NULL
, 0) == NOTOK
)
573 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
581 #endif /* SENDMAILBUG */
599 if (sm_mts
== MTS_SENDMAIL_SMTP
) {
615 smtalk (SM_QUIT
, "QUIT");
619 sm_note
.code
= sm_reply
.code
;
620 sm_note
.length
= sm_reply
.length
;
621 memcpy (sm_note
.text
, sm_reply
.text
, sm_reply
.length
+ 1);/* fall */
623 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
625 if (sm_mts
== MTS_SMTP
)
626 smtalk (SM_QUIT
, "QUIT");
628 /* The SIGPIPE block replaces old calls to discard ().
629 We're not sure what the discard () calls were for,
630 maybe to prevent deadlock on old systems. In any
631 case, blocking SIGPIPE should be harmless.
632 Because the file handles are closed below, leave it
636 sigaddset (&set
, SIGPIPE
);
637 sigprocmask (SIG_BLOCK
, &set
, &oset
);
639 kill (sm_child
, SIGKILL
);
643 sm_reply
.code
= sm_note
.code
;
644 sm_reply
.length
= sm_note
.length
;
645 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
651 netsec_shutdown(nsc
, 1);
655 if (sm_mts
== MTS_SMTP
) {
657 } else if (sm_child
!= NOTOK
) {
658 status
= pidwait (sm_child
, OK
);
664 return (status
? RP_BHST
: RP_OK
);
669 sm_ierror (const char *fmt
, ...)
674 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
677 sm_reply
.length
= strlen (sm_reply
.text
);
678 sm_reply
.code
= NOTOK
;
684 * Like sm_ierror, but assume it's an allocated error string we need to free.
688 sm_nerror (char *str
)
690 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
691 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
692 sm_reply
.length
= strlen(sm_reply
.text
);
693 sm_reply
.code
= NOTOK
;
700 smtalk (int time
, char *fmt
, ...)
707 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
711 return sm_nerror(errstr
);
713 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
714 return sm_nerror(errstr
);
716 if (netsec_flush (nsc
, &errstr
) != OK
)
717 return sm_nerror(errstr
);
719 netsec_set_timeout(nsc
, time
);
726 sm_wstream (char *buffer
, int len
)
729 static char lc
= '\0';
733 sm_ierror("No socket opened");
737 if (buffer
== NULL
&& len
== 0) {
740 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
747 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
751 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
759 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
767 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
782 int i
, code
, cont
, more
;
787 char **ehlo
= EHLOkeys
, *buffer
;
790 static int at_least_once
= 0;
795 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
810 sm_reply
.text
[0] = 0;
812 rc
= sizeof(sm_reply
.text
) - 1;
814 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
815 &errstr
)) != NULL
; ) {
818 && HasPrefix(buffer
, "250")
819 && (buffer
[3] == '-' || doingEHLO
== 2)
821 if (doingEHLO
== 2) {
822 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
823 strcpy (*ehlo
++, buffer
+ 4);
825 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
835 bp
= (unsigned char *) buffer
;
837 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
841 code
= atoi ((char *) bp
);
842 bp
+= 3, buflen
-= 3;
843 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
845 if (buflen
> 0 && *bp
== '-') {
848 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
853 if (code
!= sm_reply
.code
|| cont
)
857 sm_reply
.code
= code
;
860 bp
= (unsigned char *) sm_noreply
;
861 buflen
= strlen (sm_noreply
);
865 if ((i
= min (buflen
, rc
)) > 0) {
869 i
= strlen(sm_moreply
);
870 if (more
&& (int) rc
> i
+ 1) {
871 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
878 if (sm_reply
.code
< 100) {
886 sm_reply
.length
= rp
- sm_reply
.text
;
887 sm_reply
.text
[sm_reply
.length
] = 0;
888 return sm_reply
.code
;
900 static char buffer
[BUFSIZ
];
902 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
922 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
942 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
943 text
, sm_reply
.code
, sm_reply
.text
);
955 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
957 if (HasPrefix(ep
, s
)) {
958 for (ep
+= len
; *ep
== ' '; ep
++)
968 * Our SASL callback; we are either given SASL tokens to generate network
969 * protocols messages for, or we decode incoming protocol messages and
970 * convert them to binary SASL tokens to pass up into the SASL library.
974 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
975 unsigned int indatalen
, unsigned char **outdata
,
976 unsigned int *outdatalen
, char **errstr
)
983 case NETSEC_SASL_START
:
985 * Generate an AUTH message; if we were given an input token
986 * then generate a an AUTH message that includes the initial
990 mech
= netsec_get_sasl_mechanism(nsc
);
995 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
996 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
998 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
1000 snoopoffset
= 6 + strlen(mech
);
1001 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
1003 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1005 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1011 if (netsec_flush(nsc
, errstr
) != OK
)
1016 case NETSEC_SASL_READ
:
1018 * Read in a line that should contain a 334 response code, followed
1019 * by base64 response data.
1022 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1024 line
= netsec_readline(nsc
, &len
, errstr
);
1025 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1031 netsec_err(errstr
, "Invalid format for SASL response");
1035 if (!HasPrefix(line
, "334 ")) {
1036 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1044 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1046 netsec_err(errstr
, "Unable to decode base64 response");
1053 case NETSEC_SASL_WRITE
:
1055 * The output encoding is pretty simple, so this is easy.
1057 if (indatalen
== 0) {
1058 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1060 unsigned char *b64data
;
1061 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1062 writeBase64raw(indata
, indatalen
, b64data
);
1063 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1064 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1065 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1072 if (netsec_flush(nsc
, errstr
) != OK
)
1076 case NETSEC_SASL_FINISH
:
1078 * Finish the protocol; we're looking for a 235 message.
1080 line
= netsec_readline(nsc
, &len
, errstr
);
1084 if (!HasPrefix(line
, "235 ")) {
1086 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1088 netsec_err(errstr
, "Authentication failed: %s", line
);
1093 case NETSEC_SASL_CANCEL
:
1095 * Cancel the SASL exchange; this is done by sending a single "*".
1097 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1099 rc
= netsec_flush(nsc
, errstr
);