diff --git a/Makefile.in.patch b/Makefile.in.patch
deleted file mode 100644
index 2e1e7fc..0000000
--- a/Makefile.in.patch
+++ /dev/null
@@ -1,55 +0,0 @@
---- /home/titus/postfix/postfix-2.9.3/src/global/Makefile.in 2012-01-22 21:25:14.000000000 +0530
-+++ /home/titus/postfix/postfix-2.9.3.mod/src/global/Makefile.in 2012-08-08 00:03:15.422066765 +0530
-@@ -3,7 +3,7 @@
- canon_addr.c cfg_parser.c cleanup_strerror.c cleanup_strflags.c \
- clnt_stream.c conv_time.c db_common.c debug_peer.c debug_process.c \
- defer.c deliver_completed.c deliver_flock.c deliver_pass.c \
-- deliver_request.c dict_ldap.c dict_mysql.c dict_pgsql.c \
-+ deliver_request.c dict_ldap.c dict_mysql.c dict_pgsql.c dict_redis.c \
- dict_proxy.c dict_sqlite.c domain_list.c dot_lockfile.c dot_lockfile_as.c \
- dsb_scan.c dsn.c dsn_buf.c dsn_mask.c dsn_print.c dsn_util.c \
- ehlo_mask.c ext_prop.c file_id.c flush_clnt.c header_opts.c \
-@@ -37,7 +37,7 @@
- canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
- clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \
- defer.o deliver_completed.o deliver_flock.o deliver_pass.o \
-- deliver_request.o dict_ldap.o dict_mysql.o dict_pgsql.o \
-+ deliver_request.o dict_ldap.o dict_mysql.o dict_pgsql.o dict_redis.o \
- dict_proxy.o dict_sqlite.o domain_list.o dot_lockfile.o dot_lockfile_as.o \
- dsb_scan.o dsn.o dsn_buf.o dsn_mask.o dsn_print.o dsn_util.o \
- ehlo_mask.o ext_prop.o file_id.o flush_clnt.o header_opts.o \
-@@ -71,7 +71,7 @@
- canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
- conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
- deliver_completed.h deliver_flock.h deliver_pass.h deliver_request.h \
-- dict_ldap.h dict_mysql.h dict_pgsql.h dict_proxy.h dict_sqlite.h domain_list.h \
-+ dict_ldap.h dict_mysql.h dict_pgsql.h dict_proxy.h dict_sqlite.h domain_list.h dict_redis.h \
- dot_lockfile.h dot_lockfile_as.h dsb_scan.h dsn.h dsn_buf.h \
- dsn_mask.h dsn_print.h dsn_util.h ehlo_mask.h ext_prop.h \
- file_id.h flush_clnt.h header_opts.h header_token.h input_transp.h \
-@@ -984,6 +984,17 @@
- dict_sqlite.o: dict_sqlite.c
- dict_sqlite.o: dict_sqlite.h
- dict_sqlite.o: string_list.h
-+dict_redis.o: ../../include/dict.h
-+dict_redis.o: ../../include/mymalloc.h
-+dict_redis.o: ../../include/vbuf.h
-+dict_redis.o: ../../include/vstream.h
-+dict_redis.o: ../../include/vstring.h
-+dict_redis.o: ../../include/stringops.h
-+dict_redis.o: ../../include/sys_defs.h
-+dict_redis.o: hiredis.h
-+dict_redis.o: cfg_parser.h
-+dict_redis.o: dict_redis.c
-+dict_redis.o: dict_redis.h
- domain_list.o: ../../include/argv.h
- domain_list.o: ../../include/match_list.h
- domain_list.o: ../../include/sys_defs.h
-@@ -1372,6 +1383,7 @@
- mail_dict.o: dict_pgsql.h
- mail_dict.o: dict_proxy.h
- mail_dict.o: dict_sqlite.h
-+mail_dict.o: dict_redis.h
- mail_dict.o: mail_dict.c
- mail_dict.o: mail_dict.h
- mail_error.o: ../../include/name_mask.h
diff --git a/README.md b/README.md
index 3d788aa..c93f1d7 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,95 @@
+### Now availible on Alpine linux
+As the `postfix-redis` package, so it can be installed without compiling.
+
### Postfix Redis
-This is a patch to the postfix 2.9.3 MTA to add a lookup table for redis database.
+This is a patch to the postfix 3.4.3+ MTA to add a lookup table for redis database.
+
This patch depends on hiredis client library.
-Apply the patch to a vanilla postfix 2.9.3 source tree
+Apply the patch to a vanilla postfix 3.4.3+ source tree
+
+Copy dict_redis.c and dict_redis.h files to the postfix/src/global directory.
+
+Copy the postfix-redis.patch to the postfix folder and apply with patch -p2 -i postfix-redis.patch
+
+### Generate make files
+```
+$ make makefiles CCARGS="-DHAS_REDIS $(pkg-config --cflags hiredis)" AUXLIBS_REDIS="$(pkg-config --libs hiredis)" config_directory=/etc/postfix meta_directory=/etc/postfix daemon_directory=/usr/libexec/postfix shlib_directory=/usr/lib/postfix dynamicmaps=yes shared=yes
+```
+
+### Compile postfix
+```
+$ make
+```
+
+### Install postfix
+```
+$ make upgrade
+```
-Copy dict_redis.c, dict_redis.h and hiredis.h files to the src/global directory
-Patch Makefile.in and mail_dict.c in src/global with the provided diffs
+### Example postfix configuration
+main.cf:
-### Generate make files
-% make makefiles CCARGS="-DHAS_REDIS -I/usr/local/include/hiredis" AUXLIBS="-L/usr/local/lib -lhiredis"
-Change the include and library location for hiredis client library if required.
+```
+virtual_mailbox_domains = redis:/etc/postfix/redis-vdomains.cf
+virtual_mailbox_maps = redis:/etc/postfix/redis-vmailbox-maps.cf
+virtual_alias_maps = redis:/etc/postfix/redis-valias-maps.cf
+```
-### Compile postfix
-% make
+### Example .cf files
+#### Defaults
+```
+host = 127.0.0.1
+port = 6379
+prefix = (none)
+```
-### Install postfix
-% make install
+redis-vdomains.cf:
+```
+host = 127.0.0.1
+port = 6379
+prefix = VMD:
+```
-### Using the lookup table
-transport_maps = redis:/etc/postfix/redis_transport_maps.cf
+redis-vmailbox-maps.cf:
+```
+host = 127.0.0.1
+port = 6379
+prefix = VMM:
+```
-redis_transport_maps.cf:
-host = localhost
+redis-redis-valias-maps.cf:
+```
+host = 127.0.0.1
port = 6379
+prefix = VAM:
+```
+
+Example Redis keys and values:
+```
+1) "VMD:example.com" "example.com"
+2) "VMM:user@example.com" "user@example.com"
+3) "VAM:postmaster@example.com" "user@example.com"
+```
+
+### Testing Lookup
+
+To test the lookup table you can postmap the string as follows, and monitor redis with ```redis-cli monitor```.
+```
+$ postmap -q "postmaster@example.com" redis:/etc/postfix/redis-valias-maps.cf
+```
+
+### Creating Redis database
+virtual_mailbox_domains
+```
+$ redis-cli set VMD:example.com example.com
+```
+virtual_mailbox_maps
+```
+$ redis-cli set VMM:user@example.com user@example.com
+```
+virtual_alias_maps
+```
+$ redis-cli set VAM:postmaster@example.com user@example.com
+```
diff --git a/REDIS_README.html b/REDIS_README.html
new file mode 100644
index 0000000..792227e
--- /dev/null
+++ b/REDIS_README.html
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+Postfix Redis Howto
+
+
+
+
+
+
+
+
Postfix Redis Howto
+
+
+
+Introduction
+
+ The Postfix redis map type allows you to hook up Postfix to a
+Redis database.
+
+Building Postfix with Redis support
+
+ These instructions assume that you build Postfix from source
+code as described in the INSTALL document. Some modification may
+be required if you build Postfix from a vendor-specific source
+package.
+
+ In order to build Postfix with redis map support, you specify
+-DHAS_REDIS, the directory with the hiredis header files, and
+the location of the hiredis library file.
+
+ For example:
+
+
+
+% make tidy
+% make -f Makefile.init makefiles \
+ 'CCARGS=-DHAS_REDIS $(pkg-config --cflags hiredis)' \
+ 'AUXLIBS_REDIS=$(pkg-config --libs hiredis)'
+
+
+
+
+
+ Failure to use the AUXLIBS_REDIS variable will defeat the purpose
+of dynamic database client loading. Every Postfix executable file
+will have Redis database library dependencies. And that was exactly
+what dynamic database client loading was meant to avoid.
+
+
+
+ Then just run 'make'.
+
+Configuring Redis lookup tables
+
+ Once Postfix is built with redis support, you can specify a
+map type in main.cf like this:
+
+
+
+/etc/postfix/main.cf:
+ alias_maps = redis:/etc/postfix/redis-aliases.cf
+
+
+
+ The file /etc/postfix/redis-aliases.cf specifies how to reach the redis database.
+For a complete description, see the redis_table(5) manual page.
+
+Example: virtual mailbox domain
+
+
+#
+# redis config file for virtual(8) lookups
+#
+
+#
+# The hosts that Postfix will try to connect to
+host = 127.0.0.1
+
+# The prefix added to the query to check with redis
+prefix = VDOM:
+
+
+Using mirrored databases
+
+ Sites that have a need for multiple mail exchangers can setup a redis cluster
+if needed, for fallover capability.
+
+Credits
+
+
+
+- This code is originaly by Titus T Jose.
+
+- It has been updated by Duncan Bellamy.
+
+
+
+
+
+
diff --git a/dict_redis.c b/dict_redis.c
index fe59abb..eae2471 100644
--- a/dict_redis.c
+++ b/dict_redis.c
@@ -1,30 +1,100 @@
-
-#include "sys_defs.h"
+/*++
+/* NAME
+/* dict_redis 3
+/* SUMMARY
+/* dictionary manager interface to Redis databases
+/* SYNOPSIS
+/* #include
+/*
+/* DICT *dict_redis_open(name, open_flags, dict_flags)
+/* const char *name;
+/* int open_flags;
+/* int dict_flags;
+/* DESCRIPTION
+/* dict_redis_open() creates a dictionary of type 'redis'. This
+/* dictionary is an interface for the postfix key->value mappings
+/* to redis. The result is a pointer to the installed dictionary,
+/* or a null pointer in case of problems.
+/* .PP
+/* Arguments:
+/* .IP name
+/* Either the path to the Redis configuration file (if it
+/* starts with '/' or '.'), or the prefix which will be used to
+/* obtain main.cf configuration parameters for this search.
+/*
+/* In the first case, the configuration parameters below are
+/* specified in the file as \fIname\fR=\fIvalue\fR pairs.
+/*
+/* In the second case, the configuration parameters are
+/* prefixed with the value of \fIname\fR and an underscore,
+/* and they are specified in main.cf. For example, if this
+/* value is \fIredisDB\fR, the parameters would look like
+/* \fIredisDB_host\fR, \fIpredisDB_prefix\fR, and so on.
+/* .IP other_name
+/* reference for outside use.
+/* .IP open_flags
+/* unused.
+/* .IP dict_flags
+/* See dict_open(3).
+/*
+/* .PP
+/* Configuration parameters:
+/* .IP host
+/* IP address of the redis server hosting the database.
+/* .IP port
+/* Port number to connect to the above.
+/* .IP prefix
+/* Prefix to add to the key when looking it up.
+/* .PP
+/* For example, if you want the map to reference databases on
+/* redis host "127.0.0.1" and prefix the query with VDOM:
+/* Then the configuration file
+/* should read:
+/* .PP
+/* host = 127.0.0.1
+/* .br
+/* port = 6379
+/* .br
+/* prefix = VDOM:
+/* .PP
+/* SEE ALSO
+/* dict(3) generic dictionary manager
+/* HISTORY
+/* .ad
+/* .fi
+/* This feature was introduced with Postfix 3.7.
+/* AUTHOR(S)
+/* Titus Jose
+/* titus.nitt@gmail.com
+/*
+/* Updated by:
+/* Duncan Bellamy
+/* dunk@denkimushi.com
+/*--*/
+
+#include
#ifdef HAS_REDIS
#include
#include
#include
-#ifdef STRCASECMP_IN_STRINGS_H
-#include
-#endif
-
/* Utility library. */
-#include "dict.h"
-#include "mymalloc.h"
-#include "vstring.h"
-#include "stringops.h"
+#include
+#include
+#include
+#include
+#include
/* Global library. */
-#include "cfg_parser.h"
+#include
/* Application-specific. */
-#include "dict_redis.h"
-#include "hiredis.h"
+#include
+#include
typedef struct {
DICT dict;
@@ -32,6 +102,8 @@ typedef struct {
redisContext *c;
char *host;
int port;
+ char *prefix;
+ VSTRING *result;
} DICT_REDIS;
/* internal function declarations */
@@ -47,12 +119,13 @@ static const char *dict_redis_lookup(DICT *dict, const char *name)
DICT_REDIS *dict_redis = (DICT_REDIS *) dict;
redisReply *reply;
const char *r;
- VSTRING *result;
+ int error;
+
dict->error = 0;
-
- result = vstring_alloc(10);
- VSTRING_RESET(result);
- VSTRING_TERMINATE(result);
+
+ if (msg_verbose)
+ msg_info("%s: Requesting key %s%s", dict_redis->host, dict_redis->prefix, name);
+
/*
* Optionally fold the key.
*/
@@ -62,33 +135,42 @@ static const char *dict_redis_lookup(DICT *dict, const char *name)
vstring_strcpy(dict->fold_buf, name);
name = lowercase(vstring_str(dict->fold_buf));
}
-
- if(dict_redis->c) {
- reply = redisCommand(dict_redis->c,"GET %s",name);
- }
- else {
- dict->error = DICT_ERR_CONFIG;
- }
- if(reply->str) {
- vstring_strcpy(result,reply->str);
- r = vstring_str(result);
- freeReplyObject(reply);
- }
- else {
- dict->error = DICT_ERR_RETRY;
+
+ /*
+ * TODO(wietse) domain match, substring query, and key format support as in
+ * memcache_table.
+ */
+ reply = redisCommand(dict_redis->c, "GET %s%s", dict_redis->prefix, name);
+ error = dict->error;
+ if (reply->str) {
+ vstring_strcpy(dict_redis->result, reply->str);
+ r = vstring_str(dict_redis->result);
+ } else {
+ error = 1;
}
- return ((dict->error == 0 && *r) ? r : 0);
+ freeReplyObject(reply);
+ return ((error == 0 && *r) ? r : 0);
}
/* redis_parse_config - parse redis configuration file */
static void redis_parse_config(DICT_REDIS *dict_redis, const char *rediscf)
{
- const char *myname = "redisname_parse";
+ const char *myname = "redis_parse_config";
CFG_PARSER *p = dict_redis->parser;
+#if 0
+
+ /*
+ * TODO(wietse) substring query and key format support as in
+ * memcache_table.
+ */
+ dict_redis->key_format = cfg_get_str(p, "key_format", "%s", 0, 0);
+#endif
dict_redis->port = cfg_get_int(p, "port", 6379, 0, 0);
dict_redis->host = cfg_get_str(p, "host", "127.0.0.1", 1, 0);
+ dict_redis->prefix = cfg_get_str(p, "prefix", "", 0, 0);
+ dict_redis->result = vstring_alloc(10);
}
/* dict_redis_open - open redis data base */
@@ -97,7 +179,14 @@ DICT *dict_redis_open(const char *name, int open_flags, int dict_flags)
{
DICT_REDIS *dict_redis;
CFG_PARSER *parser;
- redisContext *c;
+
+ /*
+ * Sanity check.
+ */
+ if (open_flags != O_RDONLY)
+ return (dict_surrogate(DICT_TYPE_REDIS, name, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_REDIS, name));
/*
* Open the configuration file.
@@ -114,13 +203,30 @@ DICT *dict_redis_open(const char *name, int open_flags, int dict_flags)
dict_redis->parser = parser;
redis_parse_config(dict_redis, name);
dict_redis->dict.owner = cfg_get_owner(dict_redis->parser);
- c = redisConnect(dict_redis->host,dict_redis->port);
- if(c->err) {
- dict_redis->c = NULL;
- } else {
- dict_redis->c = c;
+ dict_redis->c = redisConnect(dict_redis->host, dict_redis->port);
+ if (dict_redis->c == 0 || dict_redis->c->err) {
+ msg_warn("%s:%s: Cannot connect to Redis server %s",
+ DICT_TYPE_REDIS, name, dict_redis->host);
+ dict_redis->dict.close((DICT *) dict_redis);
+ return (dict_surrogate(DICT_TYPE_REDIS, name, open_flags, dict_flags,
+ "open %s: %m", name));
}
-
+#if 0
+
+ /*
+ * TODO(wietse) domain match, substring query, and key format support as in
+ * memcache_table.
+ */
+ dict_redis->dbc_ctxt = 0;
+ db_common_parse(&dict_redis->dict, &dict_redis->dbc_ctxt,
+ dict_redis->key_format, 1);
+ db_common_parse_domain(dict_redis->parser, dict_redis->dbc_ctxt);
+ if (db_common_dict_partial(dict_redis->dbc_ctxt))
+ /* Breaks recipient delimiters */
+ dict_redis->dict.flags |= DICT_FLAG_PATTERN;
+ else
+ dict_redis->dict.flags |= DICT_FLAG_FIXED;
+#endif
return (DICT_DEBUG (&dict_redis->dict));
}
@@ -130,8 +236,12 @@ static void dict_redis_close(DICT *dict)
{
DICT_REDIS *dict_redis = (DICT_REDIS *) dict;
+ if (dict_redis->c)
+ redisFree(dict_redis->c);
cfg_parser_free(dict_redis->parser);
myfree(dict_redis->host);
+ myfree(dict_redis->prefix);
+ vstring_free(dict_redis->result);
if (dict->fold_buf)
vstring_free(dict->fold_buf);
dict_free(dict);
diff --git a/dict_redis.h b/dict_redis.h
index dfd6499..b078c4e 100644
--- a/dict_redis.h
+++ b/dict_redis.h
@@ -1,10 +1,39 @@
#ifndef _DICT_REDIS_H_INCLUDED_
#define _DICT_REDIS_H_INCLUDED_
+/*++
+/* NAME
+/* dict_redis 3h
+/* SUMMARY
+/* dictionary manager interface to redis databases
+/* SYNOPSIS
+/* #include
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
#include
+ /*
+ * External interface.
+ */
#define DICT_TYPE_REDIS "redis"
extern DICT *dict_redis_open(const char *, int, int);
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Titus Jose
+/* titus.nitt@gmail.com
+/*
+/* Updated by:
+/* Duncan Bellamy
+/* dunk@denkimushi.com
+/*--*/
+
#endif
diff --git a/hiredis.h b/hiredis.h
deleted file mode 100644
index 7c04b62..0000000
--- a/hiredis.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (c) 2009-2011, Salvatore Sanfilippo
- * Copyright (c) 2010-2011, Pieter Noordhuis
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Redis nor the names of its contributors may be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef __HIREDIS_H
-#define __HIREDIS_H
-#include /* for size_t */
-#include /* for va_list */
-#include /* for struct timeval */
-
-#define HIREDIS_MAJOR 0
-#define HIREDIS_MINOR 10
-#define HIREDIS_PATCH 1
-
-#define REDIS_ERR -1
-#define REDIS_OK 0
-
-/* When an error occurs, the err flag in a context is set to hold the type of
- * error that occured. REDIS_ERR_IO means there was an I/O error and you
- * should use the "errno" variable to find out what is wrong.
- * For other values, the "errstr" field will hold a description. */
-#define REDIS_ERR_IO 1 /* Error in read or write */
-#define REDIS_ERR_EOF 3 /* End of file */
-#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
-#define REDIS_ERR_OOM 5 /* Out of memory */
-#define REDIS_ERR_OTHER 2 /* Everything else... */
-
-/* Connection type can be blocking or non-blocking and is set in the
- * least significant bit of the flags field in redisContext. */
-#define REDIS_BLOCK 0x1
-
-/* Connection may be disconnected before being free'd. The second bit
- * in the flags field is set when the context is connected. */
-#define REDIS_CONNECTED 0x2
-
-/* The async API might try to disconnect cleanly and flush the output
- * buffer and read all subsequent replies before disconnecting.
- * This flag means no new commands can come in and the connection
- * should be terminated once all replies have been read. */
-#define REDIS_DISCONNECTING 0x4
-
-/* Flag specific to the async API which means that the context should be clean
- * up as soon as possible. */
-#define REDIS_FREEING 0x8
-
-/* Flag that is set when an async callback is executed. */
-#define REDIS_IN_CALLBACK 0x10
-
-/* Flag that is set when the async context has one or more subscriptions. */
-#define REDIS_SUBSCRIBED 0x20
-
-/* Flag that is set when monitor mode is active */
-#define REDIS_MONITORING 0x40
-
-#define REDIS_REPLY_STRING 1
-#define REDIS_REPLY_ARRAY 2
-#define REDIS_REPLY_INTEGER 3
-#define REDIS_REPLY_NIL 4
-#define REDIS_REPLY_STATUS 5
-#define REDIS_REPLY_ERROR 6
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* This is the reply object returned by redisCommand() */
-typedef struct redisReply {
- int type; /* REDIS_REPLY_* */
- long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
- int len; /* Length of string */
- char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
- size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
- struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
-} redisReply;
-
-typedef struct redisReadTask {
- int type;
- int elements; /* number of elements in multibulk container */
- int idx; /* index in parent (array) object */
- void *obj; /* holds user-generated value for a read task */
- struct redisReadTask *parent; /* parent task */
- void *privdata; /* user-settable arbitrary field */
-} redisReadTask;
-
-typedef struct redisReplyObjectFunctions {
- void *(*createString)(const redisReadTask*, char*, size_t);
- void *(*createArray)(const redisReadTask*, int);
- void *(*createInteger)(const redisReadTask*, long long);
- void *(*createNil)(const redisReadTask*);
- void (*freeObject)(void*);
-} redisReplyObjectFunctions;
-
-/* State for the protocol parser */
-typedef struct redisReader {
- int err; /* Error flags, 0 when there is no error */
- char errstr[128]; /* String representation of error when applicable */
-
- char *buf; /* Read buffer */
- size_t pos; /* Buffer cursor */
- size_t len; /* Buffer length */
-
- redisReadTask rstack[4];
- int ridx; /* Index of current read task */
- void *reply; /* Temporary reply pointer */
-
- redisReplyObjectFunctions *fn;
- void *privdata;
-} redisReader;
-
-/* Public API for the protocol parser. */
-redisReader *redisReaderCreate(void);
-void redisReaderFree(redisReader *r);
-int redisReaderFeed(redisReader *r, const char *buf, size_t len);
-int redisReaderGetReply(redisReader *r, void **reply);
-
-/* Backwards compatibility, can be removed on big version bump. */
-#define redisReplyReaderCreate redisReaderCreate
-#define redisReplyReaderFree redisReaderFree
-#define redisReplyReaderFeed redisReaderFeed
-#define redisReplyReaderGetReply redisReaderGetReply
-#define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
-#define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply)
-#define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr)
-
-/* Function to free the reply objects hiredis returns by default. */
-void freeReplyObject(void *reply);
-
-/* Functions to format a command according to the protocol. */
-int redisvFormatCommand(char **target, const char *format, va_list ap);
-int redisFormatCommand(char **target, const char *format, ...);
-int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
-
-/* Context for a connection to Redis */
-typedef struct redisContext {
- int err; /* Error flags, 0 when there is no error */
- char errstr[128]; /* String representation of error when applicable */
- int fd;
- int flags;
- char *obuf; /* Write buffer */
- redisReader *reader; /* Protocol reader */
-} redisContext;
-
-redisContext *redisConnect(const char *ip, int port);
-redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
-redisContext *redisConnectNonBlock(const char *ip, int port);
-redisContext *redisConnectUnix(const char *path);
-redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv);
-redisContext *redisConnectUnixNonBlock(const char *path);
-int redisSetTimeout(redisContext *c, struct timeval tv);
-void redisFree(redisContext *c);
-int redisBufferRead(redisContext *c);
-int redisBufferWrite(redisContext *c, int *done);
-
-/* In a blocking context, this function first checks if there are unconsumed
- * replies to return and returns one if so. Otherwise, it flushes the output
- * buffer to the socket and reads until it has a reply. In a non-blocking
- * context, it will return unconsumed replies until there are no more. */
-int redisGetReply(redisContext *c, void **reply);
-int redisGetReplyFromReader(redisContext *c, void **reply);
-
-/* Write a command to the output buffer. Use these functions in blocking mode
- * to get a pipeline of commands. */
-int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
-int redisAppendCommand(redisContext *c, const char *format, ...);
-int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
-
-/* Issue a command to Redis. In a blocking context, it is identical to calling
- * redisAppendCommand, followed by redisGetReply. The function will return
- * NULL if there was an error in performing the request, otherwise it will
- * return the reply. In a non-blocking context, it is identical to calling
- * only redisAppendCommand and will always return NULL. */
-void *redisvCommand(redisContext *c, const char *format, va_list ap);
-void *redisCommand(redisContext *c, const char *format, ...);
-void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/mail_dict.c.patch b/mail_dict.c.patch
deleted file mode 100644
index dedc900..0000000
--- a/mail_dict.c.patch
+++ /dev/null
@@ -1,20 +0,0 @@
---- /home/titus/postfix/postfix-2.9.3/src/global/mail_dict.c 2011-12-20 01:25:38.000000000 +0530
-+++ /home/titus/postfix/postfix-2.9.3.mod/src/global/mail_dict.c 2012-08-08 00:04:12.252776279 +0530
-@@ -38,6 +38,7 @@
- #include
- #include
- #include
-+#include
- #include
-
- typedef struct {
-@@ -59,6 +60,9 @@
- #ifdef HAS_SQLITE
- DICT_TYPE_SQLITE, dict_sqlite_open,
- #endif
-+#ifdef HAS_REDIS
-+ DICT_TYPE_REDIS, dict_redis_open,
-+#endif
- DICT_TYPE_MEMCACHE, dict_memcache_open,
- 0,
- };
diff --git a/patches/postfix-redis.patch b/patches/postfix-redis.patch
new file mode 100644
index 0000000..28e8d84
--- /dev/null
+++ b/patches/postfix-redis.patch
@@ -0,0 +1,136 @@
+diff -Nurp a/conf/dynamicmaps.cf b/conf/dynamicmaps.cf
+--- a/conf/dynamicmaps.cf 2014-05-30 12:38:33.000000000 +0100
++++ b/conf/dynamicmaps.cf 2020-08-15 08:59:29.358727776 +0100
+@@ -5,5 +5,6 @@ lmdb ${LIB_PREFIX}lmdb${LIB_SUFFIX} dict
+ mysql ${LIB_PREFIX}mysql${LIB_SUFFIX} dict_mysql_open
+ pcre ${LIB_PREFIX}pcre${LIB_SUFFIX} dict_pcre_open
+ pgsql ${LIB_PREFIX}pgsql${LIB_SUFFIX} dict_pgsql_open
++redis ${LIB_PREFIX}redis${LIB_SUFFIX} dict_redis_open
+ sdbm ${LIB_PREFIX}sdbm${LIB_SUFFIX} dict_sdbm_open mkmap_sdbm_open
+ sqlite ${LIB_PREFIX}sqlite${LIB_SUFFIX} dict_sqlite_open
+diff -Nurp a/conf/postfix-files b/conf/postfix-files
+--- a/conf/postfix-files 2019-01-29 22:24:42.000000000 +0000
++++ b/conf/postfix-files 2020-08-15 08:59:59.590574025 +0100
+@@ -78,6 +78,7 @@ $shlib_directory/${LIB_PREFIX}lmdb${LIB_
+ $shlib_directory/${LIB_PREFIX}mysql${LIB_SUFFIX}:f:root:-:755
+ $shlib_directory/${LIB_PREFIX}pcre${LIB_SUFFIX}:f:root:-:755
+ $shlib_directory/${LIB_PREFIX}pgsql${LIB_SUFFIX}:f:root:-:755
++$shlib_directory/${LIB_PREFIX}redis${LIB_SUFFIX}:f:root:-:755
+ $shlib_directory/${LIB_PREFIX}sdbm${LIB_SUFFIX}:f:root:-:755
+ $shlib_directory/${LIB_PREFIX}sqlite${LIB_SUFFIX}:f:root:-:755
+ $meta_directory/dynamicmaps.cf.d:d:root:-:755
+diff -Nurp a/makedefs b/makedefs
+--- a/makedefs 2020-05-06 15:10:47.000000000 +0100
++++ b/makedefs 2020-08-15 09:02:11.765901819 +0100
+@@ -1167,7 +1167,7 @@ DEFINED_MAP_TYPES=`
+
+ # Propagate AUXLIBS_FOO or merge them into global AUXLIBS (i.e. SYSLIBS).
+
+-PLUGGABLE_MAPS="CDB LDAP LMDB MYSQL PCRE PGSQL SDBM SQLITE"
++PLUGGABLE_MAPS="CDB LDAP LMDB MYSQL PCRE PGSQL REDIS SDBM SQLITE"
+
+ case "$dynamicmaps" in
+ yes) for name in $PLUGGABLE_MAPS
+diff -Nurp a/src/global/mail_dict.c b/src/global/mail_dict.c
+--- a/src/global/mail_dict.c 2014-06-22 01:42:55.000000000 +0100
++++ b/src/global/mail_dict.c 2020-08-15 09:03:33.445486419 +0100
+@@ -45,6 +45,7 @@
+ #include
+ #include
+ #include
++#include
+ #include
+ #include
+ #include
+@@ -68,6 +69,9 @@ static const DICT_OPEN_INFO dict_open_in
+ #ifdef HAS_PGSQL
+ DICT_TYPE_PGSQL, dict_pgsql_open,
+ #endif
++#ifdef HAS_REDIS
++ DICT_TYPE_REDIS, dict_redis_open,
++#endif
+ #ifdef HAS_SQLITE
+ DICT_TYPE_SQLITE, dict_sqlite_open,
+ #endif
+diff -Nurp a/src/global/Makefile.in b/src/global/Makefile.in
+--- a/src/global/Makefile.in 2020-01-12 13:50:56.000000000 +0000
++++ b/src/global/Makefile.in 2020-08-15 09:12:53.593101032 +0100
+@@ -3,7 +3,7 @@ SRCS = abounce.c anvil_clnt.c been_here.
+ canon_addr.c cfg_parser.c cleanup_strerror.c cleanup_strflags.c \
+ clnt_stream.c conv_time.c db_common.c debug_peer.c debug_process.c \
+ defer.c deliver_completed.c deliver_flock.c deliver_pass.c \
+- deliver_request.c dict_ldap.c dict_mysql.c dict_pgsql.c \
++ deliver_request.c dict_ldap.c dict_mysql.c dict_pgsql.c dict_redis.c \
+ dict_proxy.c dict_sqlite.c domain_list.c dot_lockfile.c dot_lockfile_as.c \
+ dsb_scan.c dsn.c dsn_buf.c dsn_mask.c dsn_print.c dsn_util.c \
+ ehlo_mask.c ext_prop.c file_id.c flush_clnt.c header_opts.c \
+@@ -78,13 +78,13 @@ OBJS = abounce.o anvil_clnt.o been_here.
+ # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
+ # When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
+ # otherwise it sets the PLUGIN_* macros.
+-MAP_OBJ = dict_ldap.o dict_mysql.o dict_pgsql.o dict_sqlite.o mkmap_cdb.o \
++MAP_OBJ = dict_ldap.o dict_mysql.o dict_pgsql.o dict_redis.o dict_sqlite.o mkmap_cdb.o \
+ mkmap_lmdb.o mkmap_sdbm.o
+ HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
+ canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
+ conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
+ deliver_completed.h deliver_flock.h deliver_pass.h deliver_request.h \
+- dict_ldap.h dict_mysql.h dict_pgsql.h dict_proxy.h dict_sqlite.h domain_list.h \
++ dict_ldap.h dict_mysql.h dict_pgsql.h dict_redis.h dict_proxy.h dict_sqlite.h domain_list.h \
+ dot_lockfile.h dot_lockfile_as.h dsb_scan.h dsn.h dsn_buf.h \
+ dsn_mask.h dsn_print.h dsn_util.h ehlo_mask.h ext_prop.h \
+ file_id.h flush_clnt.h header_opts.h header_token.h input_transp.h \
+@@ -131,7 +131,7 @@ LIBS = ../../lib/lib$(LIB_PREFIX)util$(L
+ LIB_DIR = ../../lib
+ INC_DIR = ../../include
+ PLUGIN_MAP_SO = $(LIB_PREFIX)ldap$(LIB_SUFFIX) $(LIB_PREFIX)mysql$(LIB_SUFFIX) \
+- $(LIB_PREFIX)pgsql$(LIB_SUFFIX) $(LIB_PREFIX)sqlite$(LIB_SUFFIX) \
++ $(LIB_PREFIX)pgsql$(LIB_SUFFIX) $(LIB_PREFIX)redis$(LIB_SUFFIX) $(LIB_PREFIX)sqlite$(LIB_SUFFIX) \
+ $(LIB_PREFIX)lmdb$(LIB_SUFFIX) $(LIB_PREFIX)cdb$(LIB_SUFFIX) \
+ $(LIB_PREFIX)sdbm$(LIB_SUFFIX)
+ MAKES =
+@@ -167,6 +167,9 @@ $(LIB_PREFIX)mysql$(LIB_SUFFIX): dict_my
+ $(LIB_PREFIX)pgsql$(LIB_SUFFIX): dict_pgsql.o
+ $(PLUGIN_LD) $(SHLIB_RPATH) -o $@ dict_pgsql.o $(AUXLIBS_PGSQL)
+
++$(LIB_PREFIX)redis$(LIB_SUFFIX): dict_redis.o
++ $(PLUGIN_LD) $(SHLIB_RPATH) -o $@ dict_redis.o $(AUXLIBS_REDIS)
++
+ $(LIB_PREFIX)sqlite$(LIB_SUFFIX): dict_sqlite.o
+ $(PLUGIN_LD) $(SHLIB_RPATH) -o $@ dict_sqlite.o $(AUXLIBS_SQLITE)
+
+@@ -521,6 +524,8 @@ surrogate_test: mail_dict surrogate.ref
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict mysql:/xx read >>surrogate.tmp 2>&1
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict pgsql:/xx write >>surrogate.tmp 2>&1
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict pgsql:/xx read >>surrogate.tmp 2>&1
++ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict redis:/xx write >>surrogate.tmp 2>&1
++ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict redis:/xx read >>surrogate.tmp 2>&1
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict sqlite:/xx write >>surrogate.tmp 2>&1
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict sqlite:/xx read >>surrogate.tmp 2>&1
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict memcache:/xx read >>surrogate.tmp 2>&1
+@@ -1183,6 +1188,17 @@ dict_pgsql.o: db_common.h
+ dict_pgsql.o: dict_pgsql.c
+ dict_pgsql.o: dict_pgsql.h
+ dict_pgsql.o: string_list.h
++dict_redis.o: ../../include/dict.h
++dict_redis.o: ../../include/msg.h
++dict_redis.o: ../../include/mymalloc.h
++dict_redis.o: ../../include/vbuf.h
++dict_redis.o: ../../include/vstream.h
++dict_redis.o: ../../include/vstring.h
++dict_redis.o: ../../include/stringops.h
++dict_redis.o: ../../include/sys_defs.h
++dict_redis.o: cfg_parser.h
++dict_redis.o: dict_redis.c
++dict_redis.o: dict_redis.h
+ dict_proxy.o: ../../include/argv.h
+ dict_proxy.o: ../../include/attr.h
+ dict_proxy.o: ../../include/check_arg.h
+@@ -1768,6 +1784,7 @@ mail_dict.o: dict_ldap.h
+ mail_dict.o: dict_memcache.h
+ mail_dict.o: dict_mysql.h
+ mail_dict.o: dict_pgsql.h
++mail_dict.o: dict_redis.h
+ mail_dict.o: dict_proxy.h
+ mail_dict.o: dict_sqlite.h
+ mail_dict.o: dynamicmaps.h
diff --git a/patches/postfix3.6-musl.patch b/patches/postfix3.6-musl.patch
new file mode 100644
index 0000000..e6b31f1
--- /dev/null
+++ b/patches/postfix3.6-musl.patch
@@ -0,0 +1,30 @@
+diff -Nurp a/src/dns/dns_lookup.c aa/src/dns/dns_lookup.c
+--- a/src/dns/dns_lookup.c 2020-05-23 17:38:26.000000000 +0100
++++ aa/src/dns/dns_lookup.c 2020-09-24 19:42:10.782442351 +0100
+@@ -394,8 +394,10 @@ static int dns_neg_query(const char *nam
+ if (msg_verbose)
+ msg_info("res_nmkquery() failed");
+ return (len);
+- } else if ((len = DNS_RES_NSEND(&dns_res_state,
+- msg_buf, len, answer, anslen)) < 0) {
++ }
++ msg_buf[3] |= 32; // AD flag
++ if ((len = DNS_RES_NSEND(&dns_res_state,
++ msg_buf, len, answer, anslen)) < 0) {
+ DNS_SET_H_ERRNO(&dns_res_state, TRY_AGAIN);
+ if (msg_verbose)
+ msg_info("res_nsend() failed");
+@@ -538,11 +540,12 @@ static int dns_query(const char *name, i
+ for (;;) {
+ dns_res_state.options &= ~saved_options;
+ dns_res_state.options |= flags;
+- if (keep_notfound && var_dns_ncache_ttl_fix) {
++ if (1) {
+ #ifdef HAVE_RES_SEND
+ len = dns_neg_query((char *) name, C_IN, type, reply->buf,
+ reply->buf_len);
+ #else
++#error HAVE_RES_SEND not defined
+ var_dns_ncache_ttl_fix = 0;
+ msg_warn("system library does not support %s=yes"
+ " -- ignoring this setting", VAR_DNS_NCACHE_TTL_FIX);
diff --git a/patches/postfix3.6-redis.patch b/patches/postfix3.6-redis.patch
new file mode 100644
index 0000000..42ff912
--- /dev/null
+++ b/patches/postfix3.6-redis.patch
@@ -0,0 +1,196 @@
+--- /var/tmp/postfix-3.6-20210224/conf/dynamicmaps.cf 2014-05-30 07:38:33.000000000 -0400
++++ ./conf/dynamicmaps.cf 2021-03-11 18:07:16.529195712 -0500
+@@ -5,5 +5,6 @@
+ mysql ${LIB_PREFIX}mysql${LIB_SUFFIX} dict_mysql_open
+ pcre ${LIB_PREFIX}pcre${LIB_SUFFIX} dict_pcre_open
+ pgsql ${LIB_PREFIX}pgsql${LIB_SUFFIX} dict_pgsql_open
++redis ${LIB_PREFIX}redis${LIB_SUFFIX} dict_redis_open
+ sdbm ${LIB_PREFIX}sdbm${LIB_SUFFIX} dict_sdbm_open mkmap_sdbm_open
+ sqlite ${LIB_PREFIX}sqlite${LIB_SUFFIX} dict_sqlite_open
+diff '--exclude=man' '--exclude=html' '--exclude=README_FILES' '--exclude=INSTALL' '--exclude=.indent.pro' -r -ur --new-file /var/tmp/postfix-3.6-20210224/conf/postfix-files ./conf/postfix-files
+--- /var/tmp/postfix-3.6-20210224/conf/postfix-files 2019-01-29 17:24:42.000000000 -0500
++++ ./conf/postfix-files 2021-03-11 18:07:16.530195726 -0500
+@@ -78,6 +78,7 @@
+ $shlib_directory/${LIB_PREFIX}mysql${LIB_SUFFIX}:f:root:-:755
+ $shlib_directory/${LIB_PREFIX}pcre${LIB_SUFFIX}:f:root:-:755
+ $shlib_directory/${LIB_PREFIX}pgsql${LIB_SUFFIX}:f:root:-:755
++$shlib_directory/${LIB_PREFIX}redis${LIB_SUFFIX}:f:root:-:755
+ $shlib_directory/${LIB_PREFIX}sdbm${LIB_SUFFIX}:f:root:-:755
+ $shlib_directory/${LIB_PREFIX}sqlite${LIB_SUFFIX}:f:root:-:755
+ $meta_directory/dynamicmaps.cf.d:d:root:-:755
+@@ -200,6 +201,7 @@
+ $manpage_directory/man5/nisplus_table.5:f:root:-:644
+ $manpage_directory/man5/pcre_table.5:f:root:-:644
+ $manpage_directory/man5/pgsql_table.5:f:root:-:644
++$manpage_directory/man5/redis_table.5:f:root:-:644
+ $manpage_directory/man5/postconf.5:f:root:-:644
+ $manpage_directory/man5/postfix-wrapper.5:f:root:-:644
+ $manpage_directory/man5/regexp_table.5:f:root:-:644
+@@ -307,6 +309,7 @@
+ $readme_directory/PACKAGE_README:f:root:-:644
+ $readme_directory/PCRE_README:f:root:-:644
+ $readme_directory/PGSQL_README:f:root:-:644
++$readme_directory/REDIS_README:f:root:-:644
+ $readme_directory/POSTSCREEN_README:f:root:-:644
+ $readme_directory/QMQP_README:f:root:-:644:o
+ $readme_directory/QSHAPE_README:f:root:-:644
+@@ -363,6 +366,7 @@
+ $html_directory/PACKAGE_README.html:f:root:-:644
+ $html_directory/PCRE_README.html:f:root:-:644
+ $html_directory/PGSQL_README.html:f:root:-:644
++$html_directory/REDIS_README.html:f:root:-:644
+ $html_directory/POSTSCREEN_README.html:f:root:-:644
+ $html_directory/QMQP_README.html:f:root:-:644:o
+ $html_directory/QSHAPE_README.html:f:root:-:644
+@@ -414,6 +418,7 @@
+ $html_directory/oqmgr.8.html:f:root:-:644
+ $html_directory/pcre_table.5.html:f:root:-:644
+ $html_directory/pgsql_table.5.html:f:root:-:644
++$html_directory/redis_table.5.html:f:root:-:644
+ $html_directory/pickup.8.html:f:root:-:644
+ $html_directory/pipe.8.html:f:root:-:644
+ $html_directory/postalias.1.html:f:root:-:644
+diff '--exclude=man' '--exclude=html' '--exclude=README_FILES' '--exclude=INSTALL' '--exclude=.indent.pro' -r -ur --new-file /var/tmp/postfix-3.6-20210224/makedefs ./makedefs
+--- /var/tmp/postfix-3.6-20210224/makedefs 2020-09-30 17:22:49.000000000 -0400
++++ ./makedefs 2021-03-11 18:07:16.531195739 -0500
+@@ -1165,7 +1165,7 @@
+
+ # Propagate AUXLIBS_FOO or merge them into global AUXLIBS (i.e. SYSLIBS).
+
+-PLUGGABLE_MAPS="CDB LDAP LMDB MYSQL PCRE PGSQL SDBM SQLITE"
++PLUGGABLE_MAPS="CDB LDAP LMDB MYSQL PCRE PGSQL REDIS SDBM SQLITE"
+
+ case "$dynamicmaps" in
+ yes) for name in $PLUGGABLE_MAPS
+diff '--exclude=man' '--exclude=html' '--exclude=README_FILES' '--exclude=INSTALL' '--exclude=.indent.pro' -r -ur --new-file /var/tmp/postfix-3.6-20210224/proto/DATABASE_README.html ./proto/DATABASE_README.html
+--- /var/tmp/postfix-3.6-20210224/proto/DATABASE_README.html 2020-08-29 13:51:42.000000000 -0400
++++ ./proto/DATABASE_README.html 2021-03-13 17:14:36.642652892 -0500
+@@ -421,6 +421,11 @@
+ as used in "sdbm:table" is the database file name without the ".dir"
+ or ".pag" suffix.
+
++ redis (read-only)
++
++ Redis database. Configuration details are given in redis_table(5).
++
++
+ socketmap (read-only)
+
+ Sendmail-style socketmap client. The name of the table is
+diff '--exclude=man' '--exclude=html' '--exclude=README_FILES' '--exclude=INSTALL' '--exclude=.indent.pro' -r -ur --new-file /var/tmp/postfix-3.6-20210224/proto/redis_table ./proto/redis_table
+--- /var/tmp/postfix-3.6-20210224/src/global/mail_dict.c 2014-06-21 20:42:55.000000000 -0400
++++ ./src/global/mail_dict.c 2021-03-11 18:07:16.532195753 -0500
+@@ -45,6 +45,7 @@
+ #include
+ #include
+ #include
++#include
+ #include
+ #include
+ #include
+@@ -68,6 +69,9 @@
+ #ifdef HAS_PGSQL
+ DICT_TYPE_PGSQL, dict_pgsql_open,
+ #endif
++#ifdef HAS_REDIS
++ DICT_TYPE_REDIS, dict_redis_open,
++#endif
+ #ifdef HAS_SQLITE
+ DICT_TYPE_SQLITE, dict_sqlite_open,
+ #endif
+diff '--exclude=man' '--exclude=html' '--exclude=README_FILES' '--exclude=INSTALL' '--exclude=.indent.pro' -r -ur --new-file /var/tmp/postfix-3.6-20210224/src/global/Makefile.in ./src/global/Makefile.in
+--- /var/tmp/postfix-3.6-20210224/src/global/Makefile.in 2021-01-09 16:24:11.000000000 -0500
++++ ./src/global/Makefile.in 2021-03-11 18:07:16.535195793 -0500
+@@ -3,7 +3,7 @@
+ canon_addr.c cfg_parser.c cleanup_strerror.c cleanup_strflags.c \
+ clnt_stream.c conv_time.c db_common.c debug_peer.c debug_process.c \
+ defer.c deliver_completed.c deliver_flock.c deliver_pass.c \
+- deliver_request.c dict_ldap.c dict_mysql.c dict_pgsql.c \
++ deliver_request.c dict_ldap.c dict_mysql.c dict_pgsql.c dict_redis.c \
+ dict_proxy.c dict_sqlite.c domain_list.c dot_lockfile.c dot_lockfile_as.c \
+ dsb_scan.c dsn.c dsn_buf.c dsn_mask.c dsn_print.c dsn_util.c \
+ ehlo_mask.c ext_prop.c file_id.c flush_clnt.c header_opts.c \
+@@ -80,13 +80,13 @@
+ # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
+ # When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
+ # otherwise it sets the PLUGIN_* macros.
+-MAP_OBJ = dict_ldap.o dict_mysql.o dict_pgsql.o dict_sqlite.o mkmap_cdb.o \
++MAP_OBJ = dict_ldap.o dict_mysql.o dict_pgsql.o dict_redis.o dict_sqlite.o mkmap_cdb.o \
+ mkmap_lmdb.o mkmap_sdbm.o
+ HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
+ canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
+ conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
+ deliver_completed.h deliver_flock.h deliver_pass.h deliver_request.h \
+- dict_ldap.h dict_mysql.h dict_pgsql.h dict_proxy.h dict_sqlite.h domain_list.h \
++ dict_ldap.h dict_mysql.h dict_pgsql.h dict_redis.h dict_proxy.h dict_sqlite.h domain_list.h \
+ dot_lockfile.h dot_lockfile_as.h dsb_scan.h dsn.h dsn_buf.h \
+ dsn_mask.h dsn_print.h dsn_util.h ehlo_mask.h ext_prop.h \
+ file_id.h flush_clnt.h header_opts.h header_token.h input_transp.h \
+@@ -135,7 +135,7 @@
+ LIB_DIR = ../../lib
+ INC_DIR = ../../include
+ PLUGIN_MAP_SO = $(LIB_PREFIX)ldap$(LIB_SUFFIX) $(LIB_PREFIX)mysql$(LIB_SUFFIX) \
+- $(LIB_PREFIX)pgsql$(LIB_SUFFIX) $(LIB_PREFIX)sqlite$(LIB_SUFFIX) \
++ $(LIB_PREFIX)pgsql$(LIB_SUFFIX) $(LIB_PREFIX)redis$(LIB_SUFFIX) $(LIB_PREFIX)sqlite$(LIB_SUFFIX) \
+ $(LIB_PREFIX)lmdb$(LIB_SUFFIX) $(LIB_PREFIX)cdb$(LIB_SUFFIX) \
+ $(LIB_PREFIX)sdbm$(LIB_SUFFIX)
+ MAKES =
+@@ -171,6 +171,9 @@
+ $(LIB_PREFIX)pgsql$(LIB_SUFFIX): dict_pgsql.o
+ $(PLUGIN_LD) $(SHLIB_RPATH) -o $@ dict_pgsql.o $(AUXLIBS_PGSQL)
+
++$(LIB_PREFIX)redis$(LIB_SUFFIX): dict_redis.o
++ $(PLUGIN_LD) $(SHLIB_RPATH) -o $@ dict_redis.o $(AUXLIBS_REDIS)
++
+ $(LIB_PREFIX)sqlite$(LIB_SUFFIX): dict_sqlite.o
+ $(PLUGIN_LD) $(SHLIB_RPATH) -o $@ dict_sqlite.o $(AUXLIBS_SQLITE)
+
+@@ -535,6 +538,8 @@
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict mysql:/xx read >>surrogate.tmp 2>&1
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict pgsql:/xx write >>surrogate.tmp 2>&1
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict pgsql:/xx read >>surrogate.tmp 2>&1
++ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict redis:/xx write >>surrogate.tmp 2>&1
++ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict redis:/xx read >>surrogate.tmp 2>&1
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict sqlite:/xx write >>surrogate.tmp 2>&1
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict sqlite:/xx read >>surrogate.tmp 2>&1
+ echo get foo| $(SHLIB_ENV) $(VALGRIND) ./mail_dict memcache:/xx read >>surrogate.tmp 2>&1
+@@ -1231,6 +1236,17 @@
+ dict_pgsql.o: dict_pgsql.c
+ dict_pgsql.o: dict_pgsql.h
+ dict_pgsql.o: string_list.h
++dict_redis.o: ../../include/dict.h
++dict_redis.o: ../../include/msg.h
++dict_redis.o: ../../include/mymalloc.h
++dict_redis.o: ../../include/vbuf.h
++dict_redis.o: ../../include/vstream.h
++dict_redis.o: ../../include/vstring.h
++dict_redis.o: ../../include/stringops.h
++dict_redis.o: ../../include/sys_defs.h
++dict_redis.o: cfg_parser.h
++dict_redis.o: dict_redis.c
++dict_redis.o: dict_redis.h
+ dict_proxy.o: ../../include/argv.h
+ dict_proxy.o: ../../include/attr.h
+ dict_proxy.o: ../../include/check_arg.h
+@@ -1835,6 +1851,7 @@
+ mail_dict.o: dict_memcache.h
+ mail_dict.o: dict_mysql.h
+ mail_dict.o: dict_pgsql.h
++mail_dict.o: dict_redis.h
+ mail_dict.o: dict_proxy.h
+ mail_dict.o: dict_sqlite.h
+ mail_dict.o: dynamicmaps.h
+diff '--exclude=man' '--exclude=html' '--exclude=README_FILES' '--exclude=INSTALL' '--exclude=.indent.pro' -r -ur --new-file /var/tmp/postfix-3.6-20210224/src/postconf/postconf.c ./src/postconf/postconf.c
+--- /var/tmp/postfix-3.6-20210224/src/postconf/postconf.c 2021-01-03 12:08:51.000000000 -0500
++++ ./src/postconf/postconf.c 2021-03-13 16:50:27.846333680 -0500
+@@ -339,6 +339,10 @@
+ /* with support for SDBM databases.
+ /*
+ /* This feature is available with Postfix 2.2 and later.
++/* .IP "\fBredis\fR (read-only)"
++/* Redis database. This is described in \fBredis_table\fR(5).
++/*
++/* This feature is available with Postfix 3.7 and later.
+ /* .IP "\fBsocketmap\fR (read-only)"
+ /* Sendmail-style socketmap client. The table name is
+ /* \fBinet\fR:\fIhost\fR:\fIport\fR:\fIname\fR for a TCP/IP
diff --git a/redis_table b/redis_table
new file mode 100644
index 0000000..ce5547a
--- /dev/null
+++ b/redis_table
@@ -0,0 +1,81 @@
+#++
+# NAME
+# redis_table 5
+# SUMMARY
+# Postfix Redis client configuration
+# SYNOPSIS
+# \fBpostmap -q "\fIprefix:string\fB" redis:/etc/postfix/\fIfilename\fR
+#
+# \fBpostmap -q - redis:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
+# DESCRIPTION
+# The Postfix mail system uses optional tables for address
+# rewriting or mail routing. These tables are usually in
+# \fBdbm\fR or \fBdb\fR format.
+#
+# Alternatively, lookup tables can be specified as Redis
+# databases. In order to use Redis lookups, define a Redis
+# source as a lookup table in main.cf, for example:
+# .nf
+# virtual_alias_maps = redis:/etc/postfix/redis-valias-maps.cf
+# .fi
+#
+# The file /etc/postfix/redis-valias-maps.cf has the same
+# format as the Postfix main.cf file, and can specify the
+# parameters described below.
+# .IP "\fBhost\fR"
+# The IP address of the host that Postfix will try to connect
+# to and query from.
+# .sp
+# Example:
+# .nf
+# host = 127.0.0.1
+# .fi
+# .IP "\fBport\fR"
+# The TCP port that Postfix will connect to and query from.
+# If not specified the default of \fB6379\fR will be used.
+# .sp
+# Example:
+# .nf
+# port = 6379
+# .fi
+# .IP "\fBprefix\fR"
+# This is the prefix that is added to the query from Postfix
+# before it is passed to Redis for a lookup, allowing postfix
+# to use simple key:value lookups from Redis.
+# .sp
+# Examples:
+# .nf
+# prefix = VALI
+# prefix = LOCAL
+# prefix = REV
+# prefix = HELO
+# .fi
+# SEE ALSO
+# .na
+# .nf
+# postmap(1), Postfix lookup table manager
+# postconf(5), configuration parameters
+# ldap_table(5), LDAP lookup tables
+# mysql_table(5), MySQL lookup tables
+# sqlite_table(5), SQLite lookup tables
+# README FILES
+# .ad
+# .fi
+# Use "\fBpostconf readme_directory\fR" or
+# "\fBpostconf html_directory\fR" to locate this information.
+# .na
+# .nf
+# DATABASE_README, Postfix lookup table overview
+# REDIS_README, Postfix Redis client support
+# LICENSE
+# The Secure Mailer license must be distributed with this software.
+# HISTORY
+# Redis support was introduced with Postfix version 3.7.
+# AUTHOR(S)
+# Titus Jose
+# titus.nitt@gmail.com
+#
+# Updated by:
+# Duncan Bellamy
+# dunk@denkimushi.com
+#--