summaryrefslogtreecommitdiff
path: root/nullpop.c
diff options
context:
space:
mode:
Diffstat (limited to 'nullpop.c')
-rw-r--r--nullpop.c409
1 files changed, 409 insertions, 0 deletions
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 <jkaivo@nodomainname.net>
+
+ 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <grp.h>
+#include <syslog.h>
+#include <ctype.h>
+
+/* 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;
+}