2 * fakesmtp - A fake SMTP server used by the nmh test suite
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.
15 #include <sys/socket.h>
16 #include <sys/types.h>
17 #include <sys/select.h>
22 #define PIDFILE "/tmp/fakesmtp.pid"
26 static void killpidfile(void);
27 static void handleterm(int);
28 static void putsmtp(int, char *);
29 static int getsmtp(int, char *);
32 main(int argc, char *argv[])
34 struct addrinfo hints, *res;
35 int rc, l, conn, on, datamode;
43 fprintf(stderr, "Usage: %s output-filename port\n", argv[0]);
47 if (!(f = fopen(argv[1], "w"))) {
48 fprintf(stderr, "Unable to open output file \"%s\": %s\n",
49 argv[1], strerror(errno));
54 * If there is a pid file already around, kill the previously running
55 * fakesmtp process. Hopefully this will reduce the race conditions
56 * that crop up when running the test suite.
59 if (stat(PIDFILE, &st) == 0) {
62 if (!(pid = fopen(PIDFILE, "r"))) {
63 fprintf(stderr, "Cannot open " PIDFILE
64 " (%s), continuing ...\n", strerror(errno));
66 rc = fscanf(pid, "%ld", &oldpid);
70 fprintf(stderr, "Unable to parse pid in "
71 PIDFILE ", continuing ...\n");
73 kill((pid_t) oldpid, SIGTERM);
80 memset(&hints, 0, sizeof(hints));
82 hints.ai_family = PF_INET;
83 hints.ai_socktype = SOCK_STREAM;
84 hints.ai_protocol = IPPROTO_TCP;
85 hints.ai_flags = AI_PASSIVE;
87 rc = getaddrinfo("127.0.0.1", argv[2], &hints, &res);
90 fprintf(stderr, "Unable to resolve localhost/%s: %s\n",
91 argv[2], gai_strerror(rc));
95 l = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
98 fprintf(stderr, "Unable to create listening socket: %s\n",
105 if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
106 fprintf(stderr, "Unable to set SO_REUSEADDR: %s\n",
111 if (bind(l, res->ai_addr, res->ai_addrlen) == -1) {
112 fprintf(stderr, "Unable to bind socket: %s\n", strerror(errno));
116 if (listen(l, 1) == -1) {
117 fprintf(stderr, "Unable to listen on socket: %s\n",
123 * Now we fork() and print out the process ID of our child
124 * for scripts to use. Once we do that, then exit.
131 fprintf(stderr, "Unable to fork child: %s\n", strerror(errno));
136 * Close stdin & stdout, otherwise people can
137 * think we're still doing stuff. For now leave stderr
144 printf("%ld\n", (long) child);
149 * Now that our socket & files are set up, wait 30 seconds for
150 * a connection. If there isn't one, then exit.
153 if (!(pid = fopen(PIDFILE, "w"))) {
154 fprintf(stderr, "Cannot open " PIDFILE ": %s\n",
159 fprintf(pid, "%ld\n", (long) getpid());
162 signal(SIGTERM, handleterm);
170 rc = select(l + 1, &readfd, NULL, NULL, &tv);
173 fprintf(stderr, "select() failed: %s\n", strerror(errno));
178 * I think if we get a timeout, we should just exit quietly.
186 * Alright, got a connection! Accept it.
189 if ((conn = accept(l, NULL, NULL)) == -1) {
190 fprintf(stderr, "Unable to accept connection: %s\n",
198 * Pretend to be an SMTP server.
201 putsmtp(conn, "220 Not really an ESMTP server");
207 rc = getsmtp(conn, line);
212 fprintf(f, "%s\n", line);
215 * If we're in DATA mode, then check to see if we've got
216 * a "."; otherwise, continue
220 if (strcmp(line, ".") == 0) {
222 putsmtp(conn, "250 Thanks for the info!");
228 * Most commands we ignore and send the same response to.
231 if (strcmp(line, "QUIT") == 0) {
234 putsmtp(conn, "221 Later alligator!");
237 } else if (strcmp(line, "DATA") == 0) {
238 putsmtp(conn, "354 Go ahead");
241 putsmtp(conn, "250 I'll buy that for a dollar!");
252 * Write a line to the SMTP client on the other end
256 putsmtp(int socket, char *data)
260 iov[0].iov_base = data;
261 iov[0].iov_len = strlen(data);
262 iov[1].iov_base = "\r\n";
265 writev(socket, iov, 2);
269 * Read a line (up to the \r\n)
273 getsmtp(int socket, char *data)
276 static unsigned int bytesinbuf = 0;
277 static char buffer[LINESIZE * 2], *p;
284 if (bytesinbuf > 0 && (p = strchr(buffer, '\r')) &&
287 strncpy(data, buffer, LINESIZE);
288 data[LINESIZE - 1] = '\0';
292 * Shuffle leftover bytes back to the beginning
295 bytesinbuf -= cc + 2; /* Don't forget \r\n */
296 if (bytesinbuf > 0) {
297 memmove(buffer, buffer + cc + 2, bytesinbuf);
302 if (bytesinbuf >= sizeof(buffer)) {
303 fprintf(stderr, "Buffer overflow in getsmtp()!\n");
307 memset(buffer + bytesinbuf, 0, sizeof(buffer) - bytesinbuf);
308 cc = read(socket, buffer + bytesinbuf,
309 sizeof(buffer) - bytesinbuf);
312 fprintf(stderr, "Read failed: %s\n", strerror(errno));
328 handleterm(int signal)
338 * Get rid of our pid file