From b26088b76d0d10e1fa10be5408e84babe83ecb7e Mon Sep 17 00:00:00 2001 From: chris mikkelson Date: Tue, 24 Mar 2009 21:36:30 -0500 Subject: [PATCH] High performance SMTP daemon, suitable for spamtraps, sinkholes, honeypots, etc. --- conn_pool.c | 184 +++++++++++++++++++++++++++++++ conn_queue.c | 176 ++++++++++++++++++++++++++++++ conn_queue.h | 31 ++++++ io.c | 100 +++++++++++++++++ responses.inc | 44 ++++++++ smtp.c | 291 +++++++++++++++++++++++++++++++++++++++++++++++++ smtpsink-int.h | 17 +++ smtpsink.c | 74 +++++++++++++ smtpsink.h | 36 ++++++ 9 files changed, 953 insertions(+) create mode 100644 conn_pool.c create mode 100644 conn_queue.c create mode 100644 conn_queue.h create mode 100644 io.c create mode 100644 responses.inc create mode 100644 smtp.c create mode 100644 smtpsink-int.h create mode 100644 smtpsink.c create mode 100644 smtpsink.h diff --git a/conn_pool.c b/conn_pool.c new file mode 100644 index 0000000..84976bf --- /dev/null +++ b/conn_pool.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#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); + } +} diff --git a/conn_queue.c b/conn_queue.c new file mode 100644 index 0000000..fc5e5f2 --- /dev/null +++ b/conn_queue.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include + +#include +#include +#include +#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); +} diff --git a/conn_queue.h b/conn_queue.h new file mode 100644 index 0000000..4b25885 --- /dev/null +++ b/conn_queue.h @@ -0,0 +1,31 @@ +#ifdef XREF +#include +#include +#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); + +*/ diff --git a/io.c b/io.c new file mode 100644 index 0000000..5fcd673 --- /dev/null +++ b/io.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +#include +#include +#include +#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); +} + diff --git a/responses.inc b/responses.inc new file mode 100644 index 0000000..b3b1ed1 --- /dev/null +++ b/responses.inc @@ -0,0 +1,44 @@ +/* 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"; diff --git a/smtp.c b/smtp.c new file mode 100644 index 0000000..86b28fc --- /dev/null +++ b/smtp.c @@ -0,0 +1,291 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#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); +} + diff --git a/smtpsink-int.h b/smtpsink-int.h new file mode 100644 index 0000000..992af30 --- /dev/null +++ b/smtpsink-int.h @@ -0,0 +1,17 @@ +/* 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; diff --git a/smtpsink.c b/smtpsink.c new file mode 100644 index 0000000..4cc64fb --- /dev/null +++ b/smtpsink.c @@ -0,0 +1,74 @@ +/* #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(); +} diff --git a/smtpsink.h b/smtpsink.h new file mode 100644 index 0000000..3c6d3ee --- /dev/null +++ b/smtpsink.h @@ -0,0 +1,36 @@ +#ifdef XREF +#include +#include +#include +#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 *, ...); -- 2.50.1