]>
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!
46 #define NBITS ((sizeof (int)) * 8)
49 * these codes must all be different!
51 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
54 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
55 #define SM_RCPT 302 /* see above */
56 #define SM_DATA 120 /* see above */
57 #define SM_TEXT 180 /* see above */
58 #define SM_DOT 600 /* see above */
63 static int sm_addrs
= 0;
64 static int sm_child
= NOTOK
;
65 static int sm_debug
= 0;
66 static int sm_nl
= TRUE
;
67 static int sm_verbose
= 0;
68 static netsec_context
*nsc
= NULL
;
70 static char *sm_noreply
= "No reply text given";
71 static char *sm_moreply
= "; ";
72 static struct smtp sm_reply
;
77 static char *EHLOkeys
[MAXEHLO
+ 1];
82 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
83 const char *, const char *, int);
84 static int sendmail_init (char *, int, int, int, int, const char *,
87 static int rclient (char *, char *);
88 static int sm_ierror (const char *fmt
, ...);
89 static int sm_nerror (char *);
90 static int smtalk (int time
, char *fmt
, ...);
91 static int sm_wstream (char *, int);
92 static int smhear (void);
93 static char *EHLOset (char *);
94 static int sm_sasl_callback(enum sasl_message_type
, unsigned const char *,
95 unsigned int, unsigned char **, unsigned int *,
99 sm_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
100 int debug
, int sasl
, const char *saslmech
, const char *user
,
101 const char *oauth_svc
, int tls
)
103 if (sm_mts
== MTS_SMTP
)
104 return smtp_init (client
, server
, port
, watch
, verbose
,
105 debug
, sasl
, saslmech
, user
, oauth_svc
, tls
);
107 return sendmail_init (client
, watch
, verbose
, debug
, sasl
,
112 smtp_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
113 int debug
, int sasl
, const char *saslmech
, const char *user
,
114 const char *oauth_svc
, int tls
)
122 sm_verbose
= verbose
;
128 if (client
== NULL
|| *client
== '\0') {
132 client
= LocalName(1); /* no clientname -> LocalName */
137 * Last-ditch check just in case client still isn't set to anything
140 if (client
== NULL
|| *client
== '\0')
141 client
= "localhost";
146 netsec_set_userid(nsc
, user
);
149 netsec_set_snoop(nsc
, 1);
152 if (netsec_set_sasl_params(nsc
, server
, "smtp", saslmech
,
153 sm_sasl_callback
, &errstr
) != OK
)
154 return sm_nerror(errstr
);
158 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
159 return sm_ierror("OAuth2 not supported");
162 if ((sd1
= rclient (server
, port
)) == NOTOK
)
165 SIGNAL (SIGPIPE
, SIG_IGN
);
167 netsec_set_fd(nsc
, sd1
, sd1
);
170 if (netsec_set_tls(nsc
, 1, &errstr
) != OK
)
171 return sm_nerror(errstr
);
175 * If tls == 2, that means that the user requested "initial" TLS,
176 * which happens right after the connection has opened. Do that
181 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
183 return sm_nerror(errstr
);
187 netsec_set_timeout(nsc
, SM_OPEN
);
200 * Give EHLO or HELO command
204 result
= smtalk (SM_HELO
, "EHLO %s", client
);
207 if (result
>= 500 && result
<= 599)
208 result
= smtalk (SM_HELO
, "HELO %s", client
);
216 * If the user requested TLS support, then try to do the STARTTLS command
217 * as part of the initial dialog. Assuming this works, we then need to
218 * restart the EHLO dialog after TLS negotiation is complete.
222 if (! EHLOset("STARTTLS")) {
224 return sm_ierror("SMTP server does not support TLS");
227 result
= smtalk(SM_HELO
, "STARTTLS");
235 * Okay, the other side should be waiting for us to start TLS
236 * negotiation. Oblige them.
239 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
241 return sm_nerror(errstr
);
245 result
= smtalk (SM_HELO
, "EHLO %s", client
);
255 * If the user asked for SASL, then check to see if the SMTP server
256 * supports it. Otherwise, error out (because the SMTP server
257 * might have been spoofed; we don't want to just silently not
263 if (! (server_mechs
= EHLOset("AUTH"))) {
265 return sm_ierror("SMTP server does not support SASL");
268 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
270 return sm_nerror(errstr
);
275 if (watch
&& EHLOset ("XVRB"))
276 smtalk (SM_HELO
, "VERB on");
282 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
283 const char *saslmech
, const char *user
)
285 unsigned int i
, result
, vecp
;
287 char *vec
[15], *errstr
;
292 sm_verbose
= verbose
;
297 if (client
== NULL
|| *client
== '\0') {
301 client
= LocalName(1); /* no clientname -> LocalName */
305 * Last-ditch check just in case client still isn't set to anything
308 if (client
== NULL
|| *client
== '\0')
309 client
= "localhost";
314 netsec_set_userid(nsc
, user
);
317 netsec_set_snoop(nsc
, 1);
320 if (netsec_set_sasl_params(nsc
, client
, "smtp", saslmech
,
321 sm_sasl_callback
, &errstr
) != OK
)
322 return sm_nerror(errstr
);
325 if (pipe (pdi
) == NOTOK
)
326 return sm_ierror ("no pipes");
327 if (pipe (pdo
) == NOTOK
) {
330 return sm_ierror ("no pipes");
333 for (i
= 0; (sm_child
= fork ()) == NOTOK
&& i
< 5; i
++)
342 return sm_ierror ("unable to fork");
345 if (pdo
[0] != fileno (stdin
))
346 dup2 (pdo
[0], fileno (stdin
));
347 if (pdi
[1] != fileno (stdout
))
348 dup2 (pdi
[1], fileno (stdout
));
349 if (pdi
[1] != fileno (stderr
))
350 dup2 (pdi
[1], fileno (stderr
));
351 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
355 vec
[vecp
++] = r1bindex (sendmail
, '/');
357 vec
[vecp
++] = watch
? "-odi" : "-odb";
358 vec
[vecp
++] = "-oem";
364 execvp (sendmail
, vec
);
365 fprintf (stderr
, "unable to exec ");
367 _exit (-1); /* NOTREACHED */
370 SIGNAL (SIGPIPE
, SIG_IGN
);
375 netsec_set_fd(nsc
, pdi
[i
], pdo
[1]);
376 netsec_set_timeout(nsc
, SM_OPEN
);
388 result
= smtalk (SM_HELO
, "EHLO %s", client
);
391 if (500 <= result
&& result
<= 599)
392 result
= smtalk (SM_HELO
, "HELO %s", client
);
404 * If the user asked for SASL, then check to see if the SMTP server
405 * supports it. Otherwise, error out (because the SMTP server
406 * might have been spoofed; we don't want to just silently not
412 if (! (server_mechs
= EHLOset("AUTH"))) {
414 return sm_ierror("SMTP server does not support SASL");
416 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
418 return sm_nerror(errstr
);
423 smtalk (SM_HELO
, "VERB on");
430 rclient (char *server
, char *service
)
433 char response
[BUFSIZ
];
435 if ((sd
= client (server
, service
, response
, sizeof(response
),
439 sm_ierror ("%s", response
);
444 sm_winit (char *from
, int smtputf8
)
446 const char *const mail_parameters
= smtputf8
447 ? " BODY=8BITMIME SMTPUTF8"
450 /* Just for information, if an attempt is made to send to an 8-bit
451 address without specifying SMTPUTF8, Gmail responds with
452 555 5.5.2 Syntax error.
453 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
454 if (smtputf8
&& (! EHLOset("8BITMIME") || ! EHLOset("SMTPUTF8"))) {
459 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
476 sm_wadr (char *mbox
, char *host
, char *path
)
478 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
480 path
? path
: "", mbox
, host
)) {
490 #endif /* SENDMAILBUG */
515 switch (smtalk (SM_DATA
, "DATA")) {
524 #endif /* SENDMAILBUG */
541 sm_wtxt (char *buffer
, int len
)
545 result
= sm_wstream (buffer
, len
);
547 return (result
== NOTOK
? RP_BHST
: RP_OK
);
554 if (sm_wstream ((char *) NULL
, 0) == NOTOK
)
557 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
565 #endif /* SENDMAILBUG */
583 if (sm_mts
== MTS_SENDMAIL_SMTP
) {
599 smtalk (SM_QUIT
, "QUIT");
603 sm_note
.code
= sm_reply
.code
;
604 sm_note
.length
= sm_reply
.length
;
605 memcpy (sm_note
.text
, sm_reply
.text
, sm_reply
.length
+ 1);/* fall */
607 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
609 if (sm_mts
== MTS_SMTP
)
610 smtalk (SM_QUIT
, "QUIT");
612 /* The SIGPIPE block replaces old calls to discard ().
613 We're not sure what the discard () calls were for,
614 maybe to prevent deadlock on old systems. In any
615 case, blocking SIGPIPE should be harmless.
616 Because the file handles are closed below, leave it
620 sigaddset (&set
, SIGPIPE
);
621 sigprocmask (SIG_BLOCK
, &set
, &oset
);
623 kill (sm_child
, SIGKILL
);
627 sm_reply
.code
= sm_note
.code
;
628 sm_reply
.length
= sm_note
.length
;
629 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
635 netsec_shutdown(nsc
, 1);
639 if (sm_mts
== MTS_SMTP
) {
641 } else if (sm_child
!= NOTOK
) {
642 status
= pidwait (sm_child
, OK
);
648 return (status
? RP_BHST
: RP_OK
);
653 sm_ierror (const char *fmt
, ...)
658 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
661 sm_reply
.length
= strlen (sm_reply
.text
);
662 sm_reply
.code
= NOTOK
;
668 * Like sm_ierror, but assume it's an allocated error string we need to free.
672 sm_nerror (char *str
)
674 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
675 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
676 sm_reply
.length
= strlen(sm_reply
.text
);
677 sm_reply
.code
= NOTOK
;
684 smtalk (int time
, char *fmt
, ...)
691 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
695 return sm_nerror(errstr
);
697 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
698 return sm_nerror(errstr
);
700 if (netsec_flush (nsc
, &errstr
) != OK
)
701 return sm_nerror(errstr
);
703 netsec_set_timeout(nsc
, time
);
710 sm_wstream (char *buffer
, int len
)
713 static char lc
= '\0';
717 sm_ierror("No socket opened");
721 if (buffer
== NULL
&& len
== 0) {
724 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
731 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
735 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
743 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
751 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
766 int i
, code
, cont
, more
;
771 char **ehlo
= EHLOkeys
, *buffer
;
774 static int at_least_once
= 0;
779 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
794 sm_reply
.text
[0] = 0;
796 rc
= sizeof(sm_reply
.text
) - 1;
798 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
799 &errstr
)) != NULL
; ) {
802 && strncmp (buffer
, "250", sizeof("250") - 1) == 0
803 && (buffer
[3] == '-' || doingEHLO
== 2)
805 if (doingEHLO
== 2) {
806 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
807 strcpy (*ehlo
++, buffer
+ 4);
809 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
819 bp
= (unsigned char *) buffer
;
821 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
825 code
= atoi ((char *) bp
);
826 bp
+= 3, buflen
-= 3;
827 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
829 if (buflen
> 0 && *bp
== '-') {
832 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
837 if (code
!= sm_reply
.code
|| cont
)
841 sm_reply
.code
= code
;
844 bp
= (unsigned char *) sm_noreply
;
845 buflen
= strlen (sm_noreply
);
849 if ((i
= min (buflen
, rc
)) > 0) {
853 i
= strlen(sm_moreply
);
854 if (more
&& (int) rc
> i
+ 1) {
855 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
862 if (sm_reply
.code
< 100) {
864 printf ("%s\n", sm_reply
.text
);
870 sm_reply
.length
= rp
- sm_reply
.text
;
871 sm_reply
.text
[sm_reply
.length
] = 0;
872 return sm_reply
.code
;
884 static char buffer
[BUFSIZ
];
886 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
906 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
926 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
927 text
, sm_reply
.code
, sm_reply
.text
);
939 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
941 if (strncmp (ep
, s
, len
) == 0) {
942 for (ep
+= len
; *ep
== ' '; ep
++)
952 * Our SASL callback; we are either given SASL tokens to generate network
953 * protocols messages for, or we decode incoming protocol messages and
954 * convert them to binary SASL tokens to pass up into the SASL library.
958 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
959 unsigned int indatalen
, unsigned char **outdata
,
960 unsigned int *outdatalen
, char **errstr
)
967 case NETSEC_SASL_START
:
969 * Generate an AUTH message; if we were given an input token
970 * then generate a an AUTH message that includes the initial
974 mech
= netsec_get_sasl_mechanism(nsc
);
979 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
980 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
982 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
984 snoopoffset
= 6 + strlen(mech
);
985 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
987 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
989 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
995 if (netsec_flush(nsc
, errstr
) != OK
)
1000 case NETSEC_SASL_READ
:
1002 * Read in a line that should contain a 334 response code, followed
1003 * by base64 response data.
1006 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1008 line
= netsec_readline(nsc
, &len
, errstr
);
1009 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1015 netsec_err(errstr
, "Invalid format for SASL response");
1019 if (strncmp(line
, "334 ", 4) != 0) {
1020 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1028 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1030 netsec_err(errstr
, "Unable to decode base64 response");
1037 case NETSEC_SASL_WRITE
:
1039 * The output encoding is pretty simple, so this is easy.
1041 if (indatalen
== 0) {
1042 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1044 unsigned char *b64data
;
1045 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1046 writeBase64raw(indata
, indatalen
, b64data
);
1047 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1048 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1049 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1056 if (netsec_flush(nsc
, errstr
) != OK
)
1060 case NETSEC_SASL_FINISH
:
1062 * Finish the protocol; we're looking for a 235 message.
1064 line
= netsec_readline(nsc
, &len
, errstr
);
1068 if (strncmp(line
, "235 ", 4) != 0) {
1070 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1072 netsec_err(errstr
, "Authentication failed: %s", line
);
1077 case NETSEC_SASL_CANCEL
:
1079 * Cancel the SASL exchange; this is done by sending a single "*".
1081 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1083 rc
= netsec_flush(nsc
, errstr
);