X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/afee8ed17a56617618fb7f963b111d7cf0fb56e7..9291a5f82480f2458e04cb9ea7e6749bc952b308:/test/fakesmtp.c diff --git a/test/fakesmtp.c b/test/fakesmtp.c index 7121c302..c4fa3e7c 100644 --- a/test/fakesmtp.c +++ b/test/fakesmtp.c @@ -1,5 +1,4 @@ -/* - * fakesmtp - A fake SMTP server used by the nmh test suite +/* fakesmtp - A fake SMTP server used by the nmh test suite * * This code is Copyright (c) 2012, by the authors of nmh. See the * COPYRIGHT file in the root directory of the nmh distribution for @@ -10,35 +9,38 @@ #include #include #include -#include #include #include -#include #include -#include #include -#include -#include #define PIDFILE "/tmp/fakesmtp.pid" #define LINESIZE 1024 -static void killpidfile(void); -static void handleterm(int); -static void putsmtp(int, char *); +enum { + /* Processing top-level SMTP commands (e.g. EHLO, DATA). */ + SMTP_TOP, + + /* Processing payload of a DATA command. */ + SMTP_DATA, + + /* Looking for the blank line required by XOAUTH2 after 334 response. */ + SMTP_XOAUTH_ERR +}; + +void putcrlf(int, char *); +int serve(const char *, const char *); + static int getsmtp(int, char *); int main(int argc, char *argv[]) { - struct addrinfo hints, *res; - int rc, l, conn, on, datamode; - FILE *f, *pid; - pid_t child; - fd_set readfd; - struct stat st; - struct timeval tv; + int rc, conn, smtp_state; + FILE *f; + const char *xoauth = getenv("XOAUTH"); + const char *smtputf8 = getenv("SMTPUTF8"); if (argc != 3) { fprintf(stderr, "Usage: %s output-filename port\n", argv[0]); @@ -51,156 +53,14 @@ main(int argc, char *argv[]) exit(1); } - /* - * If there is a pid file already around, kill the previously running - * fakesmtp process. Hopefully this will reduce the race conditions - * that crop up when running the test suite. - */ - - if (stat(PIDFILE, &st) == 0) { - long oldpid; - - if (!(pid = fopen(PIDFILE, "r"))) { - fprintf(stderr, "Cannot open " PIDFILE - " (%s), continuing ...\n", strerror(errno)); - } else { - rc = fscanf(pid, "%ld", &oldpid); - fclose(pid); - - if (rc != 1) { - fprintf(stderr, "Unable to parse pid in " - PIDFILE ", continuing ...\n"); - } else { - kill((pid_t) oldpid, SIGTERM); - } - } - - unlink(PIDFILE); - } - - memset(&hints, 0, sizeof(hints)); - - hints.ai_family = PF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - - rc = getaddrinfo("127.0.0.1", argv[2], &hints, &res); - - if (rc) { - fprintf(stderr, "Unable to resolve localhost/%s: %s\n", - argv[2], gai_strerror(rc)); - exit(1); - } - - l = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - - if (l == -1) { - fprintf(stderr, "Unable to create listening socket: %s\n", - strerror(errno)); - exit(1); - } - - on = 1; - - if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { - fprintf(stderr, "Unable to set SO_REUSEADDR: %s\n", - strerror(errno)); - exit(1); - } - - if (bind(l, res->ai_addr, res->ai_addrlen) == -1) { - fprintf(stderr, "Unable to bind socket: %s\n", strerror(errno)); - exit(1); - } - - if (listen(l, 1) == -1) { - fprintf(stderr, "Unable to listen on socket: %s\n", - strerror(errno)); - exit(1); - } + conn = serve(PIDFILE, argv[2]); - /* - * Now we fork() and print out the process ID of our child - * for scripts to use. Once we do that, then exit. - */ - - child = fork(); - - switch (child) { - case -1: - fprintf(stderr, "Unable to fork child: %s\n", strerror(errno)); - exit(1); - break; - case 0: - /* - * Close stdin & stdout, otherwise people can - * think we're still doing stuff. For now leave stderr - * open. - */ - fclose(stdin); - fclose(stdout); - break; - default: - printf("%ld\n", (long) child); - exit(0); - } - - /* - * Now that our socket & files are set up, wait 30 seconds for - * a connection. If there isn't one, then exit. - */ - - if (!(pid = fopen(PIDFILE, "w"))) { - fprintf(stderr, "Cannot open " PIDFILE ": %s\n", - strerror(errno)); - exit(1); - } - - fprintf(pid, "%ld\n", (long) getpid()); - fclose(pid); - - signal(SIGTERM, handleterm); - atexit(killpidfile); - - FD_ZERO(&readfd); - FD_SET(l, &readfd); - tv.tv_sec = 30; - tv.tv_usec = 0; - - rc = select(l + 1, &readfd, NULL, NULL, &tv); - - if (rc < 0) { - fprintf(stderr, "select() failed: %s\n", strerror(errno)); - exit(1); - } - - /* - * I think if we get a timeout, we should just exit quietly. - */ - - if (rc == 0) { - exit(1); - } - - /* - * Alright, got a connection! Accept it. - */ - - if ((conn = accept(l, NULL, NULL)) == -1) { - fprintf(stderr, "Unable to accept connection: %s\n", - strerror(errno)); - exit(1); - } - - close(l); - /* * Pretend to be an SMTP server. */ - putsmtp(conn, "220 Not really an ESMTP server"); - datamode = 0; + putcrlf(conn, "220 Not really an ESMTP server"); + smtp_state = SMTP_TOP; for (;;) { char line[LINESIZE]; @@ -210,19 +70,20 @@ main(int argc, char *argv[]) if (rc == -1) break; /* EOF */ - fprintf(f, "%s\n", line); - - /* - * If we're in DATA mode, then check to see if we've got - * a "."; otherwise, continue - */ + fputs(line, f); + putc('\n', f); - if (datamode) { + switch (smtp_state) { + case SMTP_DATA: if (strcmp(line, ".") == 0) { - datamode = 0; - putsmtp(conn, "250 Thanks for the info!"); + smtp_state = SMTP_TOP; + putcrlf(conn, "250 Thanks for the info!"); } continue; + case SMTP_XOAUTH_ERR: + smtp_state = SMTP_TOP; + putcrlf(conn, "535 Not no way, not no how!"); + continue; } /* @@ -232,15 +93,41 @@ main(int argc, char *argv[]) if (strcmp(line, "QUIT") == 0) { fclose(f); f = NULL; - putsmtp(conn, "221 Later alligator!"); + putcrlf(conn, "221 Later alligator!"); close(conn); break; - } else if (strcmp(line, "DATA") == 0) { - putsmtp(conn, "354 Go ahead"); - datamode = 1; - } else { - putsmtp(conn, "250 I'll buy that for a dollar!"); } + if (strcmp(line, "DATA") == 0) { + putcrlf(conn, "354 Go ahead"); + smtp_state = SMTP_DATA; + continue; + } + if (strncmp(line, "EHLO", 4) == 0) { + putcrlf(conn, "250-ready"); + if (smtputf8 != NULL) { + putcrlf(conn, "250-8BITMIME"); + putcrlf(conn, "250-SMTPUTF8"); + } + if (xoauth != NULL) { + putcrlf(conn, "250-AUTH XOAUTH2"); + } + putcrlf(conn, "250 I'll buy that for a dollar!"); + continue; + } + if (xoauth != NULL) { + /* XOAUTH2 support enabled; handle AUTH (and EHLO above). */ + if (strncmp(line, "AUTH", 4) == 0) { + if (strncmp(line, "AUTH XOAUTH2", 12) == 0 + && strstr(line, xoauth) != NULL) { + putcrlf(conn, "235 OK come in"); + continue; + } + putcrlf(conn, "334 base64-json-err"); + smtp_state = SMTP_XOAUTH_ERR; + continue; + } + } + putcrlf(conn, "250 I'll buy that for a dollar!"); } if (f) @@ -249,23 +136,6 @@ main(int argc, char *argv[]) exit(0); } -/* - * Write a line to the SMTP client on the other end - */ - -static void -putsmtp(int socket, char *data) -{ - struct iovec iov[2]; - - iov[0].iov_base = data; - iov[0].iov_len = strlen(data); - iov[1].iov_base = "\r\n"; - iov[1].iov_len = 2; - - writev(socket, iov, 2); -} - /* * Read a line (up to the \r\n) */ @@ -320,27 +190,3 @@ getsmtp(int socket, char *data) bytesinbuf += cc; } } - -/* - * Handle a SIGTERM - */ - -static void -handleterm(int signal) -{ - (void) signal; - - killpidfile(); - fflush(NULL); - _exit(1); -} - -/* - * Get rid of our pid file - */ - -static void -killpidfile(void) -{ - unlink(PIDFILE); -}