]>
diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
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
25 * On older 4.2BSD machines without the POSIX function `sigaction',
26 * the alarm handing stuff for time-outs will NOT work due to the way
27 * syscalls get restarted. This is not really crucial, since SendMail
28 * is generally well-behaved in this area.
33 * It appears that some versions of Sendmail will return Code 451
34 * when they don't really want to indicate a failure.
35 * "Code 451 almost always means sendmail has deferred; we don't
36 * really want bomb out at this point since sendmail will rectify
37 * things later." So, if you define SENDMAILBUG, Code 451 is
38 * considered the same as Code 250. Yuck!
42 #define NBITS ((sizeof (int)) * 8)
44 /* Timeout in seconds for SMTP commands.
45 * Lore has it they must be distinct. */
46 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
49 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
50 #define SM_RCPT 302 /* see above */
51 #define SM_DATA 120 /* see above */
52 #define SM_DOT 600 /* see above */
55 static int sm_addrs
= 0;
56 static int sm_child
= NOTOK
;
57 static int sm_debug
= 0;
58 static int sm_nl
= TRUE
;
59 static int sm_verbose
= 0;
60 static netsec_context
*nsc
= NULL
;
62 static char *sm_noreply
= "No reply text given";
63 static char *sm_moreply
= "; ";
64 static struct smtp sm_reply
;
69 static char *EHLOkeys
[MAXEHLO
+ 1];
74 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
75 const char *, const char *, int);
76 static int sendmail_init (char *, int, int, int, int, const char *,
79 static int rclient (char *, char *, char **);
80 static int sm_ierror (const char *fmt
, ...);
81 static int sm_nerror (char *);
82 static int smtalk (int time
, char *fmt
, ...);
83 static int sm_wstream (char *, int);
84 static int smhear (void);
85 static char *EHLOset (char *);
86 static int sm_sasl_callback(enum sasl_message_type
, unsigned const char *,
87 unsigned int, unsigned char **, unsigned int *,
91 sm_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
92 int debug
, int sasl
, const char *saslmech
, const char *user
,
93 const char *oauth_svc
, int tls
)
95 if (sm_mts
== MTS_SMTP
)
96 return smtp_init (client
, server
, port
, watch
, verbose
,
97 debug
, sasl
, saslmech
, user
, oauth_svc
, tls
);
99 return sendmail_init (client
, watch
, verbose
, debug
, sasl
,
104 smtp_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
105 int debug
, int sasl
, const char *saslmech
, const char *user
,
106 const char *oauth_svc
, int tls
)
109 char *errstr
, *chosen_server
;
114 sm_verbose
= verbose
;
120 if (client
== NULL
|| *client
== '\0') {
124 client
= LocalName(1); /* no clientname -> LocalName */
128 * Last-ditch check just in case client still isn't set to anything
130 if (client
== NULL
|| *client
== '\0')
131 client
= "localhost";
137 netsec_set_userid(nsc
, user
);
139 if ((sd1
= rclient (server
, port
, &chosen_server
)) == NOTOK
)
142 SIGNAL (SIGPIPE
, SIG_IGN
);
144 netsec_set_fd(nsc
, sd1
, sd1
);
146 netsec_set_hostname(nsc
, chosen_server
);
149 netsec_set_snoop(nsc
, 1);
152 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
154 return sm_nerror(errstr
);
158 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
159 return sm_ierror("OAuth2 not supported");
162 if (tls
& S_TLSENABLEMASK
) {
163 if (netsec_set_tls(nsc
, 1, tls
& S_NOVERIFY
, &errstr
) != OK
)
164 return sm_nerror(errstr
);
168 * If tls == S_INITTLS, that means that the user requested
169 * "initial" TLS, which happens right after the connection has
170 * opened. Do that negotiation now
173 if (tls
& S_INITTLS
) {
174 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
176 return sm_nerror(errstr
);
180 netsec_set_timeout(nsc
, SM_OPEN
);
193 * Give EHLO or HELO command
197 result
= smtalk (SM_HELO
, "EHLO %s", client
);
200 if (result
>= 500 && result
<= 599)
201 result
= smtalk (SM_HELO
, "HELO %s", client
);
209 * If the user requested TLS support, then try to do the STARTTLS command
210 * as part of the initial dialog. Assuming this works, we then need to
211 * restart the EHLO dialog after TLS negotiation is complete.
214 if (tls
& S_STARTTLS
) {
215 if (! EHLOset("STARTTLS")) {
217 return sm_ierror("SMTP server does not support TLS");
220 result
= smtalk(SM_HELO
, "STARTTLS");
228 * Okay, the other side should be waiting for us to start TLS
229 * negotiation. Oblige them.
232 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
234 return sm_nerror(errstr
);
238 result
= smtalk (SM_HELO
, "EHLO %s", client
);
248 * If the user asked for SASL, then check to see if the SMTP server
249 * supports it. Otherwise, error out (because the SMTP server
250 * might have been spoofed; we don't want to just silently not
256 if (! (server_mechs
= EHLOset("AUTH"))) {
258 return sm_ierror("SMTP server does not support SASL");
261 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
263 return sm_nerror(errstr
);
268 if (watch
&& EHLOset ("XVRB"))
269 smtalk (SM_HELO
, "VERB on");
275 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
276 const char *saslmech
, const char *user
)
278 unsigned int i
, result
, vecp
;
280 char *vec
[15], *errstr
;
285 sm_verbose
= verbose
;
290 if (client
== NULL
|| *client
== '\0') {
294 client
= LocalName(1); /* no clientname -> LocalName */
297 * Last-ditch check just in case client still isn't set to anything
299 if (client
== NULL
|| *client
== '\0')
300 client
= "localhost";
306 netsec_set_userid(nsc
, user
);
308 netsec_set_hostname(nsc
, client
);
311 netsec_set_snoop(nsc
, 1);
314 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
316 return sm_nerror(errstr
);
319 if (pipe (pdi
) == NOTOK
)
320 return sm_ierror ("no pipes");
321 if (pipe (pdo
) == NOTOK
) {
324 return sm_ierror ("no pipes");
327 for (i
= 0; (sm_child
= fork ()) == NOTOK
&& i
< 5; i
++)
336 return sm_ierror ("unable to fork");
339 if (pdo
[0] != fileno (stdin
))
340 dup2 (pdo
[0], fileno (stdin
));
341 if (pdi
[1] != fileno (stdout
))
342 dup2 (pdi
[1], fileno (stdout
));
343 if (pdi
[1] != fileno (stderr
))
344 dup2 (pdi
[1], fileno (stderr
));
345 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
349 vec
[vecp
++] = r1bindex (sendmail
, '/');
351 vec
[vecp
++] = watch
? "-odi" : "-odb";
352 vec
[vecp
++] = "-oem";
358 execvp (sendmail
, vec
);
359 fprintf (stderr
, "unable to exec ");
361 _exit (-1); /* NOTREACHED */
364 SIGNAL (SIGPIPE
, SIG_IGN
);
369 netsec_set_fd(nsc
, pdi
[i
], pdo
[1]);
370 netsec_set_timeout(nsc
, SM_OPEN
);
382 result
= smtalk (SM_HELO
, "EHLO %s", client
);
385 if (500 <= result
&& result
<= 599)
386 result
= smtalk (SM_HELO
, "HELO %s", client
);
398 * If the user asked for SASL, then check to see if the SMTP server
399 * supports it. Otherwise, error out (because the SMTP server
400 * might have been spoofed; we don't want to just silently not
406 if (! (server_mechs
= EHLOset("AUTH"))) {
408 return sm_ierror("SMTP server does not support SASL");
410 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
412 return sm_nerror(errstr
);
417 smtalk (SM_HELO
, "VERB on");
424 rclient (char *server
, char *service
, char **chosen_server
)
427 char response
[BUFSIZ
];
432 *chosen_server
= server
;
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 inform("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 inform("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);
624 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
626 if (sm_mts
== MTS_SMTP
)
627 smtalk (SM_QUIT
, "QUIT");
629 /* The SIGPIPE block replaces old calls to discard ().
630 We're not sure what the discard () calls were for,
631 maybe to prevent deadlock on old systems. In any
632 case, blocking SIGPIPE should be harmless.
633 Because the file handles are closed below, leave it
637 sigaddset (&set
, SIGPIPE
);
638 sigprocmask (SIG_BLOCK
, &set
, &oset
);
640 kill (sm_child
, SIGKILL
);
644 sm_reply
.code
= sm_note
.code
;
645 sm_reply
.length
= sm_note
.length
;
646 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
652 netsec_shutdown(nsc
, 1);
656 if (sm_mts
== MTS_SMTP
) {
658 } else if (sm_child
!= NOTOK
) {
659 status
= pidwait (sm_child
, OK
);
665 return (status
? RP_BHST
: RP_OK
);
670 sm_ierror (const char *fmt
, ...)
675 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
678 sm_reply
.length
= strlen (sm_reply
.text
);
679 sm_reply
.code
= NOTOK
;
685 * Like sm_ierror, but assume it's an allocated error string we need to free.
689 sm_nerror (char *str
)
691 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
692 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
693 sm_reply
.length
= strlen(sm_reply
.text
);
694 sm_reply
.code
= NOTOK
;
701 smtalk (int time
, char *fmt
, ...)
708 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
712 return sm_nerror(errstr
);
714 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
715 return sm_nerror(errstr
);
717 if (netsec_flush (nsc
, &errstr
) != OK
)
718 return sm_nerror(errstr
);
720 netsec_set_timeout(nsc
, time
);
727 sm_wstream (char *buffer
, int len
)
730 static char lc
= '\0';
734 sm_ierror("No socket opened");
738 if (buffer
== NULL
&& len
== 0) {
741 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
748 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
752 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
760 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
769 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
784 int i
, code
, cont
, more
;
789 char **ehlo
= EHLOkeys
, *buffer
;
792 static int at_least_once
= 0;
797 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
812 sm_reply
.text
[0] = 0;
814 rc
= sizeof(sm_reply
.text
) - 1;
816 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
817 &errstr
)) != NULL
; ) {
820 && has_prefix(buffer
, "250")
821 && (buffer
[3] == '-' || doingEHLO
== 2)
823 if (doingEHLO
== 2) {
824 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
825 strcpy (*ehlo
++, buffer
+ 4);
827 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
837 bp
= (unsigned char *) buffer
;
839 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
843 code
= atoi ((char *) bp
);
844 bp
+= 3, buflen
-= 3;
845 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
847 if (buflen
> 0 && *bp
== '-') {
850 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
855 if (code
!= sm_reply
.code
|| cont
)
859 sm_reply
.code
= code
;
862 bp
= (unsigned char *) sm_noreply
;
863 buflen
= strlen (sm_noreply
);
867 if ((i
= min (buflen
, rc
)) > 0) {
871 i
= strlen(sm_moreply
);
872 if (more
&& (int) rc
> i
+ 1) {
873 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
880 if (sm_reply
.code
< 100) {
888 sm_reply
.length
= rp
- sm_reply
.text
;
889 sm_reply
.text
[sm_reply
.length
] = 0;
890 return sm_reply
.code
;
902 static char buffer
[BUFSIZ
];
904 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
924 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
944 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
945 text
, sm_reply
.code
, sm_reply
.text
);
957 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
959 if (has_prefix(ep
, s
)) {
960 for (ep
+= len
; *ep
== ' '; ep
++)
970 * Our SASL callback; we are either given SASL tokens to generate network
971 * protocols messages for, or we decode incoming protocol messages and
972 * convert them to binary SASL tokens to pass up into the SASL library.
976 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
977 unsigned int indatalen
, unsigned char **outdata
,
978 unsigned int *outdatalen
, char **errstr
)
985 case NETSEC_SASL_START
:
987 * Generate an AUTH message; if we were given an input token
988 * then generate a an AUTH message that includes the initial
992 mech
= netsec_get_sasl_mechanism(nsc
);
997 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
998 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
1000 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
1002 snoopoffset
= 6 + strlen(mech
);
1003 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
1005 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1007 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
1013 if (netsec_flush(nsc
, errstr
) != OK
)
1018 case NETSEC_SASL_READ
:
1020 * Read in a line that should contain a 334 response code, followed
1021 * by base64 response data.
1024 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1026 line
= netsec_readline(nsc
, &len
, errstr
);
1027 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1033 netsec_err(errstr
, "Invalid format for SASL response");
1037 if (!has_prefix(line
, "334 ")) {
1038 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1046 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1048 netsec_err(errstr
, "Unable to decode base64 response");
1055 case NETSEC_SASL_WRITE
:
1057 * The output encoding is pretty simple, so this is easy.
1059 if (indatalen
== 0) {
1060 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1062 unsigned char *b64data
;
1063 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1064 writeBase64raw(indata
, indatalen
, b64data
);
1065 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1066 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1067 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1074 if (netsec_flush(nsc
, errstr
) != OK
)
1078 case NETSEC_SASL_FINISH
:
1080 * Finish the protocol; we're looking for a 235 message.
1082 line
= netsec_readline(nsc
, &len
, errstr
);
1086 if (!has_prefix(line
, "235 ")) {
1088 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1090 netsec_err(errstr
, "Authentication failed: %s", line
);
1095 case NETSEC_SASL_CANCEL
:
1097 * Cancel the SASL exchange; this is done by sending a single "*".
1099 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1101 rc
= netsec_flush(nsc
, errstr
);