diff options
author | Gilles Chehade <gilles@poolp.org> | 2012-10-29 12:05:54 +0100 |
---|---|---|
committer | Gilles Chehade <gilles@poolp.org> | 2012-10-29 12:05:54 +0100 |
commit | b7d612103df7103d7cb500f7ea7507df408f5487 (patch) | |
tree | c0da37d94ff4e2452014e7a51ef7d4ec8d66113b /smtpd/queue_backend.c | |
parent | sync with current (diff) | |
download | OpenSMTPD-b7d612103df7103d7cb500f7ea7507df408f5487.tar.xz OpenSMTPD-b7d612103df7103d7cb500f7ea7507df408f5487.zip |
new layout
Diffstat (limited to 'smtpd/queue_backend.c')
-rw-r--r-- | smtpd/queue_backend.c | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/smtpd/queue_backend.c b/smtpd/queue_backend.c new file mode 100644 index 00000000..9de7e2ec --- /dev/null +++ b/smtpd/queue_backend.c @@ -0,0 +1,375 @@ +/* $OpenBSD: queue_backend.c,v 1.39 2012/10/09 13:39:00 eric Exp $ */ + +/* + * Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org> + * + * 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/queue.h> +#include <sys/tree.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <event.h> +#include <fcntl.h> +#include <imsg.h> +#include <inttypes.h> +#include <libgen.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "smtpd.h" +#include "log.h" + +static const char* envelope_validate(struct envelope *); + +extern struct queue_backend queue_backend_fs; + +int +queue_message_incoming_path(uint32_t msgid, char *buf, size_t len) +{ + return bsnprintf(buf, len, "%s/%08x", + PATH_INCOMING, + msgid); +} + +int +queue_envelope_incoming_path(uint64_t evpid, char *buf, size_t len) +{ + return bsnprintf(buf, len, "%s/%08x%s/%016" PRIx64, + PATH_INCOMING, + evpid_to_msgid(evpid), + PATH_ENVELOPES, + evpid); +} + +int +queue_message_incoming_delete(uint32_t msgid) +{ + char rootdir[MAXPATHLEN]; + + if (! queue_message_incoming_path(msgid, rootdir, sizeof(rootdir))) + fatal("queue_message_incoming_delete: snprintf"); + + if (rmtree(rootdir, 0) == -1) + fatal("queue_message_incoming_delete: rmtree"); + + return 1; +} + +struct queue_backend * +queue_backend_lookup(const char *name) +{ + if (!strcmp(name, "fs")) + return &queue_backend_fs; + + return (NULL); +} + +int +queue_message_create(uint32_t *msgid) +{ + return env->sc_queue->message(QOP_CREATE, msgid); +} + +int +queue_message_delete(uint32_t msgid) +{ + return env->sc_queue->message(QOP_DELETE, &msgid); +} + +int +queue_message_commit(uint32_t msgid) +{ + char msgpath[MAXPATHLEN]; + char tmppath[MAXPATHLEN]; + FILE *ifp = NULL; + FILE *ofp = NULL; + + queue_message_incoming_path(msgid, msgpath, sizeof msgpath); + strlcat(msgpath, PATH_MESSAGE, sizeof(msgpath)); + + if (env->sc_queue_flags & QUEUE_COMPRESS) { + + bsnprintf(tmppath, sizeof tmppath, "%s.comp", msgpath); + ifp = fopen(msgpath, "r"); + ofp = fopen(tmppath, "w+"); + if (ifp == NULL || ofp == NULL) + goto err; + if (! compress_file(ifp, ofp)) + goto err; + fclose(ifp); + fclose(ofp); + ifp = NULL; + ofp = NULL; + + if (rename(tmppath, msgpath) == -1) { + if (errno == ENOSPC) + return (0); + fatal("queue_message_commit: rename"); + } + } + + return env->sc_queue->message(QOP_COMMIT, &msgid); + +err: + if (ifp) + fclose(ifp); + if (ofp) + fclose(ofp); + return 0; +} + +int +queue_message_corrupt(uint32_t msgid) +{ + return env->sc_queue->message(QOP_CORRUPT, &msgid); +} + +int +queue_message_fd_r(uint32_t msgid) +{ + int fdin = -1, fdout = -1, fd = -1; + FILE *ifp = NULL; + FILE *ofp = NULL; + + if ((fdin = env->sc_queue->message(QOP_FD_R, &msgid)) == -1) + return (-1); + + if (env->sc_queue_flags & QUEUE_COMPRESS) { + if ((fdout = mktmpfile()) == -1) + goto err; + if ((fd = dup(fdout)) == -1) + goto err; + if ((ifp = fdopen(fdin, "r")) == NULL) + goto err; + fdin = fd; + fd = -1; + if ((ofp = fdopen(fdout, "w+")) == NULL) + goto err; + if (! uncompress_file(ifp, ofp)) + goto err; + fclose(ifp); + fclose(ofp); + lseek(fdin, SEEK_SET, 0); + } + + return (fdin); + +err: + if (fd != -1) + close(fd); + if (fdin != -1) + close(fdin); + if (fdout != -1) + close(fdout); + if (ifp) + fclose(ifp); + if (ofp) + fclose(ofp); + return -1; +} + +int +queue_message_fd_rw(uint32_t msgid) +{ + char msgpath[MAXPATHLEN]; + + queue_message_incoming_path(msgid, msgpath, sizeof msgpath); + strlcat(msgpath, PATH_MESSAGE, sizeof(msgpath)); + + return open(msgpath, O_RDWR | O_CREAT | O_EXCL, 0600); +} + +static int +queue_envelope_dump_buffer(struct envelope *ep, char *evpbuf, size_t evpbufsize) +{ + char evpbufcom[sizeof(struct envelope)]; + char *evp; + size_t evplen; + + evp = evpbuf; + evplen = envelope_dump_buffer(ep, evpbuf, evpbufsize); + if (evplen == 0) + return (0); + + if (env->sc_queue_flags & QUEUE_COMPRESS) { + evplen = compress_buffer(evp, evplen, evpbufcom, sizeof evpbufcom); + if (evplen == 0) + return (0); + evp = evpbufcom; + } + + memmove(evpbuf, evp, evplen); + + return (evplen); +} + +static int +queue_envelope_load_buffer(struct envelope *ep, char *evpbuf, size_t evpbufsize) +{ + char evpbufcom[sizeof(struct envelope)]; + char *evp; + size_t evplen; + + evp = evpbuf; + evplen = evpbufsize; + + if (env->sc_queue_flags & QUEUE_COMPRESS) { + evplen = uncompress_buffer(evp, evplen, evpbufcom, sizeof evpbufcom); + if (evplen == 0) + return (0); + evp = evpbufcom; + } + + return (envelope_load_buffer(ep, evp, evplen)); +} + +int +queue_envelope_create(struct envelope *ep) +{ + int r; + char evpbuf[sizeof(struct envelope)]; + size_t evplen; + + ep->creation = time(NULL); + evplen = queue_envelope_dump_buffer(ep, evpbuf, sizeof evpbuf); + if (evplen == 0) + return (0); + + r = env->sc_queue->envelope(QOP_CREATE, &ep->id, evpbuf, evplen); + if (!r) { + ep->creation = 0; + ep->id = 0; + } + return (r); +} + +int +queue_envelope_delete(struct envelope *ep) +{ + return env->sc_queue->envelope(QOP_DELETE, &ep->id, NULL, 0); +} + +int +queue_envelope_load(uint64_t evpid, struct envelope *ep) +{ + const char *e; + char evpbuf[sizeof(struct envelope)]; + size_t evplen; + + ep->id = evpid; + evplen = env->sc_queue->envelope(QOP_LOAD, &ep->id, evpbuf, sizeof evpbuf); + if (evplen == 0) + return (0); + + if (queue_envelope_load_buffer(ep, evpbuf, evplen)) { + if ((e = envelope_validate(ep)) == NULL) { + ep->id = evpid; + return (1); + } + log_debug("invalid envelope %016" PRIx64 ": %s", ep->id, e); + } + return (0); +} + +int +queue_envelope_update(struct envelope *ep) +{ + char evpbuf[sizeof(struct envelope)]; + size_t evplen; + + evplen = queue_envelope_dump_buffer(ep, evpbuf, sizeof evpbuf); + if (evplen == 0) + return (0); + + return env->sc_queue->envelope(QOP_UPDATE, &ep->id, evpbuf, evplen); +} + +void * +qwalk_new(uint32_t msgid) +{ + return env->sc_queue->qwalk_new(msgid); +} + +int +qwalk(void *hdl, uint64_t *evpid) +{ + return env->sc_queue->qwalk(hdl, evpid); +} + +void +qwalk_close(void *hdl) +{ + return env->sc_queue->qwalk_close(hdl); +} + +uint32_t +queue_generate_msgid(void) +{ + uint32_t msgid; + + while((msgid = arc4random_uniform(0xffffffff)) == 0) + ; + + return msgid; +} + +uint64_t +queue_generate_evpid(uint32_t msgid) +{ + uint32_t rnd; + uint64_t evpid; + + while((rnd = arc4random_uniform(0xffffffff)) == 0) + ; + + evpid = msgid; + evpid <<= 32; + evpid |= rnd; + + return evpid; +} + + +/**/ +static const char* +envelope_validate(struct envelope *ep) +{ + if (ep->version != SMTPD_ENVELOPE_VERSION) + return "version mismatch"; + + if (memchr(ep->helo, '\0', sizeof(ep->helo)) == NULL) + return "invalid helo"; + if (ep->helo[0] == '\0') + return "empty helo"; + + if (memchr(ep->hostname, '\0', sizeof(ep->hostname)) == NULL) + return "invalid hostname"; + if (ep->hostname[0] == '\0') + return "empty hostname"; + + if (memchr(ep->errorline, '\0', sizeof(ep->errorline)) == NULL) + return "invalid error line"; + + return NULL; +} |