X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/fc6817f4dcebc81838636ab6dd01f945b80bac2a..886127936eae6a5bff449d797abb4e8aa10f9186:/test/fakesmtp.c diff --git a/test/fakesmtp.c b/test/fakesmtp.c index 700bc45d..42d4f181 100644 --- a/test/fakesmtp.c +++ b/test/fakesmtp.c @@ -10,33 +10,37 @@ #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; - fd_set readfd; - struct stat st; - struct timeval tv; + int rc, conn, smtp_state; + FILE *f; + const char *xoauth = getenv("XOAUTH"); if (argc != 3) { fprintf(stderr, "Usage: %s output-filename port\n", argv[0]); @@ -49,127 +53,14 @@ main(int argc, char *argv[]) exit(1); } - 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 that our socket & files are set up, do the following things: - * - * - Check for a PID file. If there is one, kill the old version. - * - Wait 30 seconds for a connection. If there isn't one, then - * exit. - */ - - 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); - } - - 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]; @@ -181,17 +72,17 @@ main(int argc, char *argv[]) fprintf(f, "%s\n", line); - /* - * If we're in DATA mode, then check to see if we've got - * a "."; otherwise, continue - */ - - 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; } /* @@ -199,39 +90,44 @@ main(int argc, char *argv[]) */ if (strcmp(line, "QUIT") == 0) { - putsmtp(conn, "221 Later alligator!"); + fclose(f); + f = NULL; + 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 (xoauth != NULL) { + /* XOAUTH2 support enabled; handle EHLO and AUTH. */ + if (strncmp(line, "EHLO", 4) == 0) { + putcrlf(conn, "250-ready"); + putcrlf(conn, "250 AUTH XOAUTH2"); + continue; + } + 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!"); } - fclose(f); + if (f) + fclose(f); 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) */ @@ -286,27 +182,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); -}