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));
52 memset(&hints, 0, sizeof(hints));
54 hints.ai_family = PF_INET;
55 hints.ai_socktype = SOCK_STREAM;
56 hints.ai_protocol = IPPROTO_TCP;
57 hints.ai_flags = AI_PASSIVE;
59 rc = getaddrinfo("127.0.0.1", argv[2], &hints, &res);
62 fprintf(stderr, "Unable to resolve localhost/%s: %s\n",
63 argv[2], gai_strerror(rc));
67 l = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
70 fprintf(stderr, "Unable to create listening socket: %s\n",
77 if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
78 fprintf(stderr, "Unable to set SO_REUSEADDR: %s\n",
83 if (bind(l, res->ai_addr, res->ai_addrlen) == -1) {
84 fprintf(stderr, "Unable to bind socket: %s\n", strerror(errno));
88 if (listen(l, 1) == -1) {
89 fprintf(stderr, "Unable to listen on socket: %s\n",
95 * Now that our socket & files are set up, do the following things:
97 * - Check for a PID file. If there is one, kill the old version.
98 * - Wait 30 seconds for a connection. If there isn't one, then
102 if (stat(PIDFILE, &st) == 0) {
105 if (!(pid = fopen(PIDFILE, "r"))) {
106 fprintf(stderr, "Cannot open " PIDFILE
107 " (%s), continuing ...\n", strerror(errno));
109 rc = fscanf(pid, "%ld", &oldpid);
113 fprintf(stderr, "Unable to parse pid in "
114 PIDFILE ", continuing ...\n");
116 kill((pid_t) oldpid, SIGTERM);
123 if (!(pid = fopen(PIDFILE, "w"))) {
124 fprintf(stderr, "Cannot open " PIDFILE ": %s\n",
129 fprintf(pid, "%ld\n", (long) getpid());
132 signal(SIGTERM, handleterm);
140 rc = select(l + 1, &readfd, NULL, NULL, &tv);
143 fprintf(stderr, "select() failed: %s\n", strerror(errno));
148 * I think if we get a timeout, we should just exit quietly.
156 * Alright, got a connection! Accept it.
159 if ((conn = accept(l, NULL, NULL)) == -1) {
160 fprintf(stderr, "Unable to accept connection: %s\n",
168 * Pretend to be an SMTP server.
171 putsmtp(conn, "220 Not really an ESMTP server");
177 rc = getsmtp(conn, line);
182 fprintf(f, "%s\n", line);
185 * If we're in DATA mode, then check to see if we've got
186 * a "."; otherwise, continue
190 if (strcmp(line, ".") == 0) {
192 putsmtp(conn, "250 Thanks for the info!");
198 * Most commands we ignore and send the same response to.
201 if (strcmp(line, "QUIT") == 0) {
202 putsmtp(conn, "221 Later alligator!");
205 } else if (strcmp(line, "DATA") == 0) {
206 putsmtp(conn, "354 Go ahead");
209 putsmtp(conn, "250 I'll buy that for a dollar!");
219 * Write a line to the SMTP client on the other end
223 putsmtp(int socket, char *data)
227 iov[0].iov_base = data;
228 iov[0].iov_len = strlen(data);
229 iov[1].iov_base = "\r\n";
232 writev(socket, iov, 2);
236 * Read a line (up to the \r\n)
240 getsmtp(int socket, char *data)
243 static unsigned int bytesinbuf = 0;
244 static char buffer[LINESIZE * 2], *p;
251 if (bytesinbuf > 0 && (p = strchr(buffer, '\r')) &&
254 strncpy(data, buffer, LINESIZE);
255 data[LINESIZE - 1] = '\0';
259 * Shuffle leftover bytes back to the beginning
262 bytesinbuf -= cc + 2; /* Don't forget \r\n */
263 if (bytesinbuf > 0) {
264 memmove(buffer, buffer + cc + 2, bytesinbuf);
269 if (bytesinbuf >= sizeof(buffer)) {
270 fprintf(stderr, "Buffer overflow in getsmtp()!\n");
274 memset(buffer + bytesinbuf, 0, sizeof(buffer) - bytesinbuf);
275 cc = read(socket, buffer + bytesinbuf,
276 sizeof(buffer) - bytesinbuf);
279 fprintf(stderr, "Read failed: %s\n", strerror(errno));
295 handleterm(int signal)
305 * Get rid of our pid file