honeypots, etc.
--- /dev/null
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <assert.h>
+#include <err.h>
+
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <event.h>
+#include "smtpsink.h"
+#include "conn_queue.h"
+#include "smtpsink-int.h"
+
+/* struct conn_pool should be private */
+static struct conn_pool {
+ pthread_mutex_t cpmtx;
+ pthread_cond_t ac;
+ int count, limit;
+ STAILQ_HEAD(, conn) c_pool;
+} cp;
+
+void
+conn_pool_init(int limit)
+{
+ pthread_mutex_init(&cp.cpmtx, 0);
+ pthread_cond_init(&cp.ac, 0);
+ cp.limit = limit;
+ cp.count = 0;
+ STAILQ_INIT(&cp.c_pool);
+}
+
+static struct conn *
+new_conn(void)
+{
+ struct conn *c;
+ if (pthread_mutex_lock(&cp.cpmtx)) assert(0);
+ if (STAILQ_EMPTY(&cp.c_pool)) {
+ c = malloc(sizeof(struct conn));
+ if (c) cp.count++;
+ pthread_mutex_unlock(&cp.cpmtx);
+ if (!c) return c;
+ bzero(c, sizeof(struct conn));
+ c->magic = CONN_MAGIC;
+ } else {
+ c = STAILQ_FIRST(&cp.c_pool);
+ STAILQ_REMOVE_HEAD(&cp.c_pool, c_list);
+ cp.count++;
+ pthread_mutex_unlock(&cp.cpmtx);
+ }
+ return c;
+}
+
+void
+close_conn(struct conn *c)
+{
+ close(c->fd);
+ event_del(&c->re);
+ event_del(&c->we);
+ if (c->wdata.s) {
+ free(c->wdata.s);
+ bzero(&c->wdata, sizeof(c->wdata));
+ }
+ if (c->rdata.s) {
+ free(c->rdata.s);
+ bzero(&c->rdata, sizeof(c->rdata));
+ }
+ /* call close hook */
+ if (pthread_mutex_lock(&cp.cpmtx)) assert(0);
+ cp.count --;
+ STAILQ_INSERT_HEAD(&cp.c_pool, c, c_list);
+ pthread_mutex_unlock(&cp.cpmtx);
+}
+
+static void
+wait_conn(void)
+{
+ if (pthread_mutex_lock(&cp.cpmtx)) assert(0);
+ if (cp.count >= cp.limit)
+ pthread_cond_wait(&cp.ac, &cp.cpmtx);
+ pthread_mutex_unlock(&cp.cpmtx);
+}
+
+static pthread_cond_t lc;
+static pthread_mutex_t lmtx;
+static int nl=0;
+
+static void *
+listener_loop(void *data)
+{
+ int sock = *(int *)data;
+ struct sockaddr_in addr;
+ int addrlen, fd;
+ struct linger l = {1,0};
+
+ struct conn *c;
+
+ /* wait for starting gun. thread must not run
+ * before all queues are initialized.
+ */
+ if (pthread_mutex_lock(&lmtx)) assert(0);
+ if (pthread_cond_wait(&lc)) assert(0);
+ pthread_mutex_unlock(&lmtx);
+
+ while (1) {
+ wait_conn();
+ addrlen = sizeof(addr);
+ fd = accept(sock, (struct sockaddr *)&addr, &addrlen);
+ if (fd < 0) continue;
+ if (!(c = new_conn())) {
+ close (fd);
+ continue;
+ }
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
+ c->peer = addr;
+ c->fd = fd;
+ io_initconn(c);
+ respond(c, greeting);
+ if (c->wdata.s) {
+ c->event = EV_WRITE;
+ } else {
+ c->event = EV_READ;
+ }
+ io_queue.enqueue(&io_queue, c);
+ }
+}
+
+void
+new_listener(char *spec)
+{
+ int *sock;
+ int one = 1, port = 25;
+ char *host = spec;
+ char *t = strchr(spec, ':');
+ struct sockaddr_in l_addr;
+ pthread_t thread;
+
+ if (t) {
+ *t++ = 0;
+ port = atoi(t);
+ if (port < 0) errx(1, "invalid port number: %s", t);
+ }
+
+ l_addr.sin_family = AF_INET;
+ l_addr.sin_port = htons(port);
+
+ if (!inet_aton(host, &l_addr.sin_addr)) {
+ /* do the gethostbyname dance */
+ }
+
+ sock = malloc(sizeof(int));
+ if (!sock) errx(1, "out of memory");
+
+ *sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (*sock < 0) err(1, "socket");
+ setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+
+ if (bind(*sock, (struct sockaddr *)&l_addr, sizeof l_addr) < 0)
+ err(1, "bind");
+
+ if (listen(*sock, -1) < 0) err(1, "listen");
+
+ if (!nl) {
+ if (pthread_cond_init(&lc, 0))
+ err(1,"pthread_cond_init");
+ if (pthread_mutex_init(&lmtx, 0))
+ err(1,"pthread_mutex_init");
+ }
+ nl++;
+ if (pthread_create(&thread, 0, listener_loop, (void *)sock))
+ err(1, "pthread_create");
+}
+
+void
+start_listeners(void)
+{
+ int i;
+ for (i = 0; i < nl; i++) {
+ pthread_cond_signal(&lc);
+ }
+}
--- /dev/null
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <event.h>
+#include "smtpsink.h"
+
+#include "conn_queue.h"
+
+struct event_qstate {
+ int fds[2];
+ struct event e;
+};
+
+struct tpool_qstate {
+ pthread_cond_t cond;
+ int qlen, tstart;
+};
+
+static void event_enqueue(struct conn_queue *, struct conn *);
+static void event_run(struct conn_queue *);
+
+static void tpool_enqueue(struct conn_queue *, struct conn *);
+static void tpool_run(struct conn_queue *);
+
+static void *
+event_priv_init(void)
+{
+ struct event_qstate *ep;
+ ep = (struct event_qstate *)malloc(sizeof(struct event_qstate));
+ if (!ep) return 0;
+ if (pipe(ep->fds) < 0) {
+ free(ep);
+ return 0;
+ }
+ return (void *)ep;
+}
+
+static void *
+tpool_priv_init(void)
+{
+ struct tpool_qstate *tp;
+ tp = (struct tpool_qstate *)malloc(sizeof(struct tpool_qstate));
+ if (!tp) return 0;
+ if (!pthread_cond_init(&tp->cond, 0)) {
+ free(tp);
+ return 0;
+ }
+ tp->tstart = 1;
+ return (void *)tp;
+}
+
+int
+conn_queue_init(struct conn_queue *cq, void (*handler)(struct conn *), int type)
+{
+
+ STAILQ_INIT(&cq->queue);
+ if (pthread_mutex_init(&cq->mtx, 0)) return 0;
+ cq->type = type;
+ cq->handler = handler;
+ cq->priv = 0;
+
+ switch(type) {
+ case Q_EVENT:
+ cq->enqueue = event_enqueue;
+ cq->run = event_run;
+ cq->priv = event_priv_init();
+ break;
+ case Q_TPOOL:
+ cq->enqueue = tpool_enqueue;
+ cq->run = tpool_run;
+ cq->priv = tpool_priv_init();
+ break;
+ }
+ if (cq->priv) return 1;
+ else return 0;
+}
+
+int
+conn_queue_set_tpool_size(struct conn_queue *cq, int start)
+{
+ struct tpool_qstate *tp;
+ if (cq->type != Q_TPOOL) return 0;
+ if (start <= 0) return 0;
+ if (!cq->priv) return 0;
+ tp = (struct tpool_qstate *)cq->priv;
+ tp->tstart = start;
+ return 1;
+}
+
+static void
+event_enqueue(struct conn_queue *cq, struct conn *c)
+{
+ struct event_qstate *ep = (struct event_qstate *)cq->priv;
+
+ if (pthread_mutex_lock(&cq->mtx)) assert(0);
+ STAILQ_INSERT_TAIL(&cq->queue, c, c_list);
+ pthread_mutex_unlock(&cq->mtx);
+
+ write(ep->fds[1],"",1);
+}
+
+/* event_run prepares and adds the event with callback event_callback,
+ which does the actual callout to handler */
+static void
+event_callback(int fd, short event, void *data)
+{
+ struct conn_queue *cq = (struct conn_queue *)data;
+ struct event_qstate *ep = (struct event_qstate *)cq->priv;
+ struct conn *c;
+ char ch;
+
+ assert(cq->type == Q_EVENT);
+ read(ep->fds[0], &ch, 1);
+ if (pthread_mutex_lock(&cq->mtx)) assert(0);
+ assert (!STAILQ_EMPTY(&cq->queue));
+ c = STAILQ_FIRST(&cq->queue);
+ STAILQ_REMOVE_HEAD(&cq->queue, c_list);
+ pthread_mutex_unlock(&cq->mtx);
+
+ cq->handler(c);
+}
+
+static void
+event_run(struct conn_queue *cq)
+{
+ struct event_qstate *ep = (struct event_qstate *)cq->priv;
+ event_set(&ep->e, ep->fds[0], EV_READ|EV_PERSIST,
+ event_callback, (void *)cq);
+ event_add(&ep->e, 0);
+}
+
+static void
+tpool_enqueue(struct conn_queue *cq, struct conn *c)
+{
+ struct tpool_qstate *tp = (struct tpool_qstate *)cq->priv;
+
+ if (pthread_mutex_lock(&cq->mtx)) assert(0);
+ STAILQ_INSERT_TAIL(&cq->queue, c, c_list);
+ pthread_mutex_unlock(&cq->mtx);
+ pthread_cond_signal(&tp->cond);
+}
+
+/* tpool_run starts a thread running tpool_mainloop(), passing
+ it a pointer to the queue structure. */
+static void *
+tpool_mainloop(void *arg)
+{
+ struct conn_queue *cq = (struct conn_queue *)arg;
+ struct tpool_qstate *tp = (struct tpool_qstate *)cq->priv;
+
+ struct conn *c;
+
+ while (1) {
+ if (pthread_mutex_lock(&cq->mtx)) assert(0);
+ while (STAILQ_EMPTY(&cq->queue))
+ pthread_cond_wait(&tp->cond, &cq->mtx);
+ c = STAILQ_FIRST(&cq->queue);
+ STAILQ_REMOVE_HEAD(&cq->queue, c_list);
+ pthread_mutex_unlock(&cq->mtx);
+
+ cq->handler(c);
+ }
+ return arg;
+}
+
+static void
+tpool_run(struct conn_queue *cq)
+{
+ pthread_t pt;
+
+ if (pthread_create(&pt, 0, tpool_mainloop, (void *)cq)) assert(0);
+}
--- /dev/null
+#ifdef XREF
+#include <sys/queue.h>
+#include <pthread.h>
+#endif
+#define Q_EVENT 1
+#define Q_TPOOL 2
+
+struct conn_queue {
+ void (*handler)(struct conn *);
+ void (*run)(struct conn_queue *);
+ void (*enqueue)(struct conn_queue *, struct conn *);
+ pthread_mutex_t mtx;
+ STAILQ_HEAD(,conn) queue;
+ int type;
+ void *priv;
+};
+
+int conn_queue_init(struct conn_queue *, void (*)(struct conn *), int);
+int conn_queue_set_tpool_size(struct conn_queue *, int);
+/*
+ To setup:
+
+ struct conn_queue cq;
+ conn_queue_init(&cq, do_work, Q_EVENT);
+ cq->run(cq);
+
+ client calls:
+
+ cq->enqueue(cq, c);
+
+*/
--- /dev/null
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <event.h>
+#include "smtpsink.h"
+#include "conn_queue.h"
+#include "smtpsink-int.h"
+
+struct conn_queue io_queue;
+static struct timeval timeout = {20, 0};
+
+static void
+handle_ioreq(struct conn *c)
+{
+ if (c->event | EV_READ) {
+ event_add(&c->re, &timeout);
+ }
+ if (c->event | EV_WRITE) {
+ event_add(&c->we, &timeout);
+ }
+ c->event = 0;
+}
+
+void
+io_init(void)
+{
+ event_init();
+ conn_queue_init(&io_queue, handle_ioreq, Q_EVENT);
+ io_queue.run(&io_queue);
+}
+
+static void
+read_cb(int fd, short event, void *data)
+{
+ struct conn *c = (struct conn *)data;
+ switch (event) {
+ case EV_READ:
+ c->event = EV_READ;
+ smtp_queue.enqueue(&smtp_queue, c);
+ break;
+ case EV_TIMEOUT:
+ respond(c, readtimeoutresp);
+ if (c->wdata.s) {
+ c->state = SMTP_CLOSED;
+ event_add(&c->we, &timeout);
+ }
+ else
+ close_conn(c);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void
+write_cb(int fd, short event, void *data)
+{
+ struct conn *c = (struct conn *)data;
+ int nw;
+
+ switch (event) {
+ case EV_WRITE:
+ nw = write(c->fd, c->wdata.s + c->wdata.off,
+ c->wdata.len - c->wdata.off);
+ if (nw < 0) {
+ close_conn(c);
+ return;
+ }
+ c->wdata.off += nw;
+ if (c->wdata.off < c->wdata.len) {
+ event_add(&c->we, &timeout);
+ } else {
+ free(c->wdata.s);
+ bzero(&c->wdata, sizeof(c->wdata));
+ if (c->state == SMTP_CLOSED) {
+ close_conn(c);
+ } else {
+ smtp_queue.enqueue(&smtp_queue, c);
+ }
+ }
+ break;
+ case EV_TIMEOUT:
+ close_conn(c);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void
+conn_startio(struct conn *c)
+{
+ event_set(&c->re, c->fd, EV_READ, read_cb, (void *)c);
+ event_set(&c->re, c->fd, EV_WRITE, write_cb, (void *)c);
+}
+
--- /dev/null
+/* DEFAULT responses and error messages */
+
+char *banner_hostname = "mx";
+/* greeting - %s is the banner hostname */
+static char *banner = "220 %s ESMTP\r\n";
+static char *quit_ok = "221 %s\r\n";
+
+/* HELO/EHLO */
+static char *helo_ok = "250 %s\r\n";
+static char *ehlo_ok = "250-%s\r\n"
+ "250-PIPELINING\r\n"
+ "250 8BITMIME\r\n";
+static char *helo_reject = "550 HELO command rejected\r\n";
+
+/* MAIL command */
+static char *mail_ok = "250 ok\r\n";
+static char *mail_reject = "553 envelope sender rejected.\r\n";
+
+/* RCPT command */
+static char *rcpt_ok = "250 ok\r\n";
+static char *rcpt_reject = "553 recipient rejected.\r\n";
+
+/* DATA command */
+static char *data_ok = "354 go ahead\r\n";
+
+/* End of DATA (timestamp, random integer) */
+static char *enddata_ok = "250 ok %d qp %d\r\n";
+static char *enddata_reject = "554 message rejected\r\n";
+
+/* Other commands */
+/* rset */
+static char *rset_ok = "250 flushed\r\n";
+/* help */
+static char *help_ok = "214 netqmail home page: http://qmail.org/netqmail\r\n";
+/* noop */
+static char *noop_ok = "250 ok\r\n";
+/* vrfy */
+static char *vrfy_ok = "252 send some mail, i'll try my best\r\n";
+
+static char *err_wantmail = "503 MAIL first (#5.5.1)\r\n";
+static char *err_wantrcpt = "503 RCPT first (#5.5.1)\r\n";
+static char *err_timeout = "451 timeout (#4.4.2)\r\n";
+static char *err_toolong = "555 syntax error (#5.5.4)\r\n";
+static char *err_unimpl = "502 unimplemented (#5.5.1)\r\n";
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <event.h>
+#include "smtpsink.h"
+#include "conn_queue.h"
+#include "smtpsink-int.h"
+
+struct conn_queue smtp_queue;
+static void handle_smtp(struct conn *);
+
+void
+smtp_init(int tstart)
+{
+ int i;
+ conn_queue_init(&smtp_queue, handle_smtp, Q_TPOOL);
+ conn_queue_set_tpool_size(&smtp_queue, tstart);
+ for (i = 0; i < tstart; i++) {
+ smtp_queue.run(&smtp_queue);
+ }
+}
+
+int
+respond(struct conn *c, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[2048];
+ int ret, nw;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+ if (ret < 0) assert(0);
+ nw = write(c->fd, buf, ret);
+ if (nw < 0) {
+ c->state = SMTP_CLOSED;
+ return 0;
+ }
+ if (nw < ret) {
+ if (!(c->wdata.s = malloc(ret - nw))) {
+ c->state = SMTP_CLOSED;
+ return 0;
+ }
+ memcpy(c->wdata.s, buf + nw, ret - nw);
+ c->wdata.off = 0;
+ c->wdata.len = ret - nw;
+ return 0;
+ }
+ return 1;
+}
+
+#include "responses.inc"
+
+static int
+smtpd_helo(struct conn *c, char *cmd, int len, int argoff)
+{
+ const char *resp = helo_ok;
+ if (cmd[0] == 'e' || cmd[0] == 'E')
+ resp = ehlo_ok;
+ }
+ /* if (c->state == SMTP_GREET) {
+ call-hook;
+ } */
+ return respond(c, resp, banner_hostname);
+}
+
+static int
+smtpd_mail(struct conn *c, char *cmd, int len, int argoff)
+{
+ /* call hook */
+ c->state = SMTP_MAIL;
+ return respond(c, mail_ok);
+}
+
+static int
+smtpd_rcpt(struct conn *c, char *cmd, int len, int argoff)
+{
+ if (c->state == SMTP_MAIL || c->state == SMTP_RCPT) {
+ c->state = SMTP_RCPT;
+ return respond(c, rcpt_ok);
+ }
+ return respond(c, err_wantmail);
+}
+
+static int
+smtpd_data(struct conn *c, char *cmd, int len, int argoff)
+{
+ if (c->state != SMTP_RCPT)
+ return respond(c, err_wantrcpt);
+ c->state = SMTP_DATA;
+ return respond(c, data_ok);
+}
+
+static int
+smtpd_enddata(struct conn *c)
+{
+ int now = time(0);
+ /* call hook */
+ c->state = SMTP_RSET;
+ return respond(c, enddata_ok, now, now % 38201);
+}
+
+static int
+smtpd_rset(struct conn *c, char *cmd, int len, int argoff)
+{
+ c->state = SMTP_RSET;
+ return respond(c, rset_ok);
+}
+
+static int
+smtpd_noop(struct conn *c, char *cmd, int len, int argoff)
+{
+ return respond(c, noop_ok);
+}
+
+static int
+smtpd_vrfy(struct conn *c, char *cmd, int len, int argoff)
+{
+ return respond(c, vrfy_ok);
+}
+
+static int
+smtpd_help(struct conn *c, char *cmd, int len, int argoff)
+{
+ return respond(c, help_ok);
+}
+
+static int
+smtpd_quit(struct conn *c, char *cmd, int len, int argoff)
+{
+ c->state = SMTP_CLOSED;
+ return respond(c, quit_ok);
+}
+
+static int
+smtpd_unimpl(struct conn *c, char *cmd, int len, int argoff)
+{
+ return respond(c, err_unimpl);
+}
+
+static struct cmd {
+ char *c;
+ int (*action)(struct conn *, char *, int, int);
+} commands[] = {
+ {"helo", smtpd_helo},
+ {"ehlo", smtpd_helo},
+ {"mail", smtpd_mail},
+ {"rcpt", smtpd_rcpt},
+ {"data", smtpd_data},
+ {"rset", smtpd_rset},
+ {"noop", smtpd_noop},
+ {"vrfy", smtpd_vrfy},
+ {"help", smtpd_help},
+ {"quit", smtpd_quit},
+ {0,smtpd_unimpl},
+};
+
+static int
+do_command(struct conn *c, char *cmd, int len)
+{
+ struct cmd *e;
+ int l;
+
+ for (e=&commands[0]; e->c; e++) {
+ l = strlen(e->c);
+ if (len < l) continue;
+ if (!strncasecmp(e->c,cmd,l) &&
+ (!cmd[l] || isspace(cmd[l]))) {
+ while (l < len && isspace(cmd[l])) l++;
+ return e->action(c, cmd, len, l);
+ }
+ }
+ return e->action(c, cmd, len, 0);
+}
+
+static int
+do_smtp(struct conn *c, char **buf, int *len)
+{
+ if (c->state == SMTP_DATA) {
+ char *s;
+ for (s = *buf; s < *buf + *len; s++) {
+ switch(c->dstate) {
+ case 0: if (*s == '\r') c->dstate ++;
+ continue;
+ case 1: if (*s == '\n') c->dstate ++;
+ else c->dstate = 0;
+ continue;
+ case 2: if (*s == '.') c->dstate ++;
+ else if (*s == '\r') c->dstate = 1;
+ else c->dstate = 0;
+ continue;
+ case 3: if (*s == '\r') c->dstate ++;
+ else c->dstate = 0;
+ continue;
+ case 4: if (*s == '\n') {
+ c->dstate = 5;
+ break;
+ }
+ c->dstate = 0;
+ continue;
+ }
+ break;
+ }
+ /* do_data(c, *buf, s - *buf); */
+ *len -= s - *buf;
+ *buf = s;
+ if (c->dstate == 5) return smtpd_enddata(c);
+ return 1;
+ } else {
+ char *t = strnstr(*buf, "\r\n", *len);
+ char *cmd = *buf;
+ int clen = t - *buf;
+
+ if (!t) return 0;
+ t += 2;
+ *len -= t - *buf;
+ *buf = t;
+
+ return do_command(c, cmd, clen);
+ }
+}
+
+static void
+handle_smtp(struct conn *c)
+{
+ char rbuf[2048], *s;
+ int len;
+
+ /* WRITEME: how to detect "line too long" error ? */
+
+ if (c->rdata.s) {
+ /* XXX -- c->rdata.off unused */
+ memcpy(rbuf, c->rdata.s, c->rdata.len);
+ len = c->rdata.len;
+ free(c->rdata.s);
+ bzero(&c->rdata, sizeof(c->rdata));
+ }
+
+ if (len < sizeof(rbuf) && c->event == EV_READ) {
+ int nr = read(c->fd, rbuf + len, sizeof(rbuf) - len);
+ if (nr > 0) {
+ len += nr;
+ }
+ else {
+ if (nr == 0)
+ close_conn(c);
+ else if (respond(c, readerrresp))
+ close_conn(c);
+ else {
+ c->event = EV_WRITE;
+ io_queue.enqueue(&io_queue, c);
+ }
+ return;
+ }
+ }
+
+ s = rbuf;
+ while (do_smtp(c, &s, &len));
+ if (len == sizeof(rbuf)) {
+ respond(c, err_toolong);
+ c->state = SMTP_CLOSED;
+ }
+ if (c->state == SMTP_CLOSED)
+ len = 0;
+ if (len > 0) {
+ if (!(c->rdata.s = malloc(len))) {
+ close_conn(c);
+ return;
+ }
+ c->rdata.len = len;
+ memcpy(c->rdata.s, s, len);
+ }
+ if (c->wdata.s) {
+ c->event = EV_WRITE;
+ io_queue.enqueue(&io_queue, c);
+ return;
+ }
+ if (c->state == SMTP_CLOSED) {
+ close_conn(c);
+ return;
+ }
+ c->event = EV_READ;
+ io_queue.enqueue(&io_queue, c);
+}
+
--- /dev/null
+/* internal functions and interfaces */
+
+void conn_pool_init(int);
+void close_conn(struct conn *);
+void new_listener(char *);
+void start_listeners(void);
+
+void io_init(void);
+void io_initconn(struct conn *);
+
+int conn_queue_init(struct conn_queue *, void (*)(struct conn *), int);
+int conn_queue_set_tpool_size(struct conn_queue *, int);
+
+void smtp_init(int);
+
+extern struct conn_queue io_queue, smtp_queue;
+extern char *banner_hostname;
--- /dev/null
+/* #include system header files */
+#include "smtpsink.h"
+#include "queues.h"
+
+static void
+sig_exit(int fd, short event, void *arg)
+{
+ exit 0;
+}
+
+int
+main(char **argv, int argc)
+{
+ /* defaults */
+ int concurrency = 10000;
+ int defaultport = "0.0.0.0:25";
+
+ int daemonize = 1;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ struct group *gr;
+ struct passwd *pw;
+
+ int tstart = 1, tmax = 1, tspare = 1;
+
+ /* parse cmdline args */
+
+ if (daemonize) daemon(0,0);
+
+ if (pidfd >= 0) {
+ char pid[10];
+ snprintf(pid,sizeof(pid),"%d\n",getpid());
+ write (pidfd, pid, strlen(pid));
+ }
+
+ s_addr.sin_family=AF_INET;
+ s_addr.sin_port=htons(listen_port);
+ s_addr.sin_addr.s_addr = listen_ip;
+
+ lsock = socket(PF_INET, SOCK_STREAM, 0);
+ if (lsock < 0) err (1, "socket");
+ setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+
+ if (bind(lsock, (struct sockaddr *)&s_addr, sizeof s_addr) < 0)
+ err (1, "bind");
+
+ if (listen(lsock, -1) < 0) errx (1, "listen");
+
+ if (gid) {
+ if (setgid(gid) < 0) {
+ err(1, "setgid() failed");
+ }
+ }
+ if (uid) {
+ if (setuid(uid) < 0) {
+ err(1, "setuid() failed");
+ }
+ }
+
+ openlog("smtpsink",LOG_PID,LOG_MAIL);
+ signal(SIGPIPE,SIG_IGN);
+
+ event_init();
+ signal_set(&sigint, SIGINT, sigev_exit, 0);
+ signal_set(&sigterm, SIGINT, sigev_exit, 0);
+ signal_add(&sigint, 0);
+ signal_add(&sigterm, 0);
+
+ conn_pool_init(concurrency);
+ io_init();
+ smtp_init(tstart, tmax, tspare);
+ /* modules_init(); */
+ event_dispatch();
+}
--- /dev/null
+#ifdef XREF
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <event.h>
+#endif
+typedef enum {
+ SMTP_PREBANNER,
+ SMTP_GREET,
+ SMTP_HELO,
+ SMTP_MAIL,
+ SMTP_RCPT,
+ SMTP_DATA,
+ SMTP_RSET,
+ SMTP_FINISHED,
+ SMTP_CLOSED
+} smtp_state;
+
+#define CONN_MAGIC 0x053f7f65
+
+struct conn {
+ unsigned int magic;
+ int fd;
+ struct sockaddr_in peer;
+ smtp_state state;
+ int dstate;
+ struct iobuf {
+ char *s;
+ int len, off;
+ } rdata, wdata;
+ short event;
+ struct event re, we;
+ void *priv_data;
+ STAILQ_ENTRY(conn) c_list;
+};
+
+int respond(struct conn *, const char *, ...);