From abaa38cdf786bbecf4050a7edb2e6c6db3f789bf Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Fri, 31 Mar 2006 20:29:19 +0000 Subject: Initial import --- nullpop.c | 409 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 409 insertions(+) create mode 100644 nullpop.c (limited to 'nullpop.c') diff --git a/nullpop.c b/nullpop.c new file mode 100644 index 0000000..3be7da5 --- /dev/null +++ b/nullpop.c @@ -0,0 +1,409 @@ +/* nullpop: A NOOP POP3 Server. Based on: + + virtualmail-pop3d - a POP3 server with virtual domains support + This code is licensed under the GPL; it has several authors. + vm-pop3d is based on: + GNU POP3 - a small, fast, and efficient POP3 daemon + Copyright (C) 1999 Jakob 'sparky' Kaivo + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* A command that doesn't exist */ +#define BAD_COMMAND "Invalid command" + +/* Incorrect number of arguments passed to a command */ +#define BAD_ARGS "Invalid arguments" + +/* An action on a message that doesn't exist */ +#define NO_MESG "No such message" + +/* A command that is known but not implemented */ +#define NOT_IMPL "Not implemented" + +/* The command argument was > 40 characters */ +#define TOO_LONG "Argument too long" + +/* Longest legal POP command */ +#define POP_MAXCMDLEN 255 + + + +/* Maximum length of a hostname (is this defined somewhere else?) */ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + +#define OK 0 +#define ERR_BAD_ARGS 2 +#define ERR_NO_MESG 4 +#define ERR_NOT_IMPL 5 +#define ERR_BAD_CMD 6 +#define ERR_TOO_LONG 8 +#define ERR_NO_MEM 9 +#define ERR_DEAD_SOCK 10 +#define ERR_SIGNAL 11 +#define ERR_FILE 12 +#define ERR_NO_OFILE 13 +#define ERR_TIMEOUT 14 + +unsigned int timeout; +int ifile; +FILE *ofile; + +/* Prints out usage information and exits the program */ +void +pop3_usage(char *argv0) +{ + printf("Usage: %s [OPTIONS]\n", argv0); + printf("Runs the nullpop POP3 daemon.\n\n"); + printf(" -t sets idle timeout to TIMEOUT seconds\n"); + exit(2); +} + +/* This is called if it needs to quit without going to the UPDATE stage. + This is used for conditions such as out of memory, a broken socket, or + being killed on a signal */ +int +pop3_abquit(int reason) +{ + switch (reason) { + case ERR_NO_MEM: + if (NULL != ofile) + fprintf(ofile, "-ERR Out of memory, quitting\r\n"); + syslog(LOG_ERR, "Out of memory"); + break; + case ERR_DEAD_SOCK: + if (NULL != ofile) /* should this even print to socket? */ + fprintf(ofile, "-ERR Socket closed, quitting\r\n"); + syslog(LOG_ERR, "Socket closed"); + break; + case ERR_SIGNAL: + if (NULL != ofile) + fprintf(ofile, "-ERR Quitting on signal\r\n"); + break; + case ERR_TIMEOUT: + if (NULL != ofile) + fprintf(ofile, "-ERR Session timed out\r\n"); + else + syslog(LOG_INFO, "Session timed out for no user"); + break; + case ERR_NO_OFILE: + syslog(LOG_ERR, "Quitting - couldn't open stream"); + break; + default: + if (NULL != ofile) + fprintf(ofile, "-ERR Quitting (reason unknown)\r\n"); + syslog(LOG_ERR, "Unknown quit"); + break; + } + fflush(ofile); + exit(1); +} + +/* Default signal handler to call the pop3_abquit() function */ +void +pop3_signal(int signal) +{ + syslog(LOG_ERR, "Quitting on signal: %d", signal); + pop3_abquit(ERR_SIGNAL); +} + + +/* Gets a line of input from the client */ +char * +pop3_readline(int fd) +{ + fd_set rfds; + struct timeval tv; + char buf[1024], *ret = NULL; + int available; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + memset(buf, '\0', 1024); + + while (strchr(buf, '\n') == NULL) { + if (timeout > 0) { + available = select(fd + 1, &rfds, NULL, NULL, &tv); + if (!available) + pop3_abquit(ERR_TIMEOUT); + } + if (read(fd, buf, 1024) < 1) + pop3_abquit(ERR_DEAD_SOCK); + + if (ret == NULL) { + ret = malloc((strlen(buf) + 1) * sizeof(char)); + strcpy(ret, buf); + } else { + ret = realloc(ret, (strlen(ret) + strlen(buf) + 1) * sizeof(char)); + strcat(ret, buf); + } + } + return ret; +} + +/* Takes a string as input and returns either the remainder of the string + after the first space, or a zero length string if no space */ +char * +pop3_args(const char *cmd) +{ + int space = -1, i = 0, len; + char *buf; + + len = strlen(cmd) + 1; + buf = malloc(len * sizeof(char)); + if (buf == NULL) + pop3_abquit(ERR_NO_MEM); + + while (space < 0 && i < len) { + if (cmd[i] == ' ') + space = i + 1; + else if (cmd[i] == '\0' || cmd[i] == '\r' || cmd[i] == '\n') + len = i; + i++; + } + + if (space < 0) + buf[0] = '\0'; + else { + for (i = space; i < len; i++) + if (cmd[i] == '\0' || cmd[i] == '\r' || cmd[i] == '\n') + buf[i - space] = '\0'; + else + buf[i - space] = cmd[i]; + } + + return buf; +} + +/* This takes a string and returns the string up to the first space or end of + the string, whichever occurs first */ +char * +pop3_cmd(const char *cmd) +{ + char *buf; + int i = 0, len; + + len = strlen(cmd) + 1; + buf = malloc(len * sizeof(char)); + if (buf == NULL) + pop3_abquit(ERR_NO_MEM); + + for (i = 0; i < len; i++) { + if (cmd[i] == ' ' || cmd[i] == '\0' || cmd[i] == '\r' || cmd[i] == '\n') + len = i; + else + buf[i] = cmd[i]; + } + buf[i - 1] = '\0'; + return buf; +} + +/* The main part of the daemon. This function reads input from the client and + executes the proper functions. Also handles the bulk of error reporting. */ +int +pop3_mainloop(int infile, int outfile) +{ + int status = OK; + char *buf, *arg, *cmd; + struct sockaddr_in name; + int namelen = sizeof(name); + char *temp_domain; + + ifile = infile; + ofile = fdopen(outfile, "w+"); + if (ofile == NULL) + pop3_abquit(ERR_NO_OFILE); + + if (getpeername(infile, + (struct sockaddr *) & name, (socklen_t *) & namelen) == 0) { + if ((temp_domain = (char *) inet_ntoa(name.sin_addr))) + syslog(LOG_INFO,"Connect from %s", temp_domain); + else + syslog(LOG_INFO, "Connection opened"); + } + else + syslog(LOG_INFO, "Incoming connection opened"); + + fflush(ofile); + fprintf(ofile, "+OK POP3 \r\n"); + + for (;;) { + fflush(ofile); + status = OK; + buf = pop3_readline(ifile); + cmd = pop3_cmd(buf); + arg = pop3_args(buf); + + if (strlen(arg) > POP_MAXCMDLEN || strlen(cmd) > POP_MAXCMDLEN) + status = ERR_TOO_LONG; + else if (strlen(cmd) > 4) + status = ERR_BAD_CMD; + else if (strncasecmp(cmd, "USER", 4) == 0) { + fprintf(ofile, "+OK\r\n"); + status = OK; + } else if (strncasecmp(cmd, "PASS", 4) == 0) { + fprintf(ofile, "+OK\r\n"); + status = OK; + } else if (strncasecmp(cmd, "QUIT", 4) == 0) { + fprintf(ofile, "+OK\r\n"); + fflush(ofile); + break; + } else if (strncasecmp(cmd, "AUTH", 4) == 0) { + status = ERR_NOT_IMPL; + } else if (strncasecmp(cmd, "STAT", 4) == 0) { + fprintf(ofile, "+OK %d %d\r\n", 0, 0); + status = OK; + } else if (strncasecmp(cmd, "LIST", 4) == 0) { + if (strchr(arg, ' ') != NULL) { + status = ERR_BAD_ARGS; + } else if (strlen(arg) == 0) { + fprintf(ofile, "+OK\r\n"); + fprintf(ofile, ".\r\n"); + status = OK; + } else { + status = ERR_NO_MESG; + } + } else if (strncasecmp(cmd, "RETR", 4) == 0) { + if ((strlen(arg) == 0) || (strchr(arg, ' ') != NULL)) + status = ERR_BAD_ARGS; + else + status = ERR_NO_MESG; + } else if (strncasecmp(cmd, "DELE", 4) == 0) { + if ((strlen(arg) == 0) || (strchr(arg, ' ') != NULL)) + status = ERR_BAD_ARGS; + else + status = ERR_NO_MESG; + } else if (strncasecmp(cmd, "NOOP", 4) == 0) { + if (strlen(arg) != 0) + status = ERR_BAD_ARGS; + else { + fprintf(ofile, "+OK\r\n"); + status = OK; + } + } else if (strncasecmp(cmd, "RSET", 4) == 0) { + if (strlen(arg) != 0) + status = ERR_BAD_ARGS; + else { + fprintf(ofile, "+OK\r\n"); + status = OK; + } + } else if ((strncasecmp(cmd, "TOP", 3) == 0) && (strlen(cmd) == 3)) { + status = ERR_NO_MESG; + } else if (strncasecmp(cmd, "UIDL", 4) == 0) { + if (strchr(arg, ' ') != NULL) + status = ERR_BAD_ARGS; + else + status = ERR_NO_MESG; + } else if (strncasecmp(cmd, "CAPA", 4) == 0) { + if (strlen(arg) != 0) + status = ERR_BAD_ARGS; + else { + fprintf(ofile, "+OK Capability list follows\r\n"); + fprintf(ofile, "TOP\r\n"); + fprintf(ofile, "USER\r\n"); + fprintf(ofile, "RESP-CODES\r\n"); + fprintf(ofile, "UIDL\r\n"); + fprintf(ofile, ".\r\n"); + status = OK; + } + } else { + status = ERR_BAD_CMD; + } + + if (status == OK) + fflush(ofile); + else if (status == ERR_BAD_ARGS) + fprintf(ofile, "-ERR " BAD_ARGS "\r\n"); + else if (status == ERR_NO_MESG) + fprintf(ofile, "-ERR " NO_MESG "\r\n"); + else if (status == ERR_NOT_IMPL) + fprintf(ofile, "-ERR " NOT_IMPL "\r\n"); + else if (status == ERR_BAD_CMD) + fprintf(ofile, "-ERR " BAD_COMMAND "\r\n"); + else if (status == ERR_TOO_LONG) + fprintf(ofile, "-ERR " TOO_LONG "\r\n"); + + free(buf); + free(cmd); + free(arg); + } + + fflush(ofile); + return OK; +} + +int +main(int argc, char **argv) +{ + int c = 0; + timeout = 0; /* Timeout turned off */ + + /* Set the signal handlers */ + signal(SIGINT, pop3_signal); + signal(SIGQUIT, pop3_signal); + signal(SIGTERM, pop3_signal); + +/* Don't die when a process goes away unexpectedly. + Ignore write on a pipe with no reader. */ + signal(SIGPIPE, SIG_IGN); + + while ((c = getopt(argc, argv, "t:") && c)) { + switch (c) { + case 't': + timeout = atoi(optarg); + break; + default: + pop3_usage(argv[0]); + break; + } + } + + /* Set up for syslog */ + openlog("nullpop", LOG_PID, LOG_MAIL); + + pop3_mainloop(fileno(stdin), fileno(stdout)); + + /* Close the syslog connection and exit */ + closelog(); + return OK; +} -- cgit v1.2.3