Index: conf/dynamicmaps.cf =================================================================== --- /dev/null +++ conf/dynamicmaps.cf @@ -0,0 +1,7 @@ +# Postfix dynamic maps configuration file. +# +# The first match found is the one that is used. +# Wildcards are not supported. +# +#type location of .so file open function (mkmap func) +#==== ================================ ============= ============ Index: conf/postfix-files =================================================================== --- conf/postfix-files.orig +++ conf/postfix-files @@ -65,6 +65,11 @@ $queue_directory/saved:d:$mail_owner:-:7 $queue_directory/trace:d:$mail_owner:-:700:ucr $daemon_directory/anvil:f:root:-:755 $daemon_directory/bounce:f:root:-:755 +$daemon_directory/dict_ldap.so:f:root:-:755 +$daemon_directory/dict_pcre.so:f:root:-:755 +$daemon_directory/dict_tcp.so:f:root:-:755 +$daemon_directory/dict_mysql.so:f:root:-:755 +$daemon_directory/dict_pgsql.so:f:root:-:755 $daemon_directory/cleanup:f:root:-:755 $daemon_directory/discard:f:root:-:755 $daemon_directory/dnsblog:f:root:-:755 @@ -97,6 +102,11 @@ $daemon_directory/tlsmgr:f:root:-:755 $daemon_directory/trivial-rewrite:f:root:-:755 $daemon_directory/verify:f:root:-:755 $daemon_directory/virtual:f:root:-:755 +/usr/lib/libpostfix-dns.so.1:f:root:-:755 +/usr/lib/libpostfix-global.so.1:f:root:-:755 +/usr/lib/libpostfix-tls.so.1:f:root:-:755 +/usr/lib/libpostfix-master.so.1:f:root:-:755 +/usr/lib/libpostfix-util.so.1:f:root:-:755 $daemon_directory/nqmgr:h:$daemon_directory/qmgr $daemon_directory/lmtp:h:$daemon_directory/smtp $command_directory/postalias:f:root:-:755 @@ -120,6 +130,7 @@ $config_directory/access:f:root:-:644:p1 $config_directory/aliases:f:root:-:644:p1 $config_directory/bounce.cf.default:f:root:-:644:1 $config_directory/canonical:f:root:-:644:p1 +$config_directory/dynamicmaps.cf:f:root:-:644:p $config_directory/cidr_table:f:root:-:644:o $config_directory/generic:f:root:-:644:p1 $config_directory/generics:f:root:-:644:o Index: src/dns/Makefile.in =================================================================== --- src/dns/Makefile.in.orig +++ src/dns/Makefile.in @@ -14,7 +14,7 @@ LIBS = ../../lib/libutil.a LIB_DIR = ../../lib INC_DIR = ../../include -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c all: $(LIB) @@ -31,12 +31,10 @@ tests: test dns_rr_to_pa_test dns_rr_to_ root_tests: $(LIB): $(OBJS) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-dns.so.1 -o $(LIB) $(OBJS) $(LIBS) $(SYSLIBS) $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR) - $(RANLIB) $(LIB_DIR)/$(LIB) update: $(LIB_DIR)/$(LIB) $(HDRS) -for i in $(HDRS); \ Index: src/global/Makefile.in =================================================================== --- src/global/Makefile.in.orig +++ src/global/Makefile.in @@ -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_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 @@ OBJS = abounce.o anvil_clnt.o been_here. 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_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 \ @@ -112,10 +112,13 @@ LIBS = ../../lib/libutil.a LIB_DIR = ../../lib INC_DIR = ../../include MAKES = +LDAPSO = dict_ldap.so +MYSQLSO = dict_mysql.so +PGSQLSO = dict_pgsql.so -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c -all: $(LIB) +all: $(LIB) $(LDAPSO) $(MYSQLSO) $(PGSQLSO) $(OBJS): ../../conf/makedefs.out @@ -125,14 +128,30 @@ Makefile: Makefile.in test: $(TESTPROG) $(LIB): $(OBJS) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-global.so.1 -o $(LIB) $(OBJS) $(LIBS) $(SYSLIBS) + +$(LDAPSO): dict_ldap.o + gcc -shared -Wl,-soname,dict_ldap.so -o $@ $? -lldap -llber -L../../lib -lutil -L. -lglobal + +$(MYSQLSO): dict_mysql.o + gcc -shared -Wl,-soname,dict_mysql.so -o $@ $? -L/usr/lib/mysql -lmysqlclient -L. -lutil -lglobal + +$(PGSQLSO): dict_pgsql.o + gcc -shared -Wl,-soname,dict_pgsql.so -o $@ $? -lpq -L. -lutil -lglobal $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR) - $(RANLIB) $(LIB_DIR)/$(LIB) -update: $(LIB_DIR)/$(LIB) $(HDRS) +$(LIB_DIR)/$(LDAPSO): $(LDAPSO) + cp $(LDAPSO) $(LIB_DIR) + +$(LIB_DIR)/$(MYSQLSO): $(MYSQLSO) + cp $(MYSQLSO) $(LIB_DIR) + +$(LIB_DIR)/$(PGSQLSO): $(PGSQLSO) + cp $(PGSQLSO) $(LIB_DIR) + +update: $(LIB_DIR)/$(LIB) $(LIB_DIR)/${LDAPSO} $(LIB_DIR)/${MYSQLSO} $(LIB_DIR)/${PGSQLSO} $(HDRS) -for i in $(HDRS); \ do \ cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \ @@ -577,7 +596,7 @@ lint: lint $(DEFS) $(SRCS) $(LINTFIX) clean: - rm -f *.o $(LIB) *core $(TESTPROG) junk + rm -f *.o $(LIB) $(LDAPSO) $(MYSQLSO) $(PGSQLSO) *core $(TESTPROG) junk rm -rf printfck tidy: clean Index: src/global/mail_conf.c =================================================================== --- src/global/mail_conf.c.orig +++ src/global/mail_conf.c @@ -190,6 +190,13 @@ void mail_conf_suck(void) if (dict_load_file_xt(CONFIG_DICT, path) == 0) msg_fatal("open %s: %m", path); myfree(path); + +#ifndef NO_DYNAMIC_MAPS + path = concatenate(var_config_dir, "/", "dynamicmaps.cf", (char *) 0); + dict_open_dlinfo(path); + myfree(path); +#endif + } /* mail_conf_flush - discard configuration dictionary */ Index: src/global/mail_dict.c =================================================================== --- src/global/mail_dict.c.orig +++ src/global/mail_dict.c @@ -47,6 +47,7 @@ typedef struct { static const DICT_OPEN_INFO dict_open_info[] = { DICT_TYPE_PROXY, dict_proxy_open, +#ifndef MAX_DYNAMIC_MAPS #ifdef HAS_LDAP DICT_TYPE_LDAP, dict_ldap_open, #endif @@ -60,6 +61,7 @@ static const DICT_OPEN_INFO dict_open_in DICT_TYPE_SQLITE, dict_sqlite_open, #endif DICT_TYPE_MEMCACHE, dict_memcache_open, +#endif /* MAX_DYNAMIC_MAPS */ 0, }; Index: src/global/mail_params.c =================================================================== --- src/global/mail_params.c.orig +++ src/global/mail_params.c @@ -79,6 +79,7 @@ /* char *var_export_environ; /* char *var_debug_peer_list; /* int var_debug_peer_level; +/* int var_command_maxtime; /* int var_in_flow_delay; /* int var_fault_inj_code; /* char *var_bounce_service; @@ -268,6 +269,7 @@ char *var_import_environ; char *var_export_environ; char *var_debug_peer_list; int var_debug_peer_level; +int var_command_maxtime; int var_fault_inj_code; char *var_bounce_service; char *var_cleanup_service; @@ -279,6 +281,7 @@ char *var_showq_service; char *var_error_service; char *var_flush_service; char *var_verify_service; +char *var_scache_service; char *var_trace_service; char *var_proxymap_service; char *var_proxywrite_service; Index: src/global/mkmap_open.c =================================================================== --- src/global/mkmap_open.c.orig +++ src/global/mkmap_open.c @@ -82,7 +82,7 @@ * We use a different table (in dict_open.c) when querying maps. */ typedef struct { - char *type; + const char *type; MKMAP *(*before_open) (const char *); } MKMAP_OPEN_INFO; @@ -161,7 +161,16 @@ MKMAP *mkmap_open(const char *type, con */ for (mp = mkmap_types; /* void */ ; mp++) { if (mp->type == 0) +#ifndef NO_DYNAMIC_MAPS + { + static MKMAP_OPEN_INFO oi; + oi.before_open=(MKMAP*(*)(const char*))dict_mkmap_func(type); + oi.type=type; + mp=&oi; + } +#else msg_fatal("unsupported map type: %s", type); +#endif if (strcmp(type, mp->type) == 0) break; } Index: src/master/Makefile.in =================================================================== --- src/master/Makefile.in.orig +++ src/master/Makefile.in @@ -20,7 +20,7 @@ LIB_DIR = ../../lib INC_DIR = ../../include BIN_DIR = ../../libexec -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) `for i in $(LIB_OBJ); do [ $$i = $@ ] && echo -fPIC; done` $(CFLAGS) -c $*.c all: $(PROG) $(LIB) @@ -39,12 +39,10 @@ tests: root_tests: $(LIB): $(LIB_OBJ) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-master.so.1 -o $(LIB) $(LIB_OBJ) $(LIBS) $(SYSLIBS) $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR)/$(LIB) - $(RANLIB) $(LIB_DIR)/$(LIB) $(BIN_DIR)/$(PROG): $(PROG) cp $(PROG) $(BIN_DIR) Index: src/milter/Makefile.in =================================================================== --- src/milter/Makefile.in.orig +++ src/milter/Makefile.in @@ -14,7 +14,7 @@ LIB_DIR = ../../lib INC_DIR = ../../include MAKES = -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c all: $(LIB) @@ -30,12 +30,10 @@ tests: root_tests: $(LIB): $(OBJS) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-milter.so.1 -o $(LIB) $(OBJS) $(LIBS) $(SYSLIBS) $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR) - $(RANLIB) $(LIB_DIR)/$(LIB) update: $(LIB_DIR)/$(LIB) $(HDRS) -for i in $(HDRS); \ Index: src/postconf/Makefile.in =================================================================== --- src/postconf/Makefile.in.orig +++ src/postconf/Makefile.in @@ -595,6 +595,7 @@ postconf_node.o: postconf_node.c postconf_other.o: ../../include/argv.h postconf_other.o: ../../include/dict.h postconf_other.o: ../../include/htable.h +postconf_other.o: ../../include/mail_params.h postconf_other.o: ../../include/mbox_conf.h postconf_other.o: ../../include/sys_defs.h postconf_other.o: ../../include/vbuf.h Index: src/postconf/postconf_other.c =================================================================== --- src/postconf/postconf_other.c.orig +++ src/postconf/postconf_other.c @@ -46,10 +46,15 @@ #include #include #include +#include +#include +#include /* Global library. */ #include +#include +#include /* XSASL library. */ @@ -66,6 +71,19 @@ void show_maps(void) ARGV *maps_argv; int i; +#ifndef NO_DYNAMIC_MAPS + char *path; + char *config_dir; + + if (var_config_dir) + myfree(var_config_dir); + var_config_dir = mystrdup((config_dir = safe_getenv(CONF_ENV_PATH)) != 0 ? + config_dir : DEF_CONFIG_DIR); /* XXX */ + path = concatenate(var_config_dir, "/", "dynamicmaps.cf", (char *) 0); + dict_open_dlinfo(path); + myfree(path); +#endif + maps_argv = dict_mapnames(); for (i = 0; i < maps_argv->argc; i++) vstream_printf("%s\n", maps_argv->argv[i]); Index: src/postmap/postmap.c =================================================================== --- src/postmap/postmap.c.orig +++ src/postmap/postmap.c @@ -5,7 +5,7 @@ /* Postfix lookup table management /* SYNOPSIS /* .fi -/* \fBpostmap\fR [\fB-Nbfhimnoprsvw\fR] [\fB-c \fIconfig_dir\fR] +/* \fBpostmap\fR [\fB-Nbfhimnoprsuvw\fR] [\fB-c \fIconfig_dir\fR] /* [\fB-d \fIkey\fR] [\fB-q \fIkey\fR] /* [\fIfile_type\fR:]\fIfile_name\fR ... /* DESCRIPTION @@ -151,6 +151,8 @@ /* .sp /* This feature is available in Postfix version 2.2 and later, /* and is not available for all database types. +/* .IP \fB-u\fR +/* Upgrade the database to the current version. /* .IP \fB-v\fR /* Enable verbose logging for debugging purposes. Multiple \fB-v\fR /* options make the software increasingly verbose. @@ -748,6 +750,18 @@ static void postmap_seq(const char *map_ dict_close(dict); } +/* postmap_upgrade - upgrade a map */ + +static int postmap_upgrade(const char *map_type, const char *map_name) +{ + DICT *dict; + + dict = dict_open3(map_type, map_name, O_RDWR, + DICT_FLAG_LOCK|DICT_FLAG_UPGRADE); + dict_close(dict); + return (dict != 0); +} + /* usage - explain */ static NORETURN usage(char *myname) @@ -768,6 +782,7 @@ int main(int argc, char **argv) int postmap_flags = POSTMAP_FLAG_AS_OWNER | POSTMAP_FLAG_SAVE_PERM; int open_flags = O_RDWR | O_CREAT | O_TRUNC; int dict_flags = DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_FIX; + int upgrade = 0; char *query = 0; char *delkey = 0; int sequence = 0; @@ -817,7 +832,7 @@ int main(int argc, char **argv) /* * Parse JCL. */ - while ((ch = GETOPT(argc, argv, "Nbc:d:fhimnopq:rsvw")) > 0) { + while ((ch = GETOPT(argc, argv, "Nbc:d:fhimnopq:rsuvw")) > 0) { switch (ch) { default: usage(argv[0]); @@ -834,8 +849,8 @@ int main(int argc, char **argv) msg_fatal("out of memory"); break; case 'd': - if (sequence || query || delkey) - msg_fatal("specify only one of -s -q or -d"); + if (sequence || query || delkey || upgrade) + msg_fatal("specify only one of -s -q -u or -d"); delkey = optarg; break; case 'f': @@ -861,8 +876,8 @@ int main(int argc, char **argv) postmap_flags &= ~POSTMAP_FLAG_SAVE_PERM; break; case 'q': - if (sequence || query || delkey) - msg_fatal("specify only one of -s -q or -d"); + if (sequence || query || delkey || upgrade) + msg_fatal("specify only one of -s -q -u or -d"); query = optarg; break; case 'r': @@ -870,10 +885,15 @@ int main(int argc, char **argv) dict_flags |= DICT_FLAG_DUP_REPLACE; break; case 's': - if (query || delkey) - msg_fatal("specify only one of -s or -q or -d"); + if (query || delkey || upgrade) + msg_fatal("specify only one of -s or -q -u or -d"); sequence = 1; break; + case 'u': + if (sequence || query || delkey || upgrade) + msg_fatal("specify only one of -s -q -u or -d"); + upgrade=1; + break; case 'v': msg_verbose++; break; @@ -944,6 +964,21 @@ int main(int argc, char **argv) exit(0); } exit(1); + } else if (upgrade) { /* Upgrade the map(s) */ + int success = 1; + if (optind + 1 > argc) + usage(argv[0]); + while (optind < argc) { + if ((path_name = split_at(argv[optind], ':')) != 0) { + success &= postmap_upgrade(argv[optind], path_name); + } else { + success &= postmap_upgrade(var_db_type, path_name); + } + if (!success) + exit(1); + optind++; + } + exit(0); } else { /* create/update map(s) */ if (optind + 1 > argc) usage(argv[0]); Index: src/tls/Makefile.in =================================================================== --- src/tls/Makefile.in.orig +++ src/tls/Makefile.in @@ -24,7 +24,7 @@ LIB_DIR = ../../lib INC_DIR = ../../include MAKES = -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c all: $(LIB) @@ -40,12 +40,10 @@ tests: root_tests: $(LIB): $(OBJS) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-tls.so.1 -o $(LIB) $(OBJS) $(LIBS) $(SYSLIBS) $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR) - $(RANLIB) $(LIB_DIR)/$(LIB) update: $(LIB_DIR)/$(LIB) $(HDRS) -for i in $(HDRS); \ Index: src/tls/tls_client.c =================================================================== --- src/tls/tls_client.c.orig +++ src/tls/tls_client.c @@ -156,6 +156,8 @@ #define STR vstring_str #define LEN VSTRING_LEN +int var_tls_daemon_rand_bytes; + /* load_clnt_session - load session from client cache (non-callback) */ static SSL_SESSION *load_clnt_session(TLS_SESS_STATE *TLScontext) Index: src/tls/tls_server.c =================================================================== --- src/tls/tls_server.c.orig +++ src/tls/tls_server.c @@ -155,6 +155,8 @@ #define STR(x) vstring_str(x) #define LEN(x) VSTRING_LEN(x) +int var_tls_daemon_rand_bytes; + /* Application-specific. */ /* Index: src/util/Makefile.in =================================================================== --- src/util/Makefile.in.orig +++ src/util/Makefile.in @@ -34,15 +34,16 @@ SRCS = alldig.c allprint.c argv.c argv_s unix_pass_listen.c unix_pass_trigger.c edit_file.c inet_windowsize.c \ unix_pass_fd_fix.c dict_cache.c valid_utf_8.c dict_thash.c \ ip_match.c nbbio.c stream_pass_connect.c base32_code.c dict_test.c \ - dict_fail.c msg_rate_delay.c dict_surrogate.c warn_stat.c + dict_fail.c msg_rate_delay.c dict_surrogate.c warn_stat.c \ + load_lib.c sdbm.c OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ chroot_uid.o cidr_match.o clean_env.o close_on_exec.o concatenate.o \ ctable.o dict.o dict_alloc.o dict_cdb.o dict_cidr.o dict_db.o \ dict_dbm.o dict_debug.o dict_env.o dict_ht.o dict_ni.o dict_nis.o \ - dict_nisplus.o dict_open.o dict_pcre.o dict_regexp.o dict_sdbm.o \ - dict_static.o dict_tcp.o dict_unix.o dir_forest.o doze.o dummy_read.o \ + dict_nisplus.o dict_open.o dict_regexp.o dict_sdbm.o \ + dict_static.o dict_unix.o dir_forest.o doze.o dummy_read.o \ dummy_write.o duplex_pipe.o environ.o events.o exec_command.o \ fifo_listen.o fifo_trigger.o file_limit.o find_inet.o fsspace.o \ fullname.o get_domainname.o get_hostname.o hex_code.o hex_quote.o \ @@ -70,7 +71,8 @@ OBJS = alldig.o allprint.o argv.o argv_s unix_pass_listen.o unix_pass_trigger.o edit_file.o inet_windowsize.o \ unix_pass_fd_fix.o dict_cache.o valid_utf_8.o dict_thash.o \ ip_match.o nbbio.o stream_pass_connect.o base32_code.o dict_test.o \ - dict_fail.o msg_rate_delay.o dict_surrogate.o warn_stat.o + dict_fail.o msg_rate_delay.o dict_surrogate.o warn_stat.o \ + load_lib.o sdbm.o HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \ chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \ dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \ @@ -84,7 +86,7 @@ HDRS = argv.h attr.h attr_clnt.h auto_cl msg_output.h msg_syslog.h msg_vstream.h mvect.h myaddrinfo.h myflock.h \ mymalloc.h myrand.h name_code.h name_mask.h netstring.h nvtable.h \ open_as.h open_lock.h percentm.h posix_signals.h readlline.h ring.h \ - safe.h safe_open.h sane_accept.h sane_connect.h sane_fsops.h \ + safe.h safe_open.h sane_accept.h sane_connect.h sane_fsops.h sdbm.h load_lib.h \ sane_socketpair.h sane_time.h scan_dir.h set_eugid.h set_ugid.h \ sigdelay.h sock_addr.h spawn_command.h split_at.h stat_as.h \ stringops.h sys_defs.h timed_connect.h timed_wait.h trigger.h \ @@ -98,6 +100,8 @@ DEFS = -I. -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) FILES = Makefile $(SRCS) $(HDRS) INCL = +PCRESO = dict_pcre.so +TCPSO = dict_tcp.so LIB = libutil.a TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ fifo_rdonly_bug fifo_rdwr_bug fifo_trigger fsspace fullname \ @@ -113,10 +117,11 @@ TESTPROG= dict_open dup2_pass_on_exec ev LIB_DIR = ../../lib INC_DIR = ../../include +LIBS = $(LIB_DIR)/$(LIB) $(LIB_DIR)/$(PCRESO) $(LIB_DIR)/$(TCPSO) -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c -all: $(LIB) +all: $(LIB) $(PCRESO) $(TCPSO) $(OBJS): ../../conf/makedefs.out @@ -125,15 +130,25 @@ Makefile: Makefile.in test: $(TESTPROG) +$(PCRESO): dict_pcre.o + gcc -shared -Wl,-soname,dict_pcre.so -o $@ $? -lpcre -L. -lutil + +$(TCPSO): dict_tcp.o + gcc -shared -Wl,-soname,dict_tcp.so -o $@ $? -L. -lutil + $(LIB): $(OBJS) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-util.so.1 -o $(LIB) $(OBJS) -ldl $(SYSLIBS) $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR) - $(RANLIB) $(LIB_DIR)/$(LIB) -update: $(LIB_DIR)/$(LIB) $(HDRS) +$(LIB_DIR)/$(PCRESO): $(PCRESO) + cp $(PCRESO) $(LIB_DIR) + +$(LIB_DIR)/$(TCPSO): $(TCPSO) + cp $(TCPSO) $(LIB_DIR) + +update: $(LIBS) $(HDRS) -for i in $(HDRS); \ do \ cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \ @@ -155,7 +170,8 @@ lint: lint $(DEFS) $(SRCS) $(LINTFIX) clean: - rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) *.tmp + rm -f *.o $(LIB) $(PCRESO) $(TCPSO) *core $(TESTPROG) \ + junk $(MAKES) *.tmp rm -rf printfck tidy: clean Index: src/util/dict.h =================================================================== --- src/util/dict.h.orig +++ src/util/dict.h @@ -80,6 +80,7 @@ extern DICT *dict_debug(DICT *); #define DICT_FLAG_NO_UNAUTH (1<<13) /* disallow unauthenticated data */ #define DICT_FLAG_FOLD_FIX (1<<14) /* case-fold key with fixed-case map */ #define DICT_FLAG_FOLD_MUL (1<<15) /* case-fold key with multi-case map */ +#define DICT_FLAG_UPGRADE (1<<30) /* Upgrade the db */ #define DICT_FLAG_FOLD_ANY (DICT_FLAG_FOLD_FIX | DICT_FLAG_FOLD_MUL) #define DICT_FLAG_OPEN_LOCK (1<<16) /* open file with exclusive lock */ @@ -166,6 +167,11 @@ extern int dict_error(const char *); extern DICT *dict_open(const char *, int, int); extern DICT *dict_open3(const char *, const char *, int, int); extern void dict_open_register(const char *, DICT *(*) (const char *, int, int)); +#ifndef NO_DYNAMIC_MAPS +extern void dict_open_dlinfo(const char *path); +typedef void* (*dict_mkmap_func_t)(const char *); +dict_mkmap_func_t dict_mkmap_func(const char *dict_type); +#endif #define dict_get(dp, key) ((const char *) (dp)->lookup((dp), (key))) #define dict_put(dp, key, val) (dp)->update((dp), (key), (val)) Index: src/util/dict_db.c =================================================================== --- src/util/dict_db.c.orig +++ src/util/dict_db.c @@ -693,6 +693,12 @@ static DICT *dict_db_open(const char *cl msg_fatal("set DB cache size %d: %m", dict_db_cache_size); if (type == DB_HASH && db->set_h_nelem(db, DICT_DB_NELM) != 0) msg_fatal("set DB hash element count %d: %m", DICT_DB_NELM); + if (dict_flags & DICT_FLAG_UPGRADE) { + if (msg_verbose) + msg_info("upgrading database %s",db_path); + if ((errno = db->upgrade(db,db_path,0)) != 0) + msg_fatal("upgrade of database %s: %m",db_path); + } #if DB_VERSION_MAJOR == 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR > 0) if ((errno = db->open(db, 0, db_path, 0, type, db_flags, 0644)) != 0) FREE_RETURN(dict_surrogate(class, path, open_flags, dict_flags, Index: src/util/dict_dbm.c =================================================================== --- src/util/dict_dbm.c.orig +++ src/util/dict_dbm.c @@ -417,6 +417,10 @@ DICT *dict_dbm_open(const char *path, char *dbm_path; int lock_fd; +#ifdef HAVE_GDBM + msg_fatal("%s: gdbm maps use locking that is incompatible with postfix. Use a hash map instead.", + path); +#endif /* * Note: DICT_FLAG_LOCK is used only by programs that do fine-grained (in * the time domain) locking while accessing individual database records. Index: src/util/dict_open.c =================================================================== --- src/util/dict_open.c.orig +++ src/util/dict_open.c @@ -44,6 +44,8 @@ /* DICT *(*open) (const char *, int, int); /* /* ARGV *dict_mapnames() +/* +/* void (*)() dict_mkmap_func(const char *dict_type) /* DESCRIPTION /* This module implements a low-level interface to multiple /* physical dictionary types. @@ -166,6 +168,9 @@ /* /* dict_mapnames() returns a sorted list with the names of all available /* dictionary types. +/* +/* dict_mkmap_func() returns a pointer to the mkmap setup function +/* for the given map type, as given in /etc/dynamicmaps.cf /* DIAGNOSTICS /* Fatal error: open error, unsupported dictionary type, attempt to /* update non-writable dictionary. @@ -213,6 +218,9 @@ #include #endif +#include +#include + /* Utility library. */ #include @@ -241,6 +249,27 @@ #include #include +#ifndef NO_DYNAMIC_MAPS +#include +#include +#include +#include +#include + + /* + * Interface for dynamic map loading. + */ +typedef struct { + const char *pattern; + const char *soname; + const char *openfunc; + const char *mkmapfunc; +} DLINFO; + +static DLINFO *dict_dlinfo; +static DLINFO *dict_open_dlfind(const char *type); +#endif + /* * lookup table for available map types. */ @@ -256,7 +285,9 @@ static const DICT_OPEN_INFO dict_open_in DICT_TYPE_ENVIRON, dict_env_open, DICT_TYPE_HT, dict_ht_open, DICT_TYPE_UNIX, dict_unix_open, +#ifndef MAX_DYNAMIC_MAPS DICT_TYPE_TCP, dict_tcp_open, +#endif #ifdef HAS_SDBM DICT_TYPE_SDBM, dict_sdbm_open, #endif @@ -276,9 +307,11 @@ static const DICT_OPEN_INFO dict_open_in #ifdef HAS_NETINFO DICT_TYPE_NETINFO, dict_ni_open, #endif +#ifndef MAX_DYNAMIC_MAPS #ifdef HAS_PCRE DICT_TYPE_PCRE, dict_pcre_open, #endif +#endif /* MAX_DYNAMIC_MAPS */ #ifdef HAS_POSIX_REGEXP DICT_TYPE_REGEXP, dict_regexp_open, #endif @@ -338,9 +371,32 @@ DICT *dict_open3(const char *dict_type dict_type, dict_name); if (dict_open_hash == 0) dict_open_init(); - if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0) + if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0) { +#ifdef NO_DYNAMIC_MAPS return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags, "unsupported dictionary type: %s", dict_type)); +#else + struct stat st; + LIB_FN fn[2]; + DICT *(*open) (const char *, int, int); + DLINFO *dl=dict_open_dlfind(dict_type); + if (!dl) + msg_fatal("%s: unsupported dictionary type: %s: Is the postfix-%s package installed?", myname, dict_type, dict_type); + if (stat(dl->soname,&st) < 0) { + msg_fatal("%s: unsupported dictionary type: %s (%s not found. Is the postfix-%s package installed?)", + myname, dict_type, dl->soname, dict_type); + } + fn[0].name = dl->openfunc; + fn[0].ptr = (void**)&open; + fn[1].name = NULL; + load_library_symbols(dl->soname, fn, NULL); + dict_open_register(dict_type, open); + dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type); +#endif + } + if (msg_verbose>1) { + msg_info("%s: calling %s open routine",myname,dict_type); + } if ((dict = dp->open(dict_name, open_flags, dict_flags)) == 0) return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags, "cannot open %s:%s: %m", dict_type, dict_name)); @@ -359,6 +415,36 @@ DICT *dict_open3(const char *dict_type return (dict); } +dict_mkmap_func_t dict_mkmap_func(const char *dict_type) +{ + char *myname="dict_mkmap_func"; + struct stat st; + LIB_FN fn[2]; + dict_mkmap_func_t mkmap; + DLINFO *dl; +#ifndef NO_DYNAMIC_MAPS + if (!dict_dlinfo) + msg_fatal("dlinfo==NULL"); + dl=dict_open_dlfind(dict_type); + if (!dl) + msg_fatal("%s: unsupported dictionary type: %s: Is the postfix-%s package installed?", myname, dict_type, dict_type); + if (stat(dl->soname,&st) < 0) { + msg_fatal("%s: unsupported dictionary type: %s (%s not found. Is the postfix-%s package installed?)", + myname, dict_type, dl->soname, dict_type); + } + if (!dl->mkmapfunc) + msg_fatal("%s: unsupported dictionary type: %s does not allow map creation.", myname, dict_type); + + fn[0].name = dl->mkmapfunc; + fn[0].ptr = (void**)&mkmap; + fn[1].name = NULL; + load_library_symbols(dl->soname, fn, NULL); + return mkmap; +#else + return (void(*)())NULL; +#endif +} + /* dict_open_register - register dictionary type */ void dict_open_register(const char *type, @@ -392,6 +478,9 @@ ARGV *dict_mapnames() HTABLE_INFO **ht; DICT_OPEN_INFO *dp; ARGV *mapnames; +#ifndef NO_DYNAMIC_MAPS + DLINFO *dlp; +#endif if (dict_open_hash == 0) dict_open_init(); @@ -400,6 +489,13 @@ ARGV *dict_mapnames() dp = (DICT_OPEN_INFO *) ht[0]->value; argv_add(mapnames, dp->type, ARGV_END); } +#ifndef NO_DYNAMIC_MAPS + if (!dict_dlinfo) + msg_fatal("dlinfo==NULL"); + for (dlp=dict_dlinfo; dlp->pattern; dlp++) { + argv_add(mapnames, dlp->pattern, ARGV_END); + } +#endif qsort((void *) mapnames->argv, mapnames->argc, sizeof(mapnames->argv[0]), dict_sort_alpha_cpp); myfree((char *) ht_info); @@ -407,6 +503,87 @@ ARGV *dict_mapnames() return mapnames; } +#ifndef NO_DYNAMIC_MAPS +#define STREQ(x,y) (x == y || (x[0] == y[0] && strcmp(x,y) == 0)) + +void dict_open_dlinfo(const char *path) +{ + char *myname="dict_open_dlinfo"; + VSTREAM *conf_fp=vstream_fopen(path,O_RDONLY,0); + VSTRING *buf = vstring_alloc(100); + char *cp; + ARGV *argv; + MVECT vector; + int nelm=0; + int linenum=0; + + dict_dlinfo=(DLINFO*)mvect_alloc(&vector,sizeof(DLINFO),3,NULL,NULL); + + if (!conf_fp) { + msg_warn("%s: cannot open %s. No dynamic maps will be allowed.", + myname, path); + } else { + while (vstring_get_nonl(buf,conf_fp) != VSTREAM_EOF) { + cp = vstring_str(buf); + linenum++; + if (*cp == '#' || *cp == '\0') + continue; + argv = argv_split(cp, " \t"); + if (argv->argc != 3 && argv->argc != 4) { + msg_fatal("%s: Expected \"pattern .so-name open-function [mkmap-function]\" at line %d", + myname, linenum); + } + if (STREQ(argv->argv[0],"*")) { + msg_warn("%s: wildcard dynamic map entry no longer supported.", + myname); + continue; + } + if (argv->argv[1][0] != '/') { + msg_fatal("%s: .so name must begin with a \"/\" at line %d", + myname, linenum); + } + if (nelm >= vector.nelm) { + dict_dlinfo=(DLINFO*)mvect_realloc(&vector,vector.nelm+3); + } + dict_dlinfo[nelm].pattern = mystrdup(argv->argv[0]); + dict_dlinfo[nelm].soname = mystrdup(argv->argv[1]); + dict_dlinfo[nelm].openfunc = mystrdup(argv->argv[2]); + if (argv->argc==4) + dict_dlinfo[nelm].mkmapfunc = mystrdup(argv->argv[3]); + else + dict_dlinfo[nelm].mkmapfunc = NULL; + nelm++; + argv_free(argv); + } + } + if (nelm >= vector.nelm) { + dict_dlinfo=(DLINFO*)mvect_realloc(&vector,vector.nelm+1); + } + dict_dlinfo[nelm].pattern = NULL; + dict_dlinfo[nelm].soname = NULL; + dict_dlinfo[nelm].openfunc = NULL; + dict_dlinfo[nelm].mkmapfunc = NULL; + if (conf_fp) + vstream_fclose(conf_fp); + vstring_free(buf); +} + +static DLINFO *dict_open_dlfind(const char *type) +{ + DLINFO *dp; + + if (!dict_dlinfo) + return NULL; + + for (dp=dict_dlinfo; dp->pattern; dp++) { + if (STREQ(dp->pattern,type)) + return dp; + } + return NULL; +} + +#endif /* !NO_DYNAMIC_MAPS */ + #ifdef TEST /* Index: src/util/load_lib.c =================================================================== --- /dev/null +++ src/util/load_lib.c @@ -0,0 +1,135 @@ +/*++ +/* NAME +/* load_lib 3 +/* SUMMARY +/* library loading wrappers +/* SYNOPSIS +/* #include +/* +/* extern int load_library_symbols(const char *, LIB_FN *, LIB_FN *); +/* const char *libname; +/* LIB_FN *libfuncs; +/* LIB_FN *libdata; +/* +/* DESCRIPTION +/* This module loads functions from libraries, returnine pointers +/* to the named functions. +/* +/* load_library_symbols() loads all of the desired functions, and +/* returns zero for success, or exits via msg_fatal(). +/* +/* SEE ALSO +/* msg(3) diagnostics interface +/* DIAGNOSTICS +/* Problems are reported via the msg(3) diagnostics routines: +/* library not found, symbols not found, other fatal errors. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* LaMont Jones +/* Hewlett-Packard Company +/* 3404 Harmony Road +/* Fort Collins, CO 80528, USA +/* +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System libraries. */ + +#include "sys_defs.h" +#include +#include +#include +#if defined(HAS_DLOPEN) +#include +#elif defined(HAS_SHL_LOAD) +#include +#endif + +/* Application-specific. */ + +#include "msg.h" +#include "load_lib.h" + +extern int load_library_symbols(const char * libname, LIB_FN * libfuncs, LIB_FN * libdata) +{ + char *myname = "load_library_symbols"; + LIB_FN *fn; + +#if defined(HAS_DLOPEN) + void *handle; + char *emsg; + + handle=dlopen(libname,RTLD_NOW); + emsg=dlerror(); + if (emsg) { + msg_fatal("%s: dlopen failure loading %s: %s", myname, libname, emsg); + } + + if (libfuncs) { + for (fn=libfuncs; fn->name; fn++) { + *(fn->ptr) = dlsym(handle,fn->name); + emsg=dlerror(); + if (emsg) { + msg_fatal("%s: dlsym failure looking up %s in %s: %s", myname, + fn->name, libname, emsg); + } + if (msg_verbose>1) { + msg_info("loaded %s = %lx",fn->name, *((long*)(fn->ptr))); + } + } + } + + if (libdata) { + for (fn=libdata; fn->name; fn++) { + *(fn->ptr) = dlsym(handle,fn->name); + emsg=dlerror(); + if (emsg) { + msg_fatal("%s: dlsym failure looking up %s in %s: %s", myname, + fn->name, libname, emsg); + } + if (msg_verbose>1) { + msg_info("loaded %s = %lx",fn->name, *((long*)(fn->ptr))); + } + } + } +#elif defined(HAS_SHL_LOAD) + shl_t handle; + + handle = shl_load(libname,BIND_IMMEDIATE,0); + + if (libfuncs) { + for (fn=libfuncs; fn->name; fn++) { + if (shl_findsym(&handle,fn->name,TYPE_PROCEDURE,fn->ptr) != 0) { + msg_fatal("%s: shl_findsym failure looking up %s in %s: %m", + myname, fn->name, libname); + } + if (msg_verbose>1) { + msg_info("loaded %s = %x",fn->name, *((long*)(fn->ptr))); + } + } + } + + if (libdata) { + for (fn=libdata; fn->name; fn++) { + if (shl_findsym(&handle,fn->name,TYPE_DATA,fn->ptr) != 0) { + msg_fatal("%s: shl_findsym failure looking up %s in %s: %m", + myname, fn->name, libname); + } + if (msg_verbose>1) { + msg_info("loaded %s = %x",fn->name, *((long*)(fn->ptr))); + } + } + } + +#else + msg_fatal("%s: need dlopen or shl_load support for dynamic libraries", + myname); +#endif + return 0; +} Index: src/util/load_lib.h =================================================================== --- /dev/null +++ src/util/load_lib.h @@ -0,0 +1,41 @@ +#ifndef _LOAD_LIB_H_INCLUDED_ +#define _LOAD_LIB_H_INCLUDED_ + +/*++ +/* NAME +/* load_lib 3h +/* SUMMARY +/* library loading wrappers +/* SYNOPSIS +/* #include "load_lib.h" +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +/* NULL name terminates list */ +typedef struct LIB_FN { + const char *name; + void **ptr; +} LIB_FN; + +extern int load_library_symbols(const char *, LIB_FN *, LIB_FN *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* LaMont Jones +/* Hewlett-Packard Company +/* 3404 Harmony Road +/* Fort Collins, CO 80528, USA +/* +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif Index: src/util/sdbm.c =================================================================== --- /dev/null +++ src/util/sdbm.c @@ -0,0 +1,972 @@ +/*++ +/* NAME +/* sdbm 3h +/* SUMMARY +/* SDBM Simple DBM: ndbm work-alike hashed database library +/* SYNOPSIS +/* include "sdbm.h" +/* DESCRIPTION +/* This file includes the public domain SDBM (ndbm work-alike hashed +/* database library), based on Per-Aake Larson's Dynamic Hashing +/* algorithms. BIT 18 (1978). +/* author: oz@nexus.yorku.ca +/* status: public domain +/* The file has been patched following the advice of Uwe Ohse +/* : +/* -------------------------------------------------------------- +/* this patch fixes a problem with sdbms .dir file, which arrises when +/* a second .dir block is needed for the first time. read() returns 0 +/* in that case, and the library forgot to initialize that new block. +/* +/* A related problem is that the calculation of db->maxbno is wrong. +/* It just appends 4096*BYTESIZ bits, which is not enough except for +/* small databases (.dir basically doubles everytime it's too small). +/* -------------------------------------------------------------- +/* According to Uwe Ohse, the patch has also been submitted to the +/* author of SDBM. (The 4096*BYTESIZ bits comment may apply with a +/* different size for Postfix/TLS, as the patch was sent against the +/* original SDBM distributiona and for Postfix/TLS I have changed the +/* default sizes. +/* .nf +/*--*/ + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. + * + * core routines + */ + +#include +#include +#ifdef WIN32 +#include +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#ifdef __STDC__ +#include +#endif +#include + +#include + +/* + * useful macros + */ +#define bad(x) ((x).dptr == NULL || (x).dsize <= 0) +#define exhash(item) sdbm_hash((item).dptr, (item).dsize) +#define ioerr(db) ((db)->flags |= DBM_IOERR) + +#define OFF_PAG(off) (long) (off) * PBLKSIZ +#define OFF_DIR(off) (long) (off) * DBLKSIZ + +static long masks[] = +{ + 000000000000, 000000000001, 000000000003, 000000000007, + 000000000017, 000000000037, 000000000077, 000000000177, + 000000000377, 000000000777, 000000001777, 000000003777, + 000000007777, 000000017777, 000000037777, 000000077777, + 000000177777, 000000377777, 000000777777, 000001777777, + 000003777777, 000007777777, 000017777777, 000037777777, + 000077777777, 000177777777, 000377777777, 000777777777, + 001777777777, 003777777777, 007777777777, 017777777777 +}; + +datum nullitem = +{NULL, 0}; + +typedef struct +{ + int dirf; /* directory file descriptor */ + int pagf; /* page file descriptor */ + int flags; /* status/error flags, see below */ + long maxbno; /* size of dirfile in bits */ + long curbit; /* current bit number */ + long hmask; /* current hash mask */ + long blkptr; /* current block for nextkey */ + int keyptr; /* current key for nextkey */ + long blkno; /* current page to read/write */ + long pagbno; /* current page in pagbuf */ + char *pagbuf; /* page file block buffer */ + long dirbno; /* current block in dirbuf */ + char *dirbuf; /* directory file block buffer */ +} DBM; + + +/* ************************* */ + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. keep it that way. + * + * hashing routine + */ + +/* + * polynomial conversion ignoring overflows + * [this seems to work remarkably well, in fact better + * then the ndbm hash function. Replace at your own risk] + * use: 65599 nice. + * 65587 even better. + */ +static long sdbm_hash (char *str, int len) +{ + unsigned long n = 0; + +#ifdef DUFF +#define HASHC n = *str++ + 65599 * n + if (len > 0) + { + int loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) + { + case 0: + do + { + HASHC; + case 7: + HASHC; + case 6: + HASHC; + case 5: + HASHC; + case 4: + HASHC; + case 3: + HASHC; + case 2: + HASHC; + case 1: + HASHC; + } + while (--loop); + } + + } +#else + while (len--) + n = *str++ + 65599 * n; +#endif + return n; +} + +/* + * check page sanity: + * number of entries should be something + * reasonable, and all offsets in the index should be in order. + * this could be made more rigorous. + */ +static int chkpage (char *pag) +{ + int n; + int off; + short *ino = (short *) pag; + + if ((n = ino[0]) < 0 || n > PBLKSIZ / sizeof (short)) + return 0; + + if (n > 0) + { + off = PBLKSIZ; + for (ino++; n > 0; ino += 2) + { + if (ino[0] > off || ino[1] > off || + ino[1] > ino[0]) + return 0; + off = ino[1]; + n -= 2; + } + } + return 1; +} + +/* + * search for the key in the page. + * return offset index in the range 0 < i < n. + * return 0 if not found. + */ +static int seepair (char *pag, int n, char *key, int siz) +{ + int i; + int off = PBLKSIZ; + short *ino = (short *) pag; + + for (i = 1; i < n; i += 2) + { + if (siz == off - ino[i] && + memcmp (key, pag + ino[i], siz) == 0) + return i; + off = ino[i + 1]; + } + return 0; +} + +#ifdef SEEDUPS +static int duppair (char *pag, datum key) +{ + short *ino = (short *) pag; + + return ino[0] > 0 && seepair (pag, ino[0], key.dptr, key.dsize) > 0; +} + +#endif + +/* ************************* */ + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. + * + * page-level routines + */ + +/* + * page format: + * +------------------------------+ + * ino | n | keyoff | datoff | keyoff | + * +------------+--------+--------+ + * | datoff | - - - ----> | + * +--------+---------------------+ + * | F R E E A R E A | + * +--------------+---------------+ + * | <---- - - - | data | + * +--------+-----+----+----------+ + * | key | data | key | + * +--------+----------+----------+ + * + * calculating the offsets for free area: if the number + * of entries (ino[0]) is zero, the offset to the END of + * the free area is the block size. Otherwise, it is the + * nth (ino[ino[0]]) entry's offset. + */ + +static int fitpair (char *pag, int need) +{ + int n; + int off; + int avail; + short *ino = (short *) pag; + + off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ; + avail = off - (n + 1) * sizeof (short); + need += 2 * sizeof (short); + + return need <= avail; +} + +static void putpair (char *pag, datum key, datum val) +{ + int n; + int off; + short *ino = (short *) pag; + + off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ; +/* + * enter the key first + */ + off -= key.dsize; + (void) memcpy (pag + off, key.dptr, key.dsize); + ino[n + 1] = off; +/* + * now the data + */ + off -= val.dsize; + (void) memcpy (pag + off, val.dptr, val.dsize); + ino[n + 2] = off; +/* + * adjust item count + */ + ino[0] += 2; +} + +static datum getpair (char *pag, datum key) +{ + int i; + int n; + datum val; + short *ino = (short *) pag; + + if ((n = ino[0]) == 0) + return nullitem; + + if ((i = seepair (pag, n, key.dptr, key.dsize)) == 0) + return nullitem; + + val.dptr = pag + ino[i + 1]; + val.dsize = ino[i] - ino[i + 1]; + return val; +} + +static datum getnkey (char *pag, int num) +{ + datum key; + int off; + short *ino = (short *) pag; + + num = num * 2 - 1; + if (ino[0] == 0 || num > ino[0]) + return nullitem; + + off = (num > 1) ? ino[num - 1] : PBLKSIZ; + + key.dptr = pag + ino[num]; + key.dsize = off - ino[num]; + + return key; +} + +static int delpair (char *pag, datum key) +{ + int n; + int i; + short *ino = (short *) pag; + + if ((n = ino[0]) == 0) + return 0; + + if ((i = seepair (pag, n, key.dptr, key.dsize)) == 0) + return 0; +/* + * found the key. if it is the last entry + * [i.e. i == n - 1] we just adjust the entry count. + * hard case: move all data down onto the deleted pair, + * shift offsets onto deleted offsets, and adjust them. + * [note: 0 < i < n] + */ + if (i < n - 1) + { + int m; + char *dst = pag + (i == 1 ? PBLKSIZ : ino[i - 1]); + char *src = pag + ino[i + 1]; + int zoo = dst - src; + +/* + * shift data/keys down + */ + m = ino[i + 1] - ino[n]; +#ifdef DUFF +#define MOVB *--dst = *--src + if (m > 0) + { + int loop = (m + 8 - 1) >> 3; + + switch (m & (8 - 1)) + { + case 0: + do + { + MOVB; + case 7: + MOVB; + case 6: + MOVB; + case 5: + MOVB; + case 4: + MOVB; + case 3: + MOVB; + case 2: + MOVB; + case 1: + MOVB; + } + while (--loop); + } + } +#else + dst -= m; + src -= m; + memmove (dst, src, m); +#endif +/* + * adjust offset index up + */ + while (i < n - 1) + { + ino[i] = ino[i + 2] + zoo; + i++; + } + } + ino[0] -= 2; + return 1; +} + +static void splpage (char *pag, char *new, long sbit) +{ + datum key; + datum val; + + int n; + int off = PBLKSIZ; + char cur[PBLKSIZ]; + short *ino = (short *) cur; + + (void) memcpy (cur, pag, PBLKSIZ); + (void) memset (pag, 0, PBLKSIZ); + (void) memset (new, 0, PBLKSIZ); + + n = ino[0]; + for (ino++; n > 0; ino += 2) + { + key.dptr = cur + ino[0]; + key.dsize = off - ino[0]; + val.dptr = cur + ino[1]; + val.dsize = ino[0] - ino[1]; +/* + * select the page pointer (by looking at sbit) and insert + */ + (void) putpair ((exhash (key) & sbit) ? new : pag, key, val); + + off = ino[1]; + n -= 2; + } +} + +static int getdbit (DBM * db, long dbit) +{ + long c; + long dirb; + + c = dbit / BYTESIZ; + dirb = c / DBLKSIZ; + + if (dirb != db->dirbno) + { + int got; + if (lseek (db->dirf, OFF_DIR (dirb), SEEK_SET) < 0 + || (got = read(db->dirf, db->dirbuf, DBLKSIZ)) < 0) + return 0; + if (got==0) + memset(db->dirbuf,0,DBLKSIZ); + db->dirbno = dirb; + } + + return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ); +} + +static int setdbit (DBM * db, long dbit) +{ + long c; + long dirb; + + c = dbit / BYTESIZ; + dirb = c / DBLKSIZ; + + if (dirb != db->dirbno) + { + int got; + if (lseek (db->dirf, OFF_DIR (dirb), SEEK_SET) < 0 + || (got = read(db->dirf, db->dirbuf, DBLKSIZ)) < 0) + return 0; + if (got==0) + memset(db->dirbuf,0,DBLKSIZ); + db->dirbno = dirb; + } + + db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ); + +#if 0 + if (dbit >= db->maxbno) + db->maxbno += DBLKSIZ * BYTESIZ; +#else + if (OFF_DIR((dirb+1))*BYTESIZ > db->maxbno) + db->maxbno=OFF_DIR((dirb+1))*BYTESIZ; +#endif + + if (lseek (db->dirf, OFF_DIR (dirb), SEEK_SET) < 0 + || write (db->dirf, db->dirbuf, DBLKSIZ) < 0) + return 0; + + return 1; +} + +/* + * getnext - get the next key in the page, and if done with + * the page, try the next page in sequence + */ +static datum getnext (DBM * db) +{ + datum key; + + for (;;) + { + db->keyptr++; + key = getnkey (db->pagbuf, db->keyptr); + if (key.dptr != NULL) + return key; +/* + * we either run out, or there is nothing on this page.. + * try the next one... If we lost our position on the + * file, we will have to seek. + */ + db->keyptr = 0; + if (db->pagbno != db->blkptr++) + if (lseek (db->pagf, OFF_PAG (db->blkptr), SEEK_SET) < 0) + break; + db->pagbno = db->blkptr; + if (read (db->pagf, db->pagbuf, PBLKSIZ) <= 0) + break; + if (!chkpage (db->pagbuf)) + break; + } + + return ioerr (db), nullitem; +} + +/* + * all important binary trie traversal + */ +static int getpage (DBM * db, long hash) +{ + int hbit; + long dbit; + long pagb; + + dbit = 0; + hbit = 0; + while (dbit < db->maxbno && getdbit (db, dbit)) + dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1); + + db->curbit = dbit; + db->hmask = masks[hbit]; + + pagb = hash & db->hmask; +/* + * see if the block we need is already in memory. + * note: this lookaside cache has about 10% hit rate. + */ + if (pagb != db->pagbno) + { +/* + * note: here, we assume a "hole" is read as 0s. + * if not, must zero pagbuf first. + */ + if (lseek (db->pagf, OFF_PAG (pagb), SEEK_SET) < 0 + || read (db->pagf, db->pagbuf, PBLKSIZ) < 0) + return 0; + if (!chkpage (db->pagbuf)) + return 0; + db->pagbno = pagb; + } + return 1; +} + +/* + * makroom - make room by splitting the overfull page + * this routine will attempt to make room for SPLTMAX times before + * giving up. + */ +static int makroom (DBM * db, long hash, int need) +{ + long newp; + char twin[PBLKSIZ]; + char *pag = db->pagbuf; + char *new = twin; + int smax = SPLTMAX; + + do + { +/* + * split the current page + */ + (void) splpage (pag, new, db->hmask + 1); +/* + * address of the new page + */ + newp = (hash & db->hmask) | (db->hmask + 1); + +/* + * write delay, read avoidence/cache shuffle: + * select the page for incoming pair: if key is to go to the new page, + * write out the previous one, and copy the new one over, thus making + * it the current page. If not, simply write the new page, and we are + * still looking at the page of interest. current page is not updated + * here, as sdbm_store will do so, after it inserts the incoming pair. + */ + if (hash & (db->hmask + 1)) + { + if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 + || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) + return 0; + db->pagbno = newp; + (void) memcpy (pag, new, PBLKSIZ); + } + else if (lseek (db->pagf, OFF_PAG (newp), SEEK_SET) < 0 + || write (db->pagf, new, PBLKSIZ) < 0) + return 0; + + if (!setdbit (db, db->curbit)) + return 0; +/* + * see if we have enough room now + */ + if (fitpair (pag, need)) + return 1; +/* + * try again... update curbit and hmask as getpage would have + * done. because of our update of the current page, we do not + * need to read in anything. BUT we have to write the current + * [deferred] page out, as the window of failure is too great. + */ + db->curbit = 2 * db->curbit + + ((hash & (db->hmask + 1)) ? 2 : 1); + db->hmask |= db->hmask + 1; + + if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 + || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) + return 0; + + } + while (--smax); +/* + * if we are here, this is real bad news. After SPLTMAX splits, + * we still cannot fit the key. say goodnight. + */ +#ifdef BADMESS + (void) write (2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44); +#endif + return 0; + +} + +static SDBM *sdbm_prep (char *dirname, char *pagname, int flags, int mode) +{ + SDBM *db; + struct stat dstat; + + if ((db = (SDBM *) mymalloc (sizeof (SDBM))) == NULL) + return errno = ENOMEM, (SDBM *) NULL; + + db->flags = 0; + db->blkptr = 0; + db->keyptr = 0; +/* + * adjust user flags so that WRONLY becomes RDWR, + * as required by this package. Also set our internal + * flag for RDONLY if needed. + */ + if (flags & O_WRONLY) + flags = (flags & ~O_WRONLY) | O_RDWR; + else if ((flags & 03) == O_RDONLY) + db->flags = DBM_RDONLY; +#if defined(OS2) || defined(MSDOS) || defined(WIN32) + flags |= O_BINARY; +#endif + +/* + * Make sure to ignore the O_EXCL option, as the file might exist due + * to the locking. + */ + flags &= ~O_EXCL; + +/* + * open the files in sequence, and stat the dirfile. + * If we fail anywhere, undo everything, return NULL. + */ + + if ((db->pagf = open (pagname, flags, mode)) > -1) + { + if ((db->dirf = open (dirname, flags, mode)) > -1) + { +/* + * need the dirfile size to establish max bit number. + */ + if (fstat (db->dirf, &dstat) == 0) + { + /* + * success + */ + return db; + } + msg_info ("closing dirf"); + (void) close (db->dirf); + } + msg_info ("closing pagf"); + (void) close (db->pagf); + } + myfree ((char *) db); + return (SDBM *) NULL; +} + +static DBM *sdbm_internal_open (SDBM * sdbm) +{ + DBM *db; + struct stat dstat; + + if ((db = (DBM *) mymalloc (sizeof (DBM))) == NULL) + return errno = ENOMEM, (DBM *) NULL; + + db->flags = sdbm->flags; + db->hmask = 0; + db->blkptr = sdbm->blkptr; + db->keyptr = sdbm->keyptr; + db->pagf = sdbm->pagf; + db->dirf = sdbm->dirf; + db->pagbuf = sdbm->pagbuf; + db->dirbuf = sdbm->dirbuf; + +/* + * need the dirfile size to establish max bit number. + */ + if (fstat (db->dirf, &dstat) == 0) + { +/* + * zero size: either a fresh database, or one with a single, + * unsplit data page: dirpage is all zeros. + */ + db->dirbno = (!dstat.st_size) ? 0 : -1; + db->pagbno = -1; + db->maxbno = dstat.st_size * BYTESIZ; + + (void) memset (db->pagbuf, 0, PBLKSIZ); + (void) memset (db->dirbuf, 0, DBLKSIZ); + return db; + } + myfree ((char *) db); + return (DBM *) NULL; +} + +static void sdbm_internal_close (DBM * db) +{ + if (db == NULL) + errno = EINVAL; + else + { + myfree ((char *) db); + } +} + +datum sdbm_fetch (SDBM * sdb, datum key) +{ + datum retval; + DBM *db; + + if (sdb == NULL || bad (key)) + return errno = EINVAL, nullitem; + + if (!(db = sdbm_internal_open (sdb))) + return errno = EINVAL, nullitem; + + if (getpage (db, exhash (key))) + { + retval = getpair (db->pagbuf, key); + sdbm_internal_close (db); + return retval; + } + + sdbm_internal_close (db); + + return ioerr (sdb), nullitem; +} + +int sdbm_delete (SDBM * sdb, datum key) +{ + int retval; + DBM *db; + + if (sdb == NULL || bad (key)) + return errno = EINVAL, -1; + if (sdbm_rdonly (sdb)) + return errno = EPERM, -1; + + if (!(db = sdbm_internal_open (sdb))) + return errno = EINVAL, -1; + + if (getpage (db, exhash (key))) + { + if (!delpair (db->pagbuf, key)) + retval = -1; +/* + * update the page file + */ + else if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 + || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) + retval = ioerr (sdb), -1; + else + retval = 0; + } + else + retval = ioerr (sdb), -1; + + sdbm_internal_close (db); + + return retval; +} + +int sdbm_store (SDBM * sdb, datum key, datum val, int flags) +{ + int need; + int retval; + long hash; + DBM *db; + + if (sdb == NULL || bad (key)) + return errno = EINVAL, -1; + if (sdbm_rdonly (sdb)) + return errno = EPERM, -1; + + need = key.dsize + val.dsize; +/* + * is the pair too big (or too small) for this database ?? + */ + if (need < 0 || need > PAIRMAX) + return errno = EINVAL, -1; + + if (!(db = sdbm_internal_open (sdb))) + return errno = EINVAL, -1; + + if (getpage (db, (hash = exhash (key)))) + { +/* + * if we need to replace, delete the key/data pair + * first. If it is not there, ignore. + */ + if (flags == DBM_REPLACE) + (void) delpair (db->pagbuf, key); +#ifdef SEEDUPS + else if (duppair (db->pagbuf, key)) + { + sdbm_internal_close (db); + return 1; + } +#endif +/* + * if we do not have enough room, we have to split. + */ + if (!fitpair (db->pagbuf, need)) + if (!makroom (db, hash, need)) + { + sdbm_internal_close (db); + return ioerr (db), -1; + } +/* + * we have enough room or split is successful. insert the key, + * and update the page file. + */ + (void) putpair (db->pagbuf, key, val); + + if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 + || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) + { + sdbm_internal_close (db); + return ioerr (db), -1; + } + /* + * success + */ + sdbm_internal_close (db); + return 0; + } + + sdbm_internal_close (db); + return ioerr (sdb), -1; +} + +/* + * the following two routines will break if + * deletions aren't taken into account. (ndbm bug) + */ +datum sdbm_firstkey (SDBM * sdb) +{ + datum retval; + DBM *db; + + if (sdb == NULL) + return errno = EINVAL, nullitem; + + if (!(db = sdbm_internal_open (sdb))) + return errno = EINVAL, nullitem; + +/* + * start at page 0 + */ + if (lseek (db->pagf, OFF_PAG (0), SEEK_SET) < 0 + || read (db->pagf, db->pagbuf, PBLKSIZ) < 0) + { + sdbm_internal_close (db); + return ioerr (sdb), nullitem; + } + db->pagbno = 0; + db->blkptr = 0; + db->keyptr = 0; + + retval = getnext (db); + sdb->blkptr = db->blkptr; + sdb->keyptr = db->keyptr; + sdbm_internal_close (db); + return retval; +} + +datum sdbm_nextkey (SDBM * sdb) +{ + datum retval; + DBM *db; + + if (sdb == NULL) + return errno = EINVAL, nullitem; + + if (!(db = sdbm_internal_open (sdb))) + return errno = EINVAL, nullitem; + + retval = getnext (db); + sdb->blkptr = db->blkptr; + sdb->keyptr = db->keyptr; + sdbm_internal_close (db); + return retval; +} + +void sdbm_close (SDBM * db) +{ + if (db == NULL) + errno = EINVAL; + else + { + (void) close (db->dirf); + (void) close (db->pagf); + myfree ((char *) db); + } +} + +SDBM *sdbm_open (char *file, int flags, int mode) +{ + SDBM *db; + char *dirname; + char *pagname; + int n; + + if (file == NULL || !*file) + return errno = EINVAL, (SDBM *) NULL; +/* + * need space for two seperate filenames + */ + n = strlen (file) * 2 + strlen (DIRFEXT) + strlen (PAGFEXT) + 2; + + if ((dirname = (char *) mymalloc ((unsigned) n)) == NULL) + return errno = ENOMEM, (SDBM *) NULL; +/* + * build the file names + */ + dirname = strcat (strcpy (dirname, file), DIRFEXT); + pagname = strcpy (dirname + strlen (dirname) + 1, file); + pagname = strcat (pagname, PAGFEXT); + + db = sdbm_prep (dirname, pagname, flags, mode); + myfree ((char *) dirname); + return db; +} + Index: src/util/sdbm.h =================================================================== --- /dev/null +++ src/util/sdbm.h @@ -0,0 +1,97 @@ +/*++ +/* NAME +/* sdbm 3h +/* SUMMARY +/* SDBM Simple DBM: ndbm work-alike hashed database library +/* SYNOPSIS +/* include "sdbm.h" +/* DESCRIPTION +/* .nf +/*--*/ + +#ifndef UTIL_SDBM_H +#define UTIL_SDBM_H + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Ake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. + */ + +#define DUFF /* go ahead and use the loop-unrolled version */ + +#include + +#define DBLKSIZ 16384 /* SSL cert chains require more */ +#define PBLKSIZ 8192 /* SSL cert chains require more */ +#define PAIRMAX 8008 /* arbitrary on PBLKSIZ-N */ +#define SPLTMAX 10 /* maximum allowed splits */ + /* for a single insertion */ +#define DIRFEXT ".dir" +#define PAGFEXT ".pag" + +typedef struct { + int dirf; /* directory file descriptor */ + int pagf; /* page file descriptor */ + int flags; /* status/error flags, see below */ + long blkptr; /* current block for nextkey */ + int keyptr; /* current key for nextkey */ + char pagbuf[PBLKSIZ]; /* page file block buffer */ + char dirbuf[DBLKSIZ]; /* directory file block buffer */ +} SDBM; + +#define DBM_RDONLY 0x1 /* data base open read-only */ +#define DBM_IOERR 0x2 /* data base I/O error */ + +/* + * utility macros + */ +#define sdbm_rdonly(db) ((db)->flags & DBM_RDONLY) +#define sdbm_error(db) ((db)->flags & DBM_IOERR) + +#define sdbm_clearerr(db) ((db)->flags &= ~DBM_IOERR) /* ouch */ + +#define sdbm_dirfno(db) ((db)->dirf) +#define sdbm_pagfno(db) ((db)->pagf) + +typedef struct { + char *dptr; + int dsize; +} datum; + +extern datum nullitem; + +/* + * flags to sdbm_store + */ +#define DBM_INSERT 0 +#define DBM_REPLACE 1 + +/* + * ndbm interface + */ +extern SDBM *sdbm_open(char *, int, int); +extern void sdbm_close(SDBM *); +extern datum sdbm_fetch(SDBM *, datum); +extern int sdbm_delete(SDBM *, datum); +extern int sdbm_store(SDBM *, datum, datum, int); +extern datum sdbm_firstkey(SDBM *); +extern datum sdbm_nextkey(SDBM *); + +/* + * sdbm - ndbm work-alike hashed database library + * tuning and portability constructs [not nearly enough] + * author: oz@nexus.yorku.ca + */ + +#define BYTESIZ 8 + +/* + * important tuning parms (hah) + */ + +#define SEEDUPS /* always detect duplicates */ +#define BADMESS /* generate a message for worst case: + cannot make room after SPLTMAX splits */ +#endif /* UTIL_SDBM_H */ Index: src/util/sys_defs.h =================================================================== --- src/util/sys_defs.h.orig +++ src/util/sys_defs.h @@ -755,6 +755,7 @@ extern int initgroups(const char *, int) #define INTERNAL_LOCK MYFLOCK_STYLE_FLOCK #define DEF_MAILBOX_LOCK "fcntl, dotlock" /* RedHat >= 4.x */ #define HAS_FSYNC +#define HAS_SDBM #define HAS_DB #define DEF_DB_TYPE "hash" #define ALIAS_DB_MAP "hash:/etc/aliases" @@ -768,11 +769,25 @@ extern int initgroups(const char *, int) #define STATFS_IN_SYS_VFS_H #define PREPEND_PLUS_TO_OPTSTRING #define HAS_POSIX_REGEXP +#define HAS_DLOPEN #define NATIVE_SENDMAIL_PATH "/usr/sbin/sendmail" #define NATIVE_MAILQ_PATH "/usr/bin/mailq" #define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases" #define NATIVE_COMMAND_DIR "/usr/sbin" +#ifdef DEBIAN +#define NATIVE_DAEMON_DIR "/usr/lib/postfix" +#ifndef DEF_MANPAGE_DIR +#define DEF_MANPAGE_DIR "/usr/share/man" +#endif +#ifndef DEF_SAMPLE_DIR +#define DEF_SAMPLE_DIR "/usr/share/doc/postfix/examples" +#endif +#ifndef DEF_README_DIR +#define DEF_README_DIR "/usr/share/doc/postfix" +#endif +#else #define NATIVE_DAEMON_DIR "/usr/libexec/postfix" +#endif #ifdef __GLIBC_PREREQ # define HAVE_GLIBC_API_VERSION_SUPPORT(maj, min) __GLIBC_PREREQ(maj, min) #else @@ -954,6 +969,7 @@ extern int h_errno; /* impor #define USE_STATFS #define STATFS_IN_SYS_VFS_H #define HAS_POSIX_REGEXP +#define HAS_DLOPEN #define NATIVE_SENDMAIL_PATH "/usr/sbin/sendmail" #define NATIVE_MAILQ_PATH "/usr/bin/mailq" #define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases" @@ -993,6 +1009,7 @@ extern int h_errno; /* impor #define USE_STATFS #define STATFS_IN_SYS_VFS_H #define HAS_POSIX_REGEXP +#define HAS_SHL_LOAD #define NATIVE_SENDMAIL_PATH "/usr/sbin/sendmail" #define NATIVE_MAILQ_PATH "/usr/bin/mailq" #define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases" @@ -1034,6 +1051,7 @@ extern int h_errno; #define USE_STATFS #define STATFS_IN_SYS_VFS_H #define HAS_POSIX_REGEXP +#define HAS_SHL_LOAD #define NATIVE_SENDMAIL_PATH "/usr/bin/sendmail" #define NATIVE_MAILQ_PATH "/usr/bin/mailq" #define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases"