]>
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
)
446 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>", from
)) {
463 sm_wadr (char *mbox
, char *host
, char *path
)
465 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
467 path
? path
: "", mbox
, host
)) {
477 #endif /* SENDMAILBUG */
502 switch (smtalk (SM_DATA
, "DATA")) {
511 #endif /* SENDMAILBUG */
528 sm_wtxt (char *buffer
, int len
)
532 result
= sm_wstream (buffer
, len
);
534 return (result
== NOTOK
? RP_BHST
: RP_OK
);
541 if (sm_wstream ((char *) NULL
, 0) == NOTOK
)
544 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
552 #endif /* SENDMAILBUG */
570 if (sm_mts
== MTS_SENDMAIL_SMTP
) {
586 smtalk (SM_QUIT
, "QUIT");
590 sm_note
.code
= sm_reply
.code
;
591 sm_note
.length
= sm_reply
.length
;
592 memcpy (sm_note
.text
, sm_reply
.text
, sm_reply
.length
+ 1);/* fall */
594 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
596 if (sm_mts
== MTS_SMTP
)
597 smtalk (SM_QUIT
, "QUIT");
599 /* The SIGPIPE block replaces old calls to discard ().
600 We're not sure what the discard () calls were for,
601 maybe to prevent deadlock on old systems. In any
602 case, blocking SIGPIPE should be harmless.
603 Because the file handles are closed below, leave it
607 sigaddset (&set
, SIGPIPE
);
608 sigprocmask (SIG_BLOCK
, &set
, &oset
);
610 kill (sm_child
, SIGKILL
);
614 sm_reply
.code
= sm_note
.code
;
615 sm_reply
.length
= sm_note
.length
;
616 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
622 netsec_shutdown(nsc
, 1);
626 if (sm_mts
== MTS_SMTP
) {
628 } else if (sm_child
!= NOTOK
) {
629 status
= pidwait (sm_child
, OK
);
635 return (status
? RP_BHST
: RP_OK
);
640 sm_ierror (const char *fmt
, ...)
645 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
648 sm_reply
.length
= strlen (sm_reply
.text
);
649 sm_reply
.code
= NOTOK
;
655 * Like sm_ierror, but assume it's an allocated error string we need to free.
659 sm_nerror (char *str
)
661 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
662 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
663 sm_reply
.length
= strlen(sm_reply
.text
);
664 sm_reply
.code
= NOTOK
;
671 smtalk (int time
, char *fmt
, ...)
678 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
682 return sm_nerror(errstr
);
684 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
685 return sm_nerror(errstr
);
687 if (netsec_flush (nsc
, &errstr
) != OK
)
688 return sm_nerror(errstr
);
690 netsec_set_timeout(nsc
, time
);
697 sm_wstream (char *buffer
, int len
)
700 static char lc
= '\0';
704 sm_ierror("No socket opened");
708 if (buffer
== NULL
&& len
== 0) {
711 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
718 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
722 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
730 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
738 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
753 int i
, code
, cont
, more
;
758 char **ehlo
= EHLOkeys
, *buffer
;
761 static int at_least_once
= 0;
766 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
781 sm_reply
.text
[0] = 0;
783 rc
= sizeof(sm_reply
.text
) - 1;
785 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
786 &errstr
)) != NULL
; ) {
789 && strncmp (buffer
, "250", sizeof("250") - 1) == 0
790 && (buffer
[3] == '-' || doingEHLO
== 2)
792 if (doingEHLO
== 2) {
793 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
794 strcpy (*ehlo
++, buffer
+ 4);
796 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
806 bp
= (unsigned char *) buffer
;
808 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
812 code
= atoi ((char *) bp
);
813 bp
+= 3, buflen
-= 3;
814 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
816 if (buflen
> 0 && *bp
== '-') {
819 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
824 if (code
!= sm_reply
.code
|| cont
)
828 sm_reply
.code
= code
;
831 bp
= (unsigned char *) sm_noreply
;
832 buflen
= strlen (sm_noreply
);
836 if ((i
= min (buflen
, rc
)) > 0) {
840 i
= strlen(sm_moreply
);
841 if (more
&& (int) rc
> i
+ 1) {
842 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
849 if (sm_reply
.code
< 100) {
851 printf ("%s\n", sm_reply
.text
);
857 sm_reply
.length
= rp
- sm_reply
.text
;
858 sm_reply
.text
[sm_reply
.length
] = 0;
859 return sm_reply
.code
;
871 static char buffer
[BUFSIZ
];
873 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
893 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
913 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
914 text
, sm_reply
.code
, sm_reply
.text
);
926 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
928 if (strncmp (ep
, s
, len
) == 0) {
929 for (ep
+= len
; *ep
== ' '; ep
++)
939 * Our SASL callback; we are either given SASL tokens to generate network
940 * protocols messages for, or we decode incoming protocol messages and
941 * convert them to binary SASL tokens to pass up into the SASL library.
945 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
946 unsigned int indatalen
, unsigned char **outdata
,
947 unsigned int *outdatalen
, char **errstr
)
954 case NETSEC_SASL_START
:
956 * Generate an AUTH message; if we were given an input token
957 * then generate a an AUTH message that includes the initial
961 mech
= netsec_get_sasl_mechanism(nsc
);
966 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
967 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
969 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
971 snoopoffset
= 6 + strlen(mech
);
972 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
974 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
976 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
982 if (netsec_flush(nsc
, errstr
) != OK
)
987 case NETSEC_SASL_READ
:
989 * Read in a line that should contain a 334 response code, followed
990 * by base64 response data.
993 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
995 line
= netsec_readline(nsc
, &len
, errstr
);
996 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1002 netsec_err(errstr
, "Invalid format for SASL response");
1006 if (strncmp(line
, "334 ", 4) != 0) {
1007 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1015 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1017 netsec_err(errstr
, "Unable to decode base64 response");
1024 case NETSEC_SASL_WRITE
:
1026 * The output encoding is pretty simple, so this is easy.
1028 if (indatalen
== 0) {
1029 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1031 unsigned char *b64data
;
1032 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1033 writeBase64raw(indata
, indatalen
, b64data
);
1034 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1035 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1036 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1043 if (netsec_flush(nsc
, errstr
) != OK
)
1047 case NETSEC_SASL_FINISH
:
1049 * Finish the protocol; we're looking for a 235 message.
1051 line
= netsec_readline(nsc
, &len
, errstr
);
1055 if (strncmp(line
, "235 ", 4) != 0) {
1057 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1059 netsec_err(errstr
, "Authentication failed: %s", line
);
1064 case NETSEC_SASL_CANCEL
:
1066 * Cancel the SASL exchange; this is done by sending a single "*".
1068 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1070 rc
= netsec_flush(nsc
, errstr
);