CFLAGS=-g -Wall -I/usr/local/include
-SMTPSINK_OBJS=conn_pool.o conn_queue.o io.o smtp.o smtpsink.o
+SMTPSINK_OBJS=conn_pool.o conn_queue.o io.o smtp.o module.o smtpsink.o
default: smtpsink
smtp.o: smtp.c smtpsink.h conn_queue.h smtpsink-int.h responses.inc
cc $(CFLAGS) -c smtp.c
+module.o: module.c smtpsink.h
+ cc $(CFLAGS) -c module.c
+
smtpsink.o: smtpsink.c smtpsink.h conn_queue.h smtpsink-int.h
clean:
c->peer = addr;
c->fd = fd;
io_initconn(c);
+ module_initconn(c);
greeting(c);
c->state = SMTP_GREET;
if (c->wdata.s) {
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <err.h>
+
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <event.h>
+#include "smtpsink.h"
+
+static int nmod = 0;
+static struct smtpsink_module **modlist = 0;
+
+/* modulespec is a string formatted as:
+
+ "/path/to/module,options"
+
+ parsing of "options" is module dependent
+*/
+void
+module_init(char *modulespec)
+{
+ char modpath[1024];
+ char *file, *t;
+ struct smtpsink_module *mod;
+ void *dlmod;
+
+ t = strchr(modulespec, ',');
+ if (!t) file = modulespec;
+ else {
+ if (t-modulespec - 1 > sizeof(modpath))
+ errx(1, "module path too long: %s\n", modulespec);
+ strncpy(modpath, modulespec, t - modulespec);
+ file = modpath;
+ t++;
+ }
+
+ dlmod = dlopen(file, RTLD_NOW);
+ if (!dlmod)
+ errx(1, "could not load %s: %s\n", file, dlerror());
+
+ mod = (void *)dlsym(dlmod, "ss_module");
+ if (!mod)
+ errx(1, "module %s invalid: %s\n", file, dlerror());
+
+ mod->mod_index = nmod;
+ mod->module_init(t);
+
+ modlist = realloc(modlist, nmod + 1);
+ if (!modlist)
+ errx(1, "out of memory\n");
+
+ modlist[nmod++] = mod;
+}
+
+void
+module_hup(void)
+{
+ int i;
+ for (i = 0; i < nmod; i++)
+ if (modlist[i]->module_hup)
+ modlist[i]->module_hup();
+}
+
+void
+module_initconn(struct conn *c)
+{
+ if (!c->priv_data) {
+ c->priv_data = calloc(1, nmod * sizeof(void *));
+ }
+}
+
+void
+module_resetconn(struct conn *c)
+{
+ int i;
+ for (i = 0; i < nmod; i++)
+ if (modlist[i]->reset_cdata)
+ modlist[i]->reset_cdata(c);
+}
+
+int
+module_helo(struct conn *c, char *cmd, int len, int argoff)
+{
+ int i, ret;
+ for (i = 0; i < nmod; i++) {
+ if (modlist[i]->hook_helo) {
+ ret = modlist[i]->hook_helo(c,cmd,len,argoff);
+ if ((ret & 0x0F) != SSM_PASS) return ret;
+ }
+ }
+ return SSM_ACCEPT;
+}
+
+
+int
+module_mail(struct conn *c, char *cmd, int len, int argoff)
+{
+ int i, ret;
+ for (i = 0; i < nmod; i++) {
+ if (modlist[i]->hook_mail) {
+ ret = modlist[i]->hook_mail(c,cmd,len,argoff);
+ if ((ret & 0x0F) != SSM_PASS) return ret;
+ }
+ }
+ return SSM_ACCEPT;
+}
+
+int
+module_rcpt(struct conn *c, char *cmd, int len, int argoff)
+{
+ int i, ret;
+ for (i = 0; i < nmod; i++) {
+ if (modlist[i]->hook_rcpt) {
+ ret = modlist[i]->hook_rcpt(c,cmd,len,argoff);
+ if ((ret & 0x0F) != SSM_PASS) return ret;
+ }
+ }
+ return SSM_ACCEPT;
+}
+
+void
+module_data(struct conn *c, char *data, int len)
+{
+ int i;
+ for (i = 0; i < nmod; i++) {
+ if (modlist[i]->hook_enddata) {
+ modlist[i]->hook_data(c, data, len);
+ }
+ }
+}
+
+int
+module_enddata(struct conn *c)
+{
+ int i, ret;
+ for (i = 0; i < nmod; i++) {
+ if (modlist[i]->hook_enddata) {
+ ret = modlist[i]->hook_enddata(c);
+ if ((ret & 0x0F) != SSM_PASS) return ret;
+ }
+ }
+ return SSM_ACCEPT;
+}
+
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+static int nmod = 0;
+static smtpsink_module **modlist = 0;
+
+/* modulespec is a string formatted as:
+
+ "/path/to/module,options"
+
+ parsing of "options" is module dependent
+*/
+void
+module_init(char *modulespec)
+{
+ char modpath[1024];
+ char *file, *t;
+ smtpsink_module *mod;
+ void *dlmod;
+
+ t = strchr(modulespec, ',');
+ if (!t) file = modulespec;
+ else {
+ if (t-modulespec - 1 > sizeof(modpath))
+ errx(1, "module path too long: %s\n", modulespec);
+ strncpy(modpath, modulespec, t - modulespec);
+ file = modpath;
+ }
+
+ dlmod = dlopen(file, RTLD_NOW);
+ if (!dlmod)
+ errx(1, "could not load %s: %s\n", file, dlerror());
+
+ mod = (void *)dlsym(dlmod, "smtpsink_module");
+ if (!mod)
+ errx(1, "module %s invalid: %s\n", file, dlerror());
+
+ mod->module_init();
+
+ modlist = realloc(modlist, nmod + 1);
+ if (!modlist)
+ errx(1, "out of memory\n");
+
+ modlist[nmod++] = mod;
+}
+
extern struct conn_queue io_queue, smtp_queue;
extern char *banner_hostname;
+
+/* module hooks */
+extern void module_init(char *);
+extern void module_hup(void);
+extern void module_initconn(struct conn *);
+extern void module_resetconn(struct conn *);
+extern int module_helo(struct conn *, char *, int, int);
+extern int module_mail(struct conn *, char *, int, int);
+extern int module_rcpt(struct conn *, char *, int, int);
+extern void module_data(struct conn *, char *, int);
+extern int module_enddata(struct conn *);
pthread_t sl;
int tstart = 1;
- while ((c=getopt(argc, argv, "fp:c:l:u:g:j:h:")) != -1) {
+ while ((c=getopt(argc, argv, "fp:c:l:u:g:j:h:M:")) != -1) {
switch(c) {
case 'f': daemonize = 0; break;
case 'p': pidfd = open(optarg, O_CREAT|O_TRUNC|O_WRONLY, 0600);
break;
case 'h': banner_hostname = optarg; break;
case 'M':
- /* WRITEME: module init */
+ module_init(optarg);
+ break;
case '?':
default:
usage(argv[0]);
} rdata, wdata;
short event;
struct event re, we;
- void *priv_data;
+ void **priv_data;
STAILQ_ENTRY(conn) c_list;
};
int respond(struct conn *, const char *, ...);
+
+/* return values for module methods */
+/* SSM_PASS: move on to next module */
+/* SSM_ACCEPT: stop processing, accept command */
+/* SSM_REJECT: stop processing, reject command */
+/* SSM_MSG | SSM_ACCEPT: stop processing, accept command, module responded */
+/* SSM_MSG | SSM_REJECT: stop processing, reject command, module responded */
+#define SSM_PASS 0
+#define SSM_ACCEPT 1
+#define SSM_REJECT 2
+#define SSM_MSG (1 << 8)
+
+struct smtpsink_module {
+ void (*module_init)(char *);
+ void (*module_hup)(void);
+
+ /* manage connection private data */
+ void (*reset_cdata)(struct conn *);
+
+ /* smtp command hooks */
+ int (*hook_helo)(struct conn *, char *, int, int);
+ int (*hook_mail)(struct conn *, char *, int, int);
+ int (*hook_rcpt)(struct conn *, char *, int, int);
+
+ /* data processing hooks */
+ void (*hook_data)(struct conn *, char *, int);
+ int (*hook_enddata)(struct conn *);
+
+ /* module index (filled in by module_init) */
+ unsigned mod_index;
+};