]> diplodocus.org Git - nmh/blob - test/fakesmtp.c
mhlsbr.c: Don't strchr(3) non-string NUL-less buffer.
[nmh] / test / fakesmtp.c
1 /*
2 * fakesmtp - A fake SMTP server used by the nmh test suite
3 *
4 * This code is Copyright (c) 2012, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <sys/socket.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17
18 #define PIDFILE "/tmp/fakesmtp.pid"
19
20 #define LINESIZE 1024
21
22 enum {
23 /* Processing top-level SMTP commands (e.g. EHLO, DATA). */
24 SMTP_TOP,
25
26 /* Processing payload of a DATA command. */
27 SMTP_DATA,
28
29 /* Looking for the blank line required by XOAUTH2 after 334 response. */
30 SMTP_XOAUTH_ERR
31 };
32
33 void putcrlf(int, char *);
34 int serve(const char *, const char *);
35
36 static int getsmtp(int, char *);
37
38 int
39 main(int argc, char *argv[])
40 {
41 int rc, conn, smtp_state;
42 FILE *f;
43 const char *xoauth = getenv("XOAUTH");
44 const char *smtputf8 = getenv("SMTPUTF8");
45
46 if (argc != 3) {
47 fprintf(stderr, "Usage: %s output-filename port\n", argv[0]);
48 exit(1);
49 }
50
51 if (!(f = fopen(argv[1], "w"))) {
52 fprintf(stderr, "Unable to open output file \"%s\": %s\n",
53 argv[1], strerror(errno));
54 exit(1);
55 }
56
57 conn = serve(PIDFILE, argv[2]);
58
59 /*
60 * Pretend to be an SMTP server.
61 */
62
63 putcrlf(conn, "220 Not really an ESMTP server");
64 smtp_state = SMTP_TOP;
65
66 for (;;) {
67 char line[LINESIZE];
68
69 rc = getsmtp(conn, line);
70
71 if (rc == -1)
72 break; /* EOF */
73
74 fputs(line, f);
75 putc('\n', f);
76
77 switch (smtp_state) {
78 case SMTP_DATA:
79 if (strcmp(line, ".") == 0) {
80 smtp_state = SMTP_TOP;
81 putcrlf(conn, "250 Thanks for the info!");
82 }
83 continue;
84 case SMTP_XOAUTH_ERR:
85 smtp_state = SMTP_TOP;
86 putcrlf(conn, "535 Not no way, not no how!");
87 continue;
88 }
89
90 /*
91 * Most commands we ignore and send the same response to.
92 */
93
94 if (strcmp(line, "QUIT") == 0) {
95 fclose(f);
96 f = NULL;
97 putcrlf(conn, "221 Later alligator!");
98 close(conn);
99 break;
100 }
101 if (strcmp(line, "DATA") == 0) {
102 putcrlf(conn, "354 Go ahead");
103 smtp_state = SMTP_DATA;
104 continue;
105 }
106 if (strncmp(line, "EHLO", 4) == 0) {
107 putcrlf(conn, "250-ready");
108 if (smtputf8 != NULL) {
109 putcrlf(conn, "250-8BITMIME");
110 putcrlf(conn, "250-SMTPUTF8");
111 }
112 if (xoauth != NULL) {
113 putcrlf(conn, "250-AUTH XOAUTH2");
114 }
115 putcrlf(conn, "250 I'll buy that for a dollar!");
116 continue;
117 }
118 if (xoauth != NULL) {
119 /* XOAUTH2 support enabled; handle AUTH (and EHLO above). */
120 if (strncmp(line, "AUTH", 4) == 0) {
121 if (strncmp(line, "AUTH XOAUTH2", 12) == 0
122 && strstr(line, xoauth) != NULL) {
123 putcrlf(conn, "235 OK come in");
124 continue;
125 }
126 putcrlf(conn, "334 base64-json-err");
127 smtp_state = SMTP_XOAUTH_ERR;
128 continue;
129 }
130 }
131 putcrlf(conn, "250 I'll buy that for a dollar!");
132 }
133
134 if (f)
135 fclose(f);
136
137 exit(0);
138 }
139
140 /*
141 * Read a line (up to the \r\n)
142 */
143
144 static int
145 getsmtp(int socket, char *data)
146 {
147 int cc;
148 static unsigned int bytesinbuf = 0;
149 static char buffer[LINESIZE * 2], *p;
150
151 for (;;) {
152 /*
153 * Find our \r\n
154 */
155
156 if (bytesinbuf > 0 && (p = strchr(buffer, '\r')) &&
157 *(p + 1) == '\n') {
158 *p = '\0';
159 strncpy(data, buffer, LINESIZE);
160 data[LINESIZE - 1] = '\0';
161 cc = strlen(buffer);
162
163 /*
164 * Shuffle leftover bytes back to the beginning
165 */
166
167 bytesinbuf -= cc + 2; /* Don't forget \r\n */
168 if (bytesinbuf > 0) {
169 memmove(buffer, buffer + cc + 2, bytesinbuf);
170 }
171 return cc;
172 }
173
174 if (bytesinbuf >= sizeof(buffer)) {
175 fprintf(stderr, "Buffer overflow in getsmtp()!\n");
176 exit(1);
177 }
178
179 memset(buffer + bytesinbuf, 0, sizeof(buffer) - bytesinbuf);
180 cc = read(socket, buffer + bytesinbuf,
181 sizeof(buffer) - bytesinbuf);
182
183 if (cc < 0) {
184 fprintf(stderr, "Read failed: %s\n", strerror(errno));
185 exit(1);
186 }
187
188 if (cc == 0)
189 return -1;
190
191 bytesinbuf += cc;
192 }
193 }