]> Pileus Git - ~andy/fetchmail/blob - smtp.c
Document and add rawlog.patch to contrib, as debug tool.
[~andy/fetchmail] / smtp.c
1 /*
2  * smtp.c -- code for speaking SMTP to a listener port
3  *
4  * Concept due to Harry Hochheiser.  Implementation by ESR.  Cleanup and
5  * strict RFC821 compliance by Cameron MacPherson.
6  *
7  * Copyright 1997 Eric S. Raymond, 2009 Matthias Andree
8  * For license terms, see the file COPYING in this directory.
9  */
10
11 #include "config.h"
12 #include "fetchmail.h"
13
14 #include <ctype.h>
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <signal.h>
19 #include "socket.h"
20 #include "smtp.h"
21 #include "gettext.h"
22
23 struct opt
24 {
25     const char *name;
26     int value;
27 };
28
29 static struct opt extensions[] =
30 {
31     {"8BITMIME",        ESMTP_8BITMIME},
32     {"SIZE",            ESMTP_SIZE},
33     {"ETRN",            ESMTP_ETRN},
34     {"AUTH ",           ESMTP_AUTH},
35 #ifdef ODMR_ENABLE
36     {"ATRN",            ESMTP_ATRN},
37 #endif /* ODMR_ENABLE */
38     {(char *)NULL, 0},
39 };
40
41 char smtp_response[MSGBUFSIZE];
42
43 /* XXX: this must not be used for LMTP! */
44 int SMTP_helo(int sock, char smtp_mode, const char *host)
45 /* send a "HELO" message to the SMTP listener */
46 {
47   int ok;
48
49   SockPrintf(sock,"HELO %s\r\n", host);
50   if (outlevel >= O_MONITOR)
51       report(stdout, "%cMTP> HELO %s\n", smtp_mode, host);
52   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_HELO);
53   return ok;
54 }
55
56 static void SMTP_auth_error(int sock, const char *msg)
57 {
58     SockPrintf(sock, "*\r\n");
59     SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
60     if (outlevel >= O_MONITOR) report(stdout, "%s", msg);
61 }
62
63 static void SMTP_auth(int sock, char smtp_mode, char *username, char *password, char *buf)
64 /* ESMTP Authentication support for fetchmail by Wojciech Polak */
65 {       
66         int c;
67         char *p = 0;
68         char b64buf[512];
69         char tmp[512];
70
71         if (!username || !password) return;
72
73         memset(b64buf, 0, sizeof(b64buf));
74         memset(tmp, 0, sizeof(tmp));
75
76         if (strstr(buf, "CRAM-MD5")) {
77                 unsigned char digest[16];
78                 memset(digest, 0, sizeof(digest));
79
80                 if (outlevel >= O_MONITOR)
81                         report(stdout, GT_("ESMTP CRAM-MD5 Authentication...\n"));
82                 SockPrintf(sock, "AUTH CRAM-MD5\r\n");
83                 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
84                 strlcpy(tmp, smtp_response, sizeof(tmp));
85
86                 if (strncmp(tmp, "334", 3)) { /* Server rejects AUTH */
87                         SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
88                         return;
89                 }
90
91                 p = strchr(tmp, ' ');
92                 p++;
93                 /* (hmh) from64tobits will not NULL-terminate strings! */
94                 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
95                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
96                         return;
97                 }
98                 if (outlevel >= O_DEBUG)
99                         report(stdout, GT_("Challenge decoded: %s\n"), b64buf);
100                 hmac_md5((unsigned char *)password, strlen(password),
101                          (unsigned char *)b64buf, strlen(b64buf), digest, sizeof(digest));
102                 snprintf(tmp, sizeof(tmp),
103                 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
104                 username,  digest[0], digest[1], digest[2], digest[3],
105                 digest[4], digest[5], digest[6], digest[7], digest[8],
106                 digest[9], digest[10], digest[11], digest[12], digest[13],
107                 digest[14], digest[15]);
108
109                 to64frombits(b64buf, tmp, strlen(tmp));
110                 SockPrintf(sock, "%s\r\n", b64buf);
111                 SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
112         }
113         else if (strstr(buf, "PLAIN")) {
114                 int len;
115                 if (outlevel >= O_MONITOR)
116                         report(stdout, GT_("ESMTP PLAIN Authentication...\n"));
117                 snprintf(tmp, sizeof(tmp), "^%s^%s", username, password);
118
119                 len = strlen(tmp);
120                 for (c = len - 1; c >= 0; c--)
121                 {
122                         if (tmp[c] == '^')
123                                 tmp[c] = '\0';
124                 }
125                 to64frombits(b64buf, tmp, len);
126                 SockPrintf(sock, "AUTH PLAIN %s\r\n", b64buf);
127                 SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
128         }
129         else if (strstr(buf, "LOGIN")) {
130                 if (outlevel >= O_MONITOR)
131                         report(stdout, GT_("ESMTP LOGIN Authentication...\n"));
132                 SockPrintf(sock, "AUTH LOGIN\r\n");
133                 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
134                 strlcpy(tmp, smtp_response, sizeof(tmp));
135
136                 if (strncmp(tmp, "334", 3)) { /* Server rejects AUTH */
137                         SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
138                         return;
139                 }
140
141                 p = strchr(tmp, ' ');
142                 p++;
143                 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
144                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
145                         return;
146                 }
147                 to64frombits(b64buf, username, strlen(username));
148                 SockPrintf(sock, "%s\r\n", b64buf);
149                 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
150                 strlcpy(tmp, smtp_response, sizeof(tmp));
151                 p = strchr(tmp, ' ');
152                 if (!p) {
153                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
154                         return;
155                 }
156                 p++;
157                 memset(b64buf, 0, sizeof(b64buf));
158                 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
159                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
160                         return;
161                 }
162                 to64frombits(b64buf, password, strlen(password));
163                 SockPrintf(sock, "%s\r\n", b64buf);
164                 SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
165         }
166         return;
167 }
168
169 int SMTP_ehlo(int sock, char smtp_mode, const char *host, char *name, char *password, int *opt)
170 /* send a "EHLO" message to the SMTP listener, return extension status bits */
171 {
172   struct opt *hp;
173   char auth_response[511];
174   const int tmout = (mytimeout >= TIMEOUT_HELO ? mytimeout : TIMEOUT_HELO);
175
176   if (SockTimeout(sock, tmout) != 0)
177       return SM_UNRECOVERABLE;
178
179   SockPrintf(sock,"%cHLO %s\r\n", (smtp_mode == 'S') ? 'E' : smtp_mode, host);
180   if (outlevel >= O_MONITOR)
181       report(stdout, "%cMTP> %cHLO %s\n", 
182             smtp_mode, (smtp_mode == 'S') ? 'E' : smtp_mode, host);
183
184   *opt = 0;
185   while ((SockRead(sock, smtp_response, sizeof(smtp_response)-1)) != -1)
186   {
187       size_t n;
188
189       set_timeout(0);
190
191       n = strlen(smtp_response);
192       if (n > 0 && smtp_response[n-1] == '\n')
193           smtp_response[--n] = '\0';
194       if (n > 0 && smtp_response[n-1] == '\r')
195           smtp_response[--n] = '\0';
196       if (n < 4)
197           return SM_ERROR;
198       smtp_response[n] = '\0';
199       if (outlevel >= O_MONITOR)
200           report(stdout, "%cMTP< %s\n", smtp_mode, smtp_response);
201       for (hp = extensions; hp->name; hp++)
202           if (!strncasecmp(hp->name, smtp_response+4, strlen(hp->name))) {
203               *opt |= hp->value;
204               if (strncmp(hp->name, "AUTH ", 5) == 0)
205                 strncpy(auth_response, smtp_response, sizeof(auth_response));
206                 auth_response[sizeof(auth_response)-1] = '\0';
207           }
208       if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') && smtp_response[3] == ' ') {
209           if (*opt & ESMTP_AUTH)
210                 SMTP_auth(sock, smtp_mode, name, password, auth_response);
211           return SM_OK;
212       }
213       else if (smtp_response[3] != '-')
214           return SM_ERROR;
215   }
216   return SM_UNRECOVERABLE;
217 }
218
219 int SMTP_from(int sock, char smtp_mode, const char *from, const char *opts)
220 /* send a "MAIL FROM:" message to the SMTP listener */
221 {
222     int ok;
223     char buf[MSGBUFSIZE];
224
225     if (from[0]=='<')
226         snprintf(buf, sizeof(buf), "MAIL FROM:%s", from);
227     else
228         snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", from);
229     if (opts)
230         snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s", opts);
231     SockPrintf(sock,"%s\r\n", buf);
232     if (outlevel >= O_MONITOR)
233         report(stdout, "%cMTP> %s\n", smtp_mode, buf);
234     ok = SMTP_ok(sock, smtp_mode, TIMEOUT_MAIL);
235     return ok;
236 }
237
238 int SMTP_rcpt(int sock, char smtp_mode, const char *to)
239 /* send a "RCPT TO:" message to the SMTP listener */
240 {
241   int ok;
242
243   SockPrintf(sock,"RCPT TO:<%s>\r\n", to);
244   if (outlevel >= O_MONITOR)
245       report(stdout, "%cMTP> RCPT TO:<%s>\n", smtp_mode, to);
246   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_RCPT);
247   return ok;
248 }
249
250 int SMTP_data(int sock, char smtp_mode)
251 /* send a "DATA" message to the SMTP listener */
252 {
253   int ok;
254
255   SockPrintf(sock,"DATA\r\n");
256   if (outlevel >= O_MONITOR)
257       report(stdout, "%cMTP> DATA\n", smtp_mode);
258   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DATA);
259   return ok;
260 }
261
262 int SMTP_rset(int sock, char smtp_mode)
263 /* send a "RSET" message to the SMTP listener */
264 {
265   int ok;
266
267   SockPrintf(sock,"RSET\r\n");
268   if (outlevel >= O_MONITOR)
269       report(stdout, "%cMTP> RSET\n", smtp_mode);
270   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
271   return ok;
272 }
273
274 int SMTP_quit(int sock, char smtp_mode)
275 /* send a "QUIT" message to the SMTP listener */
276 {
277   int ok;
278
279   SockPrintf(sock,"QUIT\r\n");
280   if (outlevel >= O_MONITOR)
281       report(stdout, "%cMTP> QUIT\n", smtp_mode);
282   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
283   return ok;
284 }
285
286 int SMTP_eom(int sock, char smtp_mode)
287 /* send a message data terminator to the SMTP listener */
288 {
289   SockPrintf(sock,".\r\n");
290   if (outlevel >= O_MONITOR)
291       report(stdout, "%cMTP>. (EOM)\n", smtp_mode);
292
293   /* 
294    * When doing LMTP, must process many of these at the outer level. 
295    */
296   if (smtp_mode == 'S')
297       return SMTP_ok(sock, smtp_mode, TIMEOUT_EOM);
298   else
299       return SM_OK;
300 }
301
302 time_t last_smtp_ok = 0;
303
304 int SMTP_ok(int sock, char smtp_mode, int mintimeout)
305 /**< returns status of SMTP connection and saves the message in
306  * smtp_response, without trailing [CR]LF, but with normalized CRLF
307  * between multiple lines of multi-line replies */
308 {
309     char reply[MSGBUFSIZE], *i;
310     int tmo = mytimeout >= mintimeout ? mytimeout : mintimeout;
311
312     smtp_response[0] = '\0';
313
314     if (SockTimeout(sock, tmo))
315         return SM_UNRECOVERABLE;
316
317     while ((SockRead(sock, reply, sizeof(reply)-1)) != -1)
318     {
319         size_t n;
320
321         n = strlen(reply);
322         if (n > 0 && reply[n-1] == '\n')
323             reply[--n] = '\0';
324         if (n > 0 && reply[n-1] == '\r')
325             reply[--n] = '\0';
326
327         /* stomp over control characters */
328         for (i = reply; *i; i++)
329             if (iscntrl((unsigned char)*i))
330                 *i = '?';
331
332         if (outlevel >= O_MONITOR)
333             report(stdout, "%cMTP< %s\n", smtp_mode, reply);
334         /* note that \0 is part of the strchr search string and the
335          * blank after the reply code is optional (RFC 5321 4.2.1) */
336         if (n < 3 || !strchr(" -", reply[3]))
337         {
338             if (outlevel >= O_MONITOR)
339                 report(stderr, GT_("smtp listener protocol error\n"));
340             return SM_UNRECOVERABLE;
341         }
342
343         last_smtp_ok = time((time_t *) NULL);
344
345         strlcat(smtp_response, reply,  sizeof(smtp_response));
346
347         if (strchr("123", reply[0])
348                 && isdigit((unsigned char)reply[1])
349                 && isdigit((unsigned char)reply[2])
350                 && strchr(" ", reply[3])) /* matches space and \0 */ {
351             return SM_OK;
352         } else if (reply[3] != '-')
353             return SM_ERROR;
354
355         strlcat(smtp_response, "\r\n", sizeof(smtp_response));
356     }
357
358     if (outlevel >= O_MONITOR)
359         report(stderr, GT_("smtp listener protocol error\n"));
360     return SM_UNRECOVERABLE;
361 }
362
363 /* smtp.c ends here */