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;
42 fprintf(stderr, "Usage: %s output-filename port\n", argv[0]);
46 if (!(f = fopen(argv[1], "w"))) {
47 fprintf(stderr, "Unable to open output file \"%s\": %s\n",
48 argv[1], strerror(errno));
53 * If there is a pid file already around, kill the previously running
54 * fakesmtp process. Hopefully this will reduce the race conditions
55 * that crop up when running the test suite.
58 if (stat(PIDFILE, &st) == 0) {
61 if (!(pid = fopen(PIDFILE, "r"))) {
62 fprintf(stderr, "Cannot open " PIDFILE
63 " (%s), continuing ...\n", strerror(errno));
65 rc = fscanf(pid, "%ld", &oldpid);
69 fprintf(stderr, "Unable to parse pid in "
70 PIDFILE ", continuing ...\n");
72 kill((pid_t) oldpid, SIGTERM);
79 memset(&hints, 0, sizeof(hints));
81 hints.ai_family = PF_INET;
82 hints.ai_socktype = SOCK_STREAM;
83 hints.ai_protocol = IPPROTO_TCP;
84 hints.ai_flags = AI_PASSIVE;
86 rc = getaddrinfo("127.0.0.1", argv[2], &hints, &res);
89 fprintf(stderr, "Unable to resolve localhost/%s: %s\n",
90 argv[2], gai_strerror(rc));
94 l = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
97 fprintf(stderr, "Unable to create listening socket: %s\n",
104 if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
105 fprintf(stderr, "Unable to set SO_REUSEADDR: %s\n",
110 if (bind(l, res->ai_addr, res->ai_addrlen) == -1) {
111 fprintf(stderr, "Unable to bind socket: %s\n", strerror(errno));
115 if (listen(l, 1) == -1) {
116 fprintf(stderr, "Unable to listen on socket: %s\n",
122 * Now that our socket & files are set up, wait 30 seconds for
123 * a connection. If there isn't one, then exit.
126 if (!(pid = fopen(PIDFILE, "w"))) {
127 fprintf(stderr, "Cannot open " PIDFILE ": %s\n",
132 fprintf(pid, "%ld\n", (long) getpid());
135 signal(SIGTERM, handleterm);
143 rc = select(l + 1, &readfd, NULL, NULL, &tv);
146 fprintf(stderr, "select() failed: %s\n", strerror(errno));
151 * I think if we get a timeout, we should just exit quietly.
159 * Alright, got a connection! Accept it.
162 if ((conn = accept(l, NULL, NULL)) == -1) {
163 fprintf(stderr, "Unable to accept connection: %s\n",
171 * Pretend to be an SMTP server.
174 putsmtp(conn, "220 Not really an ESMTP server");
180 rc = getsmtp(conn, line);
185 fprintf(f, "%s\n", line);
188 * If we're in DATA mode, then check to see if we've got
189 * a "."; otherwise, continue
193 if (strcmp(line, ".") == 0) {
195 putsmtp(conn, "250 Thanks for the info!");
201 * Most commands we ignore and send the same response to.
204 if (strcmp(line, "QUIT") == 0) {
205 putsmtp(conn, "221 Later alligator!");
208 } else if (strcmp(line, "DATA") == 0) {
209 putsmtp(conn, "354 Go ahead");
212 putsmtp(conn, "250 I'll buy that for a dollar!");
222 * Write a line to the SMTP client on the other end
226 putsmtp(int socket, char *data)
230 iov[0].iov_base = data;
231 iov[0].iov_len = strlen(data);
232 iov[1].iov_base = "\r\n";
235 writev(socket, iov, 2);
239 * Read a line (up to the \r\n)
243 getsmtp(int socket, char *data)
246 static unsigned int bytesinbuf = 0;
247 static char buffer[LINESIZE * 2], *p;
254 if (bytesinbuf > 0 && (p = strchr(buffer, '\r')) &&
257 strncpy(data, buffer, LINESIZE);
258 data[LINESIZE - 1] = '\0';
262 * Shuffle leftover bytes back to the beginning
265 bytesinbuf -= cc + 2; /* Don't forget \r\n */
266 if (bytesinbuf > 0) {
267 memmove(buffer, buffer + cc + 2, bytesinbuf);
272 if (bytesinbuf >= sizeof(buffer)) {
273 fprintf(stderr, "Buffer overflow in getsmtp()!\n");
277 memset(buffer + bytesinbuf, 0, sizeof(buffer) - bytesinbuf);
278 cc = read(socket, buffer + bytesinbuf,
279 sizeof(buffer) - bytesinbuf);
282 fprintf(stderr, "Read failed: %s\n", strerror(errno));
298 handleterm(int signal)
308 * Get rid of our pid file