summaryrefslogtreecommitdiff
path: root/pop3e.c
diff options
context:
space:
mode:
authorSunil Nimmagadda <sunil@nimmagadda.net>2014-03-27 09:53:22 +0500
committerSunil Nimmagadda <sunil@nimmagadda.net>2014-03-27 09:53:22 +0500
commit305e2fd5530ace4fc7c3e9665a4645a94efdfbd7 (patch)
tree934ccba83378bdb1d17d4bf26b8dda37d60a7e74 /pop3e.c
Import pop3d.
Diffstat (limited to 'pop3e.c')
-rw-r--r--pop3e.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/pop3e.c b/pop3e.c
new file mode 100644
index 0000000..cb6f017
--- /dev/null
+++ b/pop3e.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2014 Sunil Nimmagadda <sunil@nimmagadda.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/tree.h>
+
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "imsgev.h"
+#include "pop3d.h"
+#include "ssl.h"
+
+#define BACKLOG 5
+
+static void auth_response(struct session *, int);
+static void pop3_accept(int, short, void *);
+static void pop3_listen(const char *);
+static void pop3_pause(int, short, void *);
+static void pop3d_imsgev(struct imsgev *, int, struct imsg *);
+static void needfd(struct imsgev *);
+static void sig_handler(int, short, void *);
+
+struct imsgev iev_pop3d;
+void *ssl_ctx;
+
+pid_t
+pop3_main(int pair[2], struct passwd *pw)
+{
+ extern struct session_tree sessions;
+ struct event ev_sigint, ev_sigterm;
+ pid_t pid;
+
+ pid = fork();
+ if (pid < 0)
+ fatal("pop3e: fork");
+
+ if (pid > 0)
+ return (pid);
+
+ close(pair[0]);
+ setproctitle("pop3 engine");
+ SPLAY_INIT(&sessions);
+ event_init();
+ signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ imsgev_init(&iev_pop3d, pair[1], NULL, pop3d_imsgev, needfd);
+ pop3_listen("pop3");
+
+ ssl_init();
+ if ((ssl_ctx = ssl_setup()) == NULL)
+ fatal("ssl_setup failed");
+ pop3_listen("pop3s");
+
+ if (chroot(pw->pw_dir) == -1 || chdir("/") == -1)
+ fatal("chroot");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("cannot drop privileges");
+
+ if (event_dispatch() < 0)
+ fatal("event_dispatch");
+
+ logit(LOG_INFO, "pop3 engine exiting");
+ _exit(0);
+}
+
+static void
+pop3_listen(const char *port)
+{
+ struct listener *l = NULL;
+ struct addrinfo hints, *res, *res0;
+ int error, opt, serrno, s;
+ const char *cause = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(NULL, port, &hints, &res0);
+ if (error)
+ errx(1, "%s", gai_strerror(error));
+
+ for (res = res0; res != NULL; res = res->ai_next) {
+ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s == -1) {
+ cause = "socket";
+ continue;
+ }
+
+ opt = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ &opt, sizeof(opt)) == -1)
+ fatal("listener setsockopt(SO_REUSEADDR)");
+
+ if (bind(s, res->ai_addr, res->ai_addrlen) == -1) {
+ serrno = errno;
+ cause = "bind";
+ close(s);
+ errno = serrno;
+ continue;
+ }
+
+ set_nonblocking(s);
+ if (listen(s, BACKLOG) == -1)
+ fatal("listen");
+
+ l = xcalloc(1, sizeof(*l), "pop3_listen");
+ l->sock = s;
+ if (strcmp(port, "pop3s") == 0)
+ l->flags |= POP3S;
+
+ event_set(&l->ev, s, EV_READ|EV_PERSIST, pop3_accept, l);
+ event_add(&l->ev, NULL);
+ evtimer_set(&l->pause, pop3_pause, l);
+ }
+
+ if (l == NULL)
+ errx(1, "%s", cause);
+
+ freeaddrinfo(res0);
+}
+
+static void
+pop3_accept(int fd, short events, void *arg)
+{
+ struct sockaddr_storage ss;
+ struct listener *l = arg;
+ struct timeval timeout = {1, 0};
+ socklen_t len;
+ int s;
+
+ len = sizeof(ss);
+ s = accept(fd, (struct sockaddr *)&ss, &len);
+ if (s == -1) {
+ switch (errno) {
+ case EINTR:
+ case EWOULDBLOCK:
+ case ECONNABORTED:
+ return;
+ case EMFILE:
+ case ENFILE:
+ event_del(&l->ev);
+ evtimer_add(&l->pause, &timeout);
+ return;
+ default:
+ fatalx("accept");
+ }
+ }
+
+ set_nonblocking(s);
+ l->ss = ss;
+ session_init(l, s);
+}
+
+static void
+pop3_pause(int fd, short events, void *arg)
+{
+ struct listener *l = arg;
+
+ event_add(&l->ev, NULL);
+}
+
+static void
+pop3d_imsgev(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ extern struct session_tree sessions;
+ struct session key, *r;
+
+ switch (code) {
+ case IMSGEV_IMSG:
+ key.id = imsg->hdr.peerid;
+ r = SPLAY_FIND(session_tree, &sessions, &key);
+ if (r == NULL) {
+ logit(LOG_INFO, "%u: session not found", key.id);
+ fatalx("pop3e: session lost");
+ }
+ switch (imsg->hdr.type) {
+ case IMSG_AUTH:
+ auth_response(r, imsg->fd);
+ break;
+ default:
+ logit(LOG_DEBUG, "%s: unexpected imsg %d",
+ __func__, imsg->hdr.type);
+ break;
+ }
+ break;
+ case IMSGEV_EREAD:
+ case IMSGEV_EWRITE:
+ case IMSGEV_EIMSG:
+ fatal("pop3e: imsgev read/write error");
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ break;
+ }
+}
+
+static void
+auth_response(struct session *s, int fd)
+{
+ if (fd == -1) {
+ session_reply(s, "%s", "-ERR auth failed");
+ io_set_write(&s->io);
+ session_close(s, 1);
+ return;
+ }
+
+ session_imsgev_init(s, fd);
+}
+
+static void
+needfd(struct imsgev *iev)
+{
+ /* XXX can anything be done to handle fd exhaustion? */
+ fatalx("pop3e needs an fd");
+}
+
+static void
+sig_handler(int sig, short event, void *arg)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ event_loopexit(NULL);
+ }
+}
+