postfix 20010228-pl04
This commit is contained in:
parent
ed97fbd98d
commit
48a8a26166
|
@ -5063,3 +5063,36 @@ Apologies for any names omitted.
|
|||
sending QUIT after process idle timeout while the LMTP
|
||||
server had disconnected. Files: smtp/smtp_proto.c,
|
||||
lmtp/lmtp_proto.c.
|
||||
|
||||
20010727
|
||||
|
||||
Bugfix: updated LDAP client module from LaMont Jones, HP.
|
||||
This also introduces new LDAP query filter patterns: %u
|
||||
(address localpart) and %d (domain part). Files:
|
||||
conf/sample-ldap.cf, util/dict_ldap.c.
|
||||
|
||||
20010729
|
||||
|
||||
Bugfix: recursive smtpd_whatever_restrictions clobbered
|
||||
intermediate results when switching between sender and
|
||||
recipient address restrictions. Problem found by Victor
|
||||
Duchovni, morganstanley.com. In order to fix, introduced
|
||||
address resolver result caching, which should also help to
|
||||
speed up sender/recipient address restriction processing.
|
||||
|
||||
Bugfix: the not yet announced DUNNO access table lookup
|
||||
result did not prevent lookups with substrings of the same
|
||||
lookup key. Found by Victor Duchovni, morganstanley.com.
|
||||
|
||||
20010730
|
||||
|
||||
Robustness: trim trailing whitespace from regexp and pcre
|
||||
right-hand sides, for consistency with DB/DBM tables.
|
||||
Files: util/dict_pcre.c, util/dict_regexp.c.
|
||||
|
||||
20010731
|
||||
|
||||
Robustness: eliminate duplicate IP addresses after expansion
|
||||
of hostnames in $inet_interfaces, so that Postfix does not
|
||||
suddenly refuse to start up after someone changes the DNS.
|
||||
Files: util/inet_addr_list.c global/own_inet_addr.c.
|
||||
|
|
|
@ -12,9 +12,10 @@ the mysqlclient library (and libm) to AUXLIBS, for example:
|
|||
|
||||
make -f Makefile.init makefiles \
|
||||
'CCARGS=-DHAS_MYSQL -I/usr/local/mysql/include' \
|
||||
'AUXLIBS=-L/usr/local/mysql/lib -lmysqlclient -lm'
|
||||
'AUXLIBS=-L/usr/local/mysql/lib -lmysqlclient -lz -lm'
|
||||
|
||||
then, just run 'make'.
|
||||
then, just run 'make'. This requires libz, the compression library.
|
||||
Older mysql implementations build without libz.
|
||||
|
||||
Postfix installations which may benefit from using mysql map types
|
||||
include sites that have a need for instantaneous updates of
|
||||
|
|
|
@ -23,6 +23,13 @@
|
|||
#ldap_server_port = 389
|
||||
|
||||
# The ldap_query_filter parameter specifies the filter used for queries.
|
||||
# The replacement for "%s" is the address input into the map; e.g.
|
||||
# for alias maps, the "user" part (the RFC 2822 local-part) of
|
||||
# "user@domain.com" for To: addresses destined for local delivery
|
||||
# (those matching $mydestination or a virtual domain), and all of
|
||||
# "user@domain.com" (the RFC 2822 addr-spec) for other addresses.
|
||||
# "%u" provides just the user portion of the input, and "%d" provides
|
||||
# just the hostname.
|
||||
#
|
||||
#ldap_query_filter = (mailacceptinggeneralid=%s)
|
||||
|
||||
|
@ -31,6 +38,13 @@
|
|||
#
|
||||
#ldap_result_attribute = maildrop
|
||||
|
||||
# The ldap_special_result_attribute lists the attribute(s) of an
|
||||
# entry which contain links, either ldap url's or distinguished names.
|
||||
# The entries referenced by these links are (recursively) treated as if
|
||||
# they were contained in the referencing entity.
|
||||
#
|
||||
# ldap_special_result_attribute =
|
||||
|
||||
# The ldap_scope parameter specifies the LDAP search scope: sub, base, or one.
|
||||
#
|
||||
#ldap_scope = sub
|
||||
|
|
|
@ -1027,7 +1027,7 @@ Berkeley DB library version.
|
|||
|
||||
<hr>
|
||||
|
||||
<a name="nosuid"><h1>sendmail has set-uid root file permissions, or is run from a
|
||||
<a name="nosuid"><h3>sendmail has set-uid root file permissions, or is run from a
|
||||
set-uid root process</h3></a>
|
||||
|
||||
Traditionally, the UNIX <b>sendmail</b> command is installed with
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* Version of this program.
|
||||
*/
|
||||
#define VAR_MAIL_VERSION "mail_version"
|
||||
#define DEF_MAIL_VERSION "Postfix-20010228-pl03"
|
||||
#define DEF_MAIL_VERSION "Postfix-20010228-pl04"
|
||||
extern char *var_mail_version;
|
||||
|
||||
/* LICENSE
|
||||
|
|
|
@ -67,7 +67,7 @@ depend: $(MAKES)
|
|||
done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
|
||||
@$(EXPORT) make -f Makefile.in Makefile 1>&2
|
||||
|
||||
tests: smtpd_check_test smtpd_check_test2 smtpd_token_test
|
||||
tests: smtpd_check_test smtpd_check_test2 smtpd_acl_test smtpd_token_test
|
||||
|
||||
smtpd_check_test: smtpd_check smtpd_check.in smtpd_check.ref
|
||||
../postmap/postmap smtpd_check_access
|
||||
|
@ -81,6 +81,12 @@ smtpd_check_test2: smtpd_check smtpd_check.in2 smtpd_check.ref2
|
|||
diff smtpd_check.ref2 smtpd_check.tmp
|
||||
rm -f smtpd_check.tmp smtpd_check_access.*
|
||||
|
||||
smtpd_acl_test: smtpd_check smtpd_acl.in smtpd_acl.ref
|
||||
../postmap/postmap smtpd_check_access
|
||||
./smtpd_check <smtpd_acl.in >smtpd_check.tmp 2>&1
|
||||
diff smtpd_acl.ref smtpd_check.tmp
|
||||
rm -f smtpd_check.tmp smtpd_check_access.*
|
||||
|
||||
smtpd_token_test: smtpd_token smtpd_token.in smtpd_token.ref
|
||||
./smtpd_token <smtpd_token.in >smtpd_token.tmp 2>&1
|
||||
diff smtpd_token.ref smtpd_token.tmp
|
||||
|
@ -164,6 +170,7 @@ smtpd_check.o: ../../include/mymalloc.h
|
|||
smtpd_check.o: ../../include/dict.h
|
||||
smtpd_check.o: ../../include/vstream.h
|
||||
smtpd_check.o: ../../include/htable.h
|
||||
smtpd_check.o: ../../include/ctable.h
|
||||
smtpd_check.o: ../../include/dns.h
|
||||
smtpd_check.o: ../../include/namadr_list.h
|
||||
smtpd_check.o: ../../include/domain_list.h
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
#
|
||||
# Initialize
|
||||
#
|
||||
smtpd_delay_reject 0
|
||||
mynetworks 127.0.0.0/8,168.100.189.0/28
|
||||
relay_domains porcupine.org
|
||||
#
|
||||
# Test check_domain_access()
|
||||
#
|
||||
helo_restrictions hash:./smtpd_check_access
|
||||
# Expect: REJECT
|
||||
helo foo.dunno.com
|
||||
# Expect: OK
|
||||
helo bar.dunno.com
|
||||
# Expect: OK
|
||||
helo foo.duuno.com
|
||||
#
|
||||
# Test check_namadr_access(), domain part
|
||||
#
|
||||
client_restrictions hash:./smtpd_check_access
|
||||
# Expect: REJECT
|
||||
client foo.dunno.com 131.155.210.17
|
||||
# Expect: OK
|
||||
client bar.dunno.com 131.155.210.17
|
||||
# Expect: OK
|
||||
client bar.dunno.com 131.155.210.19
|
||||
#
|
||||
# Test check_namadr_access(), address part
|
||||
#
|
||||
# Expect: OK
|
||||
client bar.duno.com 131.155.210.17
|
||||
# Expect: REJECT
|
||||
client bar.duno.com 131.155.210.19
|
||||
# Expect: REJECT
|
||||
client bar.duno.com 44.33.22.11
|
||||
# Expect: OK
|
||||
client bar.duno.com 44.33.22.55
|
||||
# Expect: REJECT
|
||||
client bar.duno.com 44.33.44.33
|
||||
#
|
||||
# Test check_mail_access()
|
||||
#
|
||||
sender_restrictions hash:./smtpd_check_access
|
||||
# Expect: REJECT
|
||||
mail reject@dunno.domain
|
||||
# Expect: OK
|
||||
mail ok@dunno.domain
|
||||
# Expect: OK
|
||||
mail anyone@dunno.domain
|
||||
# Expect: OK
|
||||
mail bad-sender@dunno.domain
|
||||
#
|
||||
# Again, with a domain that rejects by default
|
||||
#
|
||||
# Expect: REJECT
|
||||
mail reject@reject.domain
|
||||
# Expect: OK
|
||||
mail ok@reject.domain
|
||||
# Expect: REJECT
|
||||
mail anyone@reject.domain
|
||||
# Expect: REJECT
|
||||
mail good-sender@reject.domain
|
||||
#
|
||||
# Again, with a domain that accepts by default
|
||||
#
|
||||
# Expect: REJECT
|
||||
mail reject@ok.domain
|
||||
# Expect: OK
|
||||
mail ok@ok.domain
|
||||
# Expect: OK
|
||||
mail anyone@ok.domain
|
||||
# Expect: OK
|
||||
mail bad-sender@ok.domain
|
||||
#
|
||||
# Test check_mail_access()
|
||||
#
|
||||
recipient_restrictions hash:./smtpd_check_access
|
||||
# Expect: REJECT
|
||||
rcpt reject@dunno.domain
|
||||
# Expect: OK
|
||||
rcpt ok@dunno.domain
|
||||
# Expect: OK
|
||||
rcpt anyone@dunno.domain
|
||||
# Expect: OK
|
||||
rcpt bad-sender@dunno.domain
|
||||
#
|
||||
# Again, with a domain that rejects by default
|
||||
#
|
||||
# Expect: REJECT
|
||||
rcpt reject@reject.domain
|
||||
# Expect: OK
|
||||
rcpt ok@reject.domain
|
||||
# Expect: REJECT
|
||||
rcpt anyone@reject.domain
|
||||
# Expect: REJECT
|
||||
rcpt good-sender@reject.domain
|
||||
#
|
||||
# Again, with a domain that accepts by default
|
||||
#
|
||||
# Expect: REJECT
|
||||
rcpt reject@ok.domain
|
||||
# Expect: OK
|
||||
rcpt ok@ok.domain
|
||||
# Expect: OK
|
||||
rcpt anyone@ok.domain
|
||||
# Expect: OK
|
||||
rcpt bad-sender@ok.domain
|
|
@ -0,0 +1,164 @@
|
|||
>>> #
|
||||
>>> # Initialize
|
||||
>>> #
|
||||
>>> smtpd_delay_reject 0
|
||||
OK
|
||||
>>> mynetworks 127.0.0.0/8,168.100.189.0/28
|
||||
OK
|
||||
>>> relay_domains porcupine.org
|
||||
OK
|
||||
>>> #
|
||||
>>> # Test check_domain_access()
|
||||
>>> #
|
||||
>>> helo_restrictions hash:./smtpd_check_access
|
||||
OK
|
||||
>>> # Expect: REJECT
|
||||
>>> helo foo.dunno.com
|
||||
./smtpd_check: reject: HELO from localhost[127.0.0.1]: 554 <foo.dunno.com>: Helo command rejected: Access denied
|
||||
554 <foo.dunno.com>: Helo command rejected: Access denied
|
||||
>>> # Expect: OK
|
||||
>>> helo bar.dunno.com
|
||||
OK
|
||||
>>> # Expect: OK
|
||||
>>> helo foo.duuno.com
|
||||
OK
|
||||
>>> #
|
||||
>>> # Test check_namadr_access(), domain part
|
||||
>>> #
|
||||
>>> client_restrictions hash:./smtpd_check_access
|
||||
OK
|
||||
>>> # Expect: REJECT
|
||||
>>> client foo.dunno.com 131.155.210.17
|
||||
./smtpd_check: reject: CONNECT from foo.dunno.com[131.155.210.17]: 554 <foo.dunno.com[131.155.210.17]>: Client host rejected: Access denied
|
||||
554 <foo.dunno.com[131.155.210.17]>: Client host rejected: Access denied
|
||||
>>> # Expect: OK
|
||||
>>> client bar.dunno.com 131.155.210.17
|
||||
OK
|
||||
>>> # Expect: OK
|
||||
>>> client bar.dunno.com 131.155.210.19
|
||||
OK
|
||||
>>> #
|
||||
>>> # Test check_namadr_access(), address part
|
||||
>>> #
|
||||
>>> # Expect: OK
|
||||
>>> client bar.duno.com 131.155.210.17
|
||||
OK
|
||||
>>> # Expect: REJECT
|
||||
>>> client bar.duno.com 131.155.210.19
|
||||
./smtpd_check: reject: CONNECT from bar.duno.com[131.155.210.19]: 554 <bar.duno.com[131.155.210.19]>: Client host rejected: Access denied
|
||||
554 <bar.duno.com[131.155.210.19]>: Client host rejected: Access denied
|
||||
>>> # Expect: REJECT
|
||||
>>> client bar.duno.com 44.33.22.11
|
||||
./smtpd_check: reject: CONNECT from bar.duno.com[44.33.22.11]: 554 <bar.duno.com[44.33.22.11]>: Client host rejected: Access denied
|
||||
554 <bar.duno.com[44.33.22.11]>: Client host rejected: Access denied
|
||||
>>> # Expect: OK
|
||||
>>> client bar.duno.com 44.33.22.55
|
||||
OK
|
||||
>>> # Expect: REJECT
|
||||
>>> client bar.duno.com 44.33.44.33
|
||||
./smtpd_check: reject: CONNECT from bar.duno.com[44.33.44.33]: 554 <bar.duno.com[44.33.44.33]>: Client host rejected: Access denied
|
||||
554 <bar.duno.com[44.33.44.33]>: Client host rejected: Access denied
|
||||
>>> #
|
||||
>>> # Test check_mail_access()
|
||||
>>> #
|
||||
>>> sender_restrictions hash:./smtpd_check_access
|
||||
OK
|
||||
>>> # Expect: REJECT
|
||||
>>> mail reject@dunno.domain
|
||||
./smtpd_check: reject: MAIL from bar.duno.com[44.33.44.33]: 554 <reject@dunno.domain>: Sender address rejected: Access denied; from=<reject@dunno.domain>
|
||||
554 <reject@dunno.domain>: Sender address rejected: Access denied
|
||||
>>> # Expect: OK
|
||||
>>> mail ok@dunno.domain
|
||||
OK
|
||||
>>> # Expect: OK
|
||||
>>> mail anyone@dunno.domain
|
||||
OK
|
||||
>>> # Expect: OK
|
||||
>>> mail bad-sender@dunno.domain
|
||||
OK
|
||||
>>> #
|
||||
>>> # Again, with a domain that rejects by default
|
||||
>>> #
|
||||
>>> # Expect: REJECT
|
||||
>>> mail reject@reject.domain
|
||||
./smtpd_check: reject: MAIL from bar.duno.com[44.33.44.33]: 554 <reject@reject.domain>: Sender address rejected: Access denied; from=<reject@reject.domain>
|
||||
554 <reject@reject.domain>: Sender address rejected: Access denied
|
||||
>>> # Expect: OK
|
||||
>>> mail ok@reject.domain
|
||||
OK
|
||||
>>> # Expect: REJECT
|
||||
>>> mail anyone@reject.domain
|
||||
./smtpd_check: reject: MAIL from bar.duno.com[44.33.44.33]: 554 <anyone@reject.domain>: Sender address rejected: Access denied; from=<anyone@reject.domain>
|
||||
554 <anyone@reject.domain>: Sender address rejected: Access denied
|
||||
>>> # Expect: REJECT
|
||||
>>> mail good-sender@reject.domain
|
||||
./smtpd_check: reject: MAIL from bar.duno.com[44.33.44.33]: 554 <good-sender@reject.domain>: Sender address rejected: Access denied; from=<good-sender@reject.domain>
|
||||
554 <good-sender@reject.domain>: Sender address rejected: Access denied
|
||||
>>> #
|
||||
>>> # Again, with a domain that accepts by default
|
||||
>>> #
|
||||
>>> # Expect: REJECT
|
||||
>>> mail reject@ok.domain
|
||||
./smtpd_check: reject: MAIL from bar.duno.com[44.33.44.33]: 554 <reject@ok.domain>: Sender address rejected: Access denied; from=<reject@ok.domain>
|
||||
554 <reject@ok.domain>: Sender address rejected: Access denied
|
||||
>>> # Expect: OK
|
||||
>>> mail ok@ok.domain
|
||||
OK
|
||||
>>> # Expect: OK
|
||||
>>> mail anyone@ok.domain
|
||||
OK
|
||||
>>> # Expect: OK
|
||||
>>> mail bad-sender@ok.domain
|
||||
OK
|
||||
>>> #
|
||||
>>> # Test check_mail_access()
|
||||
>>> #
|
||||
>>> recipient_restrictions hash:./smtpd_check_access
|
||||
OK
|
||||
>>> # Expect: REJECT
|
||||
>>> rcpt reject@dunno.domain
|
||||
./smtpd_check: reject: RCPT from bar.duno.com[44.33.44.33]: 554 <reject@dunno.domain>: Recipient address rejected: Access denied; from=<bad-sender@ok.domain> to=<reject@dunno.domain>
|
||||
554 <reject@dunno.domain>: Recipient address rejected: Access denied
|
||||
>>> # Expect: OK
|
||||
>>> rcpt ok@dunno.domain
|
||||
OK
|
||||
>>> # Expect: OK
|
||||
>>> rcpt anyone@dunno.domain
|
||||
OK
|
||||
>>> # Expect: OK
|
||||
>>> rcpt bad-sender@dunno.domain
|
||||
OK
|
||||
>>> #
|
||||
>>> # Again, with a domain that rejects by default
|
||||
>>> #
|
||||
>>> # Expect: REJECT
|
||||
>>> rcpt reject@reject.domain
|
||||
./smtpd_check: reject: RCPT from bar.duno.com[44.33.44.33]: 554 <reject@reject.domain>: Recipient address rejected: Access denied; from=<bad-sender@ok.domain> to=<reject@reject.domain>
|
||||
554 <reject@reject.domain>: Recipient address rejected: Access denied
|
||||
>>> # Expect: OK
|
||||
>>> rcpt ok@reject.domain
|
||||
OK
|
||||
>>> # Expect: REJECT
|
||||
>>> rcpt anyone@reject.domain
|
||||
./smtpd_check: reject: RCPT from bar.duno.com[44.33.44.33]: 554 <anyone@reject.domain>: Recipient address rejected: Access denied; from=<bad-sender@ok.domain> to=<anyone@reject.domain>
|
||||
554 <anyone@reject.domain>: Recipient address rejected: Access denied
|
||||
>>> # Expect: REJECT
|
||||
>>> rcpt good-sender@reject.domain
|
||||
./smtpd_check: reject: RCPT from bar.duno.com[44.33.44.33]: 554 <good-sender@reject.domain>: Recipient address rejected: Access denied; from=<bad-sender@ok.domain> to=<good-sender@reject.domain>
|
||||
554 <good-sender@reject.domain>: Recipient address rejected: Access denied
|
||||
>>> #
|
||||
>>> # Again, with a domain that accepts by default
|
||||
>>> #
|
||||
>>> # Expect: REJECT
|
||||
>>> rcpt reject@ok.domain
|
||||
./smtpd_check: reject: RCPT from bar.duno.com[44.33.44.33]: 554 <reject@ok.domain>: Recipient address rejected: Access denied; from=<bad-sender@ok.domain> to=<reject@ok.domain>
|
||||
554 <reject@ok.domain>: Recipient address rejected: Access denied
|
||||
>>> # Expect: OK
|
||||
>>> rcpt ok@ok.domain
|
||||
OK
|
||||
>>> # Expect: OK
|
||||
>>> rcpt anyone@ok.domain
|
||||
OK
|
||||
>>> # Expect: OK
|
||||
>>> rcpt bad-sender@ok.domain
|
||||
OK
|
|
@ -2,9 +2,30 @@ bad.domain 554 match bad.domain
|
|||
friend.bad.domain OK
|
||||
bad-sender@ 554 match bad-sender@
|
||||
bad-sender@good.domain OK
|
||||
good-sender@ OK
|
||||
131.155.210 554 match 131.155.210
|
||||
131.155.210.17 OK
|
||||
131.155.210.19 REJECT
|
||||
reject@this.address 554 match reject@this.address
|
||||
open_user@some.site open
|
||||
strict_user@some.site strict
|
||||
auth_client 123456
|
||||
|
||||
dunno.com dunno
|
||||
foo.dunno.com reject
|
||||
|
||||
44.33.22 dunno
|
||||
44.33.22.11 REJECT
|
||||
44.33 REJECT
|
||||
|
||||
reject@dunno.domain REJECT
|
||||
ok@dunno.domain OK
|
||||
dunno.domain DUNNO
|
||||
|
||||
reject@reject.domain REJECT
|
||||
ok@reject.domain OK
|
||||
reject.domain REJECT
|
||||
|
||||
reject@ok.domain REJECT
|
||||
ok@ok.domain OK
|
||||
ok.domain OK
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
/*++
|
||||
/* NAME
|
||||
/* ctable 3
|
||||
/* SUMMARY
|
||||
/* cache manager
|
||||
/* SYNOPSIS
|
||||
/* #include <ctable.h>
|
||||
/*
|
||||
/* CTABLE *ctable_create(limit, create, delete, context)
|
||||
/* int limit;
|
||||
/* void *(*create)(const char *key, void *context);
|
||||
/* void (*delete)(void *value, void *context);
|
||||
/* void *context;
|
||||
/*
|
||||
/* const void *ctable_locate(cache, key)
|
||||
/* CTABLE *cache;
|
||||
/* const char *key;
|
||||
/*
|
||||
/* void ctable_free(cache)
|
||||
/* CTABLE *cache;
|
||||
/*
|
||||
/* void ctable_walk(cache, action)
|
||||
/* CTABLE *cache;
|
||||
/* void (*action)(const char *key, const void *value);
|
||||
/* DESCRIPTION
|
||||
/* This module maintains multiple caches. Cache items are purged
|
||||
/* automatically when the number of items exceeds a configurable
|
||||
/* limit. Caches never shrink. Each cache entry consists of a
|
||||
/* string-valued lookup key and a generic data pointer value.
|
||||
/*
|
||||
/* ctable_create() creates a cache with the specified size limit, and
|
||||
/* returns a pointer to the result. The create and delete arguments
|
||||
/* specify pointers to call-back functions that create a value, given
|
||||
/* a key, and delete a given value, respectively. The context argument
|
||||
/* is passed on to the call-back routines.
|
||||
/*
|
||||
/* ctable_locate() looks up or generates the value that corresponds to
|
||||
/* the specified key, and returns that value.
|
||||
/*
|
||||
/* ctable_free() destroys the specified cache, including its contents.
|
||||
/*
|
||||
/* ctable_walk() iterates over all elements in the cache, and invokes
|
||||
/* the action function for each cache element with the corresponding
|
||||
/* key and value as arguments. This function is useful mainly for
|
||||
/* cache performance debugging.
|
||||
/* DIAGNOSTICS
|
||||
/* Fatal errors: out of memory. Panic: interface violation.
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
/* The Secure Mailer license must be distributed with this software.
|
||||
/* AUTHOR(S)
|
||||
/* Wietse Venema
|
||||
/* IBM T.J. Watson Research
|
||||
/* P.O. Box 704
|
||||
/* Yorktown Heights, NY 10598, USA
|
||||
/*--*/
|
||||
|
||||
/* System library. */
|
||||
|
||||
#include <sys_defs.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* Utility library. */
|
||||
|
||||
#include <msg.h>
|
||||
#include <mymalloc.h>
|
||||
#include <ring.h>
|
||||
#include <htable.h>
|
||||
#include <ctable.h>
|
||||
|
||||
/*
|
||||
* Cache entries are kept in most-recently used order. We use a hash table
|
||||
* to quickly locate cache entries.
|
||||
*/
|
||||
#define CTABLE_ENTRY struct ctable_entry
|
||||
|
||||
struct ctable_entry {
|
||||
RING ring; /* MRU linkage */
|
||||
const char *key; /* lookup key */
|
||||
void *value; /* corresponding value */
|
||||
};
|
||||
|
||||
#define RING_TO_CTABLE_ENTRY(ring_ptr) \
|
||||
RING_TO_APPL(ring_ptr, CTABLE_ENTRY, ring)
|
||||
#define RING_PTR_OF(x) (&((x)->ring))
|
||||
|
||||
struct ctable {
|
||||
HTABLE *table; /* table with key, ctable_entry pairs */
|
||||
unsigned limit; /* max nr of entries */
|
||||
unsigned used; /* current nr of entries */
|
||||
CTABLE_CREATE_FN create; /* constructor */
|
||||
CTABLE_DELETE_FN delete; /* destructor */
|
||||
RING ring; /* MRU linkage */
|
||||
void *context; /* application context */
|
||||
};
|
||||
|
||||
#define CTABLE_MIN_SIZE 5
|
||||
|
||||
/* ctable_create - create empty cache */
|
||||
|
||||
CTABLE *ctable_create(int limit, CTABLE_CREATE_FN create,
|
||||
CTABLE_DELETE_FN delete, void *context)
|
||||
{
|
||||
CTABLE *cache = (CTABLE *) mymalloc(sizeof(CTABLE));
|
||||
char *myname = "ctable_create";
|
||||
|
||||
if (limit < 1)
|
||||
msg_panic("%s: bad cache limit: %d", myname, limit);
|
||||
|
||||
cache->table = htable_create(limit);
|
||||
cache->limit = (limit < CTABLE_MIN_SIZE ? CTABLE_MIN_SIZE : limit);
|
||||
cache->used = 0;
|
||||
cache->create = create;
|
||||
cache->delete = delete;
|
||||
ring_init(RING_PTR_OF(cache));
|
||||
cache->context = context;
|
||||
return (cache);
|
||||
}
|
||||
|
||||
/* ctable_locate - look up or create cache item */
|
||||
|
||||
const void *ctable_locate(CTABLE *cache, const char *key)
|
||||
{
|
||||
char *myname = "ctable_locate";
|
||||
CTABLE_ENTRY *entry;
|
||||
|
||||
/*
|
||||
* If the entry is not in the cache, make sure there is room for a new
|
||||
* entry and install it at the front of the MRU chain. Otherwise, move
|
||||
* the entry to the front of the MRU chain if it is not already there.
|
||||
* All this means that the cache never shrinks.
|
||||
*/
|
||||
if ((entry = (CTABLE_ENTRY *) htable_find(cache->table, key)) == 0) {
|
||||
if (cache->used >= cache->limit) {
|
||||
entry = RING_TO_CTABLE_ENTRY(ring_pred(RING_PTR_OF(cache)));
|
||||
if (msg_verbose)
|
||||
msg_info("%s: purge entry key %s", myname, entry->key);
|
||||
ring_detach(RING_PTR_OF(entry));
|
||||
cache->delete(entry->value, cache->context);
|
||||
htable_delete(cache->table, entry->key, (void (*) (char *)) 0);
|
||||
} else {
|
||||
entry = (CTABLE_ENTRY *) mymalloc(sizeof(CTABLE_ENTRY));
|
||||
cache->used++;
|
||||
}
|
||||
entry->value = cache->create(key, cache->context);
|
||||
entry->key = htable_enter(cache->table, key, (char *) entry)->key;
|
||||
ring_append(RING_PTR_OF(cache), RING_PTR_OF(entry));
|
||||
if (msg_verbose)
|
||||
msg_info("%s: install entry key %s", myname, entry->key);
|
||||
} else if (entry == RING_TO_CTABLE_ENTRY(ring_succ(RING_PTR_OF(cache)))) {
|
||||
if (msg_verbose)
|
||||
msg_info("%s: leave existing entry key %s", myname, entry->key);
|
||||
} else {
|
||||
ring_detach(RING_PTR_OF(entry));
|
||||
ring_append(RING_PTR_OF(cache), RING_PTR_OF(entry));
|
||||
if (msg_verbose)
|
||||
msg_info("%s: move existing entry key %s", myname, entry->key);
|
||||
}
|
||||
return (entry->value);
|
||||
}
|
||||
|
||||
static CTABLE *ctable_free_cache;
|
||||
|
||||
/* ctable_free_callback - callback function */
|
||||
|
||||
static void ctable_free_callback(char *ptr)
|
||||
{
|
||||
CTABLE_ENTRY *entry = (CTABLE_ENTRY *) ptr;
|
||||
|
||||
ctable_free_cache->delete(entry->value, ctable_free_cache->context);
|
||||
myfree((char *) entry);
|
||||
}
|
||||
|
||||
/* ctable_free - destroy cache and contents */
|
||||
|
||||
void ctable_free(CTABLE *cache)
|
||||
{
|
||||
CTABLE *saved_cache = ctable_free_cache;
|
||||
|
||||
/*
|
||||
* XXX the hash table does not pass application context so we have to
|
||||
* store it in a global variable.
|
||||
*/
|
||||
ctable_free_cache = cache;
|
||||
htable_free(cache->table, ctable_free_callback);
|
||||
myfree((char *) cache);
|
||||
ctable_free_cache = saved_cache;
|
||||
}
|
||||
|
||||
/* ctable_walk - iterate over all cache entries */
|
||||
|
||||
void ctable_walk(CTABLE *cache, void (*action) (const char *, const void *))
|
||||
{
|
||||
RING *entry = RING_PTR_OF(cache);
|
||||
|
||||
/* Walking down the MRU chain is less work than using ht_walk(). */
|
||||
|
||||
while ((entry = ring_succ(entry)) != RING_PTR_OF(cache))
|
||||
action((RING_TO_CTABLE_ENTRY(entry)->key),
|
||||
(RING_TO_CTABLE_ENTRY(entry)->value));
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
/*
|
||||
* Proof-of-concept test program. Read keys from stdin, ask for values not
|
||||
* in cache.
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#include <vstream.h>
|
||||
#include <vstring.h>
|
||||
#include <vstring_vstream.h>
|
||||
#include <msg_vstream.h>
|
||||
|
||||
#define STR(x) vstring_str(x)
|
||||
|
||||
static void *ask(const char *key, void *context)
|
||||
{
|
||||
VSTRING *data_buf = (VSTRING *) context;
|
||||
|
||||
vstream_printf("ask: %s = ", key);
|
||||
vstream_fflush(VSTREAM_OUT);
|
||||
if (vstring_get_nonl(data_buf, VSTREAM_IN) == VSTREAM_EOF)
|
||||
vstream_longjmp(VSTREAM_IN, 1);
|
||||
if (!isatty(0)) {
|
||||
vstream_printf("%s\n", STR(data_buf));
|
||||
vstream_fflush(VSTREAM_OUT);
|
||||
}
|
||||
return (mystrdup(STR(data_buf)));
|
||||
}
|
||||
|
||||
static void drop(void *data, void *unused_context)
|
||||
{
|
||||
myfree(data);
|
||||
}
|
||||
|
||||
int main(int unused_argc, char **argv)
|
||||
{
|
||||
VSTRING *key_buf;
|
||||
VSTRING *data_buf;
|
||||
CTABLE *cache;
|
||||
const char *value;
|
||||
|
||||
msg_vstream_init(argv[0], VSTREAM_ERR);
|
||||
key_buf = vstring_alloc(100);
|
||||
data_buf = vstring_alloc(100);
|
||||
cache = ctable_create(1, ask, drop, (void *) data_buf);
|
||||
msg_verbose = 1;
|
||||
vstream_control(VSTREAM_IN, VSTREAM_CTL_EXCEPT, VSTREAM_CTL_END);
|
||||
|
||||
if (vstream_setjmp(VSTREAM_IN) == 0) {
|
||||
for (;;) {
|
||||
vstream_printf("key = ");
|
||||
vstream_fflush(VSTREAM_OUT);
|
||||
if (vstring_get_nonl(key_buf, VSTREAM_IN) == VSTREAM_EOF)
|
||||
vstream_longjmp(VSTREAM_IN, 1);
|
||||
if (!isatty(0)) {
|
||||
vstream_printf("%s\n", STR(key_buf));
|
||||
vstream_fflush(VSTREAM_OUT);
|
||||
}
|
||||
value = ctable_locate(cache, STR(key_buf));
|
||||
vstream_printf("result: %s\n", value);
|
||||
}
|
||||
}
|
||||
ctable_free(cache);
|
||||
vstring_free(key_buf);
|
||||
vstring_free(data_buf);
|
||||
return (0);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef _CTABLE_H_INCLUDED_
|
||||
#define _CTABLE_H_INCLUDED_
|
||||
|
||||
/*++
|
||||
/* NAME
|
||||
/* ctable 5
|
||||
/* SUMMARY
|
||||
/* cache manager
|
||||
/* SYNOPSIS
|
||||
/* #include <ctable.h>
|
||||
/* DESCRIPTION
|
||||
/* .nf
|
||||
|
||||
/*
|
||||
* Interface of the cache manager. The structure of a cache is not visible
|
||||
* to the caller.
|
||||
*/
|
||||
|
||||
#define CTABLE struct ctable
|
||||
typedef void *(*CTABLE_CREATE_FN) (const char *, void *);
|
||||
typedef void (*CTABLE_DELETE_FN) (void *, void *);
|
||||
|
||||
extern CTABLE *ctable_create(int, CTABLE_CREATE_FN, CTABLE_DELETE_FN, void *);
|
||||
extern void ctable_free(CTABLE *);
|
||||
extern void ctable_walk(CTABLE *, void (*) (const char *, const void *));
|
||||
extern const void *ctable_locate(CTABLE *, const char *);
|
||||
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
/* The Secure Mailer license must be distributed with this software.
|
||||
/* AUTHOR(S)
|
||||
/* Wietse Venema
|
||||
/* IBM T.J. Watson Research
|
||||
/* P.O. Box 704
|
||||
/* Yorktown Heights, NY 10598, USA
|
||||
/*--*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
a
|
||||
1
|
||||
b
|
||||
2
|
||||
c
|
||||
3
|
||||
d
|
||||
4
|
||||
e
|
||||
5
|
||||
f
|
||||
6
|
||||
f
|
||||
a
|
||||
1
|
||||
b
|
||||
2
|
||||
c
|
||||
3
|
||||
d
|
||||
4
|
||||
e
|
||||
5
|
||||
f
|
||||
6
|
||||
f
|
||||
e
|
||||
d
|
||||
c
|
||||
b
|
||||
a
|
||||
1
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
6
|
||||
f
|
|
@ -0,0 +1,99 @@
|
|||
key = a
|
||||
ask: a = 1
|
||||
./ctable: ctable_locate: install entry key a
|
||||
result: 1
|
||||
key = b
|
||||
ask: b = 2
|
||||
./ctable: ctable_locate: install entry key b
|
||||
result: 2
|
||||
key = c
|
||||
ask: c = 3
|
||||
./ctable: ctable_locate: install entry key c
|
||||
result: 3
|
||||
key = d
|
||||
ask: d = 4
|
||||
./ctable: ctable_locate: install entry key d
|
||||
result: 4
|
||||
key = e
|
||||
ask: e = 5
|
||||
./ctable: ctable_locate: install entry key e
|
||||
result: 5
|
||||
key = f
|
||||
./ctable: ctable_locate: purge entry key a
|
||||
ask: f = 6
|
||||
./ctable: ctable_locate: install entry key f
|
||||
result: 6
|
||||
key = f
|
||||
./ctable: ctable_locate: leave existing entry key f
|
||||
result: 6
|
||||
key = a
|
||||
./ctable: ctable_locate: purge entry key b
|
||||
ask: a = 1
|
||||
./ctable: ctable_locate: install entry key a
|
||||
result: 1
|
||||
key = b
|
||||
./ctable: ctable_locate: purge entry key c
|
||||
ask: b = 2
|
||||
./ctable: ctable_locate: install entry key b
|
||||
result: 2
|
||||
key = c
|
||||
./ctable: ctable_locate: purge entry key d
|
||||
ask: c = 3
|
||||
./ctable: ctable_locate: install entry key c
|
||||
result: 3
|
||||
key = d
|
||||
./ctable: ctable_locate: purge entry key e
|
||||
ask: d = 4
|
||||
./ctable: ctable_locate: install entry key d
|
||||
result: 4
|
||||
key = e
|
||||
./ctable: ctable_locate: purge entry key f
|
||||
ask: e = 5
|
||||
./ctable: ctable_locate: install entry key e
|
||||
result: 5
|
||||
key = f
|
||||
./ctable: ctable_locate: purge entry key a
|
||||
ask: f = 6
|
||||
./ctable: ctable_locate: install entry key f
|
||||
result: 6
|
||||
key = f
|
||||
./ctable: ctable_locate: leave existing entry key f
|
||||
result: 6
|
||||
key = e
|
||||
./ctable: ctable_locate: move existing entry key e
|
||||
result: 5
|
||||
key = d
|
||||
./ctable: ctable_locate: move existing entry key d
|
||||
result: 4
|
||||
key = c
|
||||
./ctable: ctable_locate: move existing entry key c
|
||||
result: 3
|
||||
key = b
|
||||
./ctable: ctable_locate: move existing entry key b
|
||||
result: 2
|
||||
key = a
|
||||
./ctable: ctable_locate: purge entry key f
|
||||
ask: a = 1
|
||||
./ctable: ctable_locate: install entry key a
|
||||
result: 1
|
||||
key = b
|
||||
./ctable: ctable_locate: move existing entry key b
|
||||
result: 2
|
||||
key = c
|
||||
./ctable: ctable_locate: move existing entry key c
|
||||
result: 3
|
||||
key = d
|
||||
./ctable: ctable_locate: move existing entry key d
|
||||
result: 4
|
||||
key = e
|
||||
./ctable: ctable_locate: move existing entry key e
|
||||
result: 5
|
||||
key = f
|
||||
./ctable: ctable_locate: purge entry key a
|
||||
ask: f = 6
|
||||
./ctable: ctable_locate: install entry key f
|
||||
result: 6
|
||||
key = f
|
||||
./ctable: ctable_locate: leave existing entry key f
|
||||
result: 6
|
||||
key =
|
|
@ -151,6 +151,10 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
|
|||
void (*saved_alarm) (int);
|
||||
int rc = 0;
|
||||
|
||||
#ifdef LDAP_API_FEATURE_X_MEMCACHE
|
||||
LDAPMemCache *dircache;
|
||||
#endif
|
||||
|
||||
#ifdef LDAP_OPT_NETWORK_TIMEOUT
|
||||
struct timeval mytimeval;
|
||||
|
||||
|
@ -162,7 +166,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
|
|||
msg_info("%s: Connecting to server %s", myname,
|
||||
dict_ldap->server_host);
|
||||
|
||||
#ifdef UNTESTED_LDAP_OPT_NETWORK_TIMEOUT
|
||||
#ifdef LDAP_OPT_NETWORK_TIMEOUT
|
||||
dict_ldap->ld = ldap_init(dict_ldap->server_host,
|
||||
(int) dict_ldap->server_port);
|
||||
if (dict_ldap->ld == NULL) {
|
||||
|
@ -247,6 +251,27 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
|
|||
myname, dict_ldap->cache_size, dict_ldap->ldapsource,
|
||||
dict_ldap->cache_expiry);
|
||||
|
||||
#ifdef LDAP_API_FEATURE_X_MEMCACHE
|
||||
rc = ldap_memcache_init(dict_ldap->cache_expiry, dict_ldap->cache_size,
|
||||
NULL, NULL, &dircache);
|
||||
if (rc != LDAP_SUCCESS) {
|
||||
msg_warn
|
||||
("%s: Unable to configure cache for %s: %d (%s) -- continuing",
|
||||
myname, dict_ldap->ldapsource, rc, ldap_err2string(rc));
|
||||
} else {
|
||||
rc = ldap_memcache_set(dict_ldap->ld, dircache);
|
||||
if (rc != LDAP_SUCCESS) {
|
||||
msg_warn
|
||||
("%s: Unable to configure cache for %s: %d (%s) -- continuing",
|
||||
myname, dict_ldap->ldapsource, rc, ldap_err2string(rc));
|
||||
} else {
|
||||
if (msg_verbose)
|
||||
msg_info("%s: Caching enabled for %s",
|
||||
myname, dict_ldap->ldapsource);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
rc = ldap_enable_cache(dict_ldap->ld, dict_ldap->cache_expiry,
|
||||
dict_ldap->cache_size);
|
||||
if (rc != LDAP_SUCCESS) {
|
||||
|
@ -258,6 +283,8 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
|
|||
msg_info("%s: Caching enabled for %s",
|
||||
myname, dict_ldap->ldapsource);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
if (msg_verbose)
|
||||
msg_info("%s: Cached connection handle for LDAP source %s",
|
||||
|
@ -315,7 +342,7 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
|
|||
if (strcasecmp(dict_ldap->result_attributes->argv[i],
|
||||
attr) == 0) {
|
||||
if (msg_verbose)
|
||||
msg_info("%s: search returned value(s) for requested result attribute %s", myname, attr);
|
||||
msg_info("%s: search returned %d value(s) for requested result attribute %s", myname, i, attr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +417,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
|
|||
* load on the LDAP server.
|
||||
*/
|
||||
if (dict_ldap->domain) {
|
||||
char *p=strrchr(name,'@');
|
||||
const char *p=strrchr(name,'@');
|
||||
if (p != 0)
|
||||
p=p+1;
|
||||
else
|
||||
|
@ -482,7 +509,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
|
|||
/*
|
||||
* Does the supplied query_filter even include a substitution?
|
||||
*/
|
||||
if ((char *) strstr(dict_ldap->query_filter, "%s") == NULL) {
|
||||
if ((char *) strchr(dict_ldap->query_filter, '%') == NULL) {
|
||||
|
||||
/*
|
||||
* No, log the fact and continue.
|
||||
|
@ -494,21 +521,40 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
|
|||
|
||||
/*
|
||||
* Yes, replace all instances of %s with the address to look up.
|
||||
* Replace %u with the user portion, and %d with the domain portion.
|
||||
*/
|
||||
sub = dict_ldap->query_filter;
|
||||
end = sub + strlen(dict_ldap->query_filter);
|
||||
while (sub < end) {
|
||||
|
||||
/*
|
||||
* Make sure it's %s and not something else, though it wouldn't
|
||||
* really matter; the token could be any single character.
|
||||
* Make sure it's %[sud] and not something else. For backward
|
||||
* compatibilty, treat anything other than %u or %d as %s, with
|
||||
* a warning.
|
||||
*/
|
||||
if (*(sub) == '%') {
|
||||
if ((sub + 1) != end && *(sub + 1) != 's')
|
||||
msg_warn
|
||||
("%s: Invalid lookup substitution format '%%%c'!",
|
||||
myname, *(sub + 1));
|
||||
vstring_strcat(filter_buf, vstring_str(escaped_name));
|
||||
char *u=vstring_str(escaped_name);
|
||||
char *p=strchr(u,'@');
|
||||
switch (*(sub+1)) {
|
||||
case 'd':
|
||||
if (p)
|
||||
vstring_strcat(filter_buf, p+1);
|
||||
break;
|
||||
case 'u':
|
||||
if (p)
|
||||
vstring_strncat(filter_buf, u, p-u);
|
||||
else
|
||||
vstring_strcat(filter_buf, u);
|
||||
break;
|
||||
default:
|
||||
msg_warn
|
||||
("%s: Invalid lookup substitution format '%%%c'!",
|
||||
myname, *(sub + 1));
|
||||
/* fall through */
|
||||
case 's':
|
||||
vstring_strcat(filter_buf, u);
|
||||
break;
|
||||
}
|
||||
sub++;
|
||||
} else
|
||||
vstring_strncat(filter_buf, sub, 1);
|
||||
|
@ -607,7 +653,8 @@ static void dict_ldap_close(DICT *dict)
|
|||
myfree(dict_ldap->ldapsource);
|
||||
myfree(dict_ldap->server_host);
|
||||
myfree(dict_ldap->search_base);
|
||||
match_list_free(dict_ldap->domain);
|
||||
if (dict_ldap->domain)
|
||||
match_list_free(dict_ldap->domain);
|
||||
myfree(dict_ldap->query_filter);
|
||||
argv_free(dict_ldap->result_attributes);
|
||||
myfree(dict_ldap->bind_dn);
|
||||
|
@ -626,15 +673,15 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
|
|||
char *scope;
|
||||
char *attr;
|
||||
|
||||
if (msg_verbose)
|
||||
msg_info("%s: Using LDAP source %s", myname, ldapsource);
|
||||
|
||||
dict_ldap = (DICT_LDAP *) dict_alloc(DICT_TYPE_LDAP, ldapsource,
|
||||
sizeof(*dict_ldap));
|
||||
dict_ldap->dict.lookup = dict_ldap_lookup;
|
||||
dict_ldap->dict.close = dict_ldap_close;
|
||||
dict_ldap->dict.flags = dict_flags | DICT_FLAG_FIXED;
|
||||
|
||||
if (msg_verbose)
|
||||
msg_info("%s: Using LDAP source %s", myname, ldapsource);
|
||||
|
||||
dict_ldap->ldapsource = mystrdup(ldapsource);
|
||||
|
||||
config_param = vstring_alloc(15);
|
||||
|
|
|
@ -266,6 +266,7 @@ DICT *dict_pcre_open(const char *map, int unused_flags, int dict_flags)
|
|||
continue;
|
||||
|
||||
p = vstring_str(line_buffer);
|
||||
trimblanks(p, 0)[0] = 0; /* Trim space at end */
|
||||
re_delimiter = *p++;
|
||||
regexp = p;
|
||||
|
||||
|
|
|
@ -365,6 +365,8 @@ DICT *dict_regexp_open(const char *map, int unused_flags, int dict_flags)
|
|||
if (*p == 0) /* Skip blank lines */
|
||||
continue;
|
||||
|
||||
trimblanks(p, 0)[0] = 0; /* Trim space at end */
|
||||
|
||||
rule = dict_regexp_parseline(lineno, p, &nsub, map_fp);
|
||||
if (rule) {
|
||||
if (nsub > max_nsub)
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
168.100.189.2
|
||||
168.100.189.2
|
||||
168.100.189.1
|
||||
168.100.189.3
|
||||
168.100.189.3
|
||||
168.100.189.3
|
||||
168.100.189.4
|
||||
168.100.189.1
|
||||
168.100.189.4
|
|
@ -0,0 +1,15 @@
|
|||
unknown: list before sort/uniq
|
||||
unknown: 168.100.189.2
|
||||
unknown: 168.100.189.2
|
||||
unknown: 168.100.189.1
|
||||
unknown: 168.100.189.3
|
||||
unknown: 168.100.189.3
|
||||
unknown: 168.100.189.3
|
||||
unknown: 168.100.189.4
|
||||
unknown: 168.100.189.1
|
||||
unknown: 168.100.189.4
|
||||
unknown: list after sort/uniq
|
||||
unknown: 168.100.189.1
|
||||
unknown: 168.100.189.2
|
||||
unknown: 168.100.189.3
|
||||
unknown: 168.100.189.4
|
|
@ -29,6 +29,17 @@ extern void ring_detach(RING *);
|
|||
#define ring_succ(c) ((c)->succ)
|
||||
#define ring_pred(c) ((c)->pred)
|
||||
|
||||
/*
|
||||
* Typically, an application will embed a RING structure into a larger
|
||||
* structure that also contains application-specific members. This approach
|
||||
* gives us the best of both worlds. The application can still use the
|
||||
* generic RING primitives for manipulating RING structures. The macro below
|
||||
* transforms a pointer from RING structure to the structure that contains
|
||||
* it.
|
||||
*/
|
||||
#define RING_TO_APPL(ring_ptr,app_type,ring_member) \
|
||||
((app_type *) (((char *) (ring_ptr)) - offsetof(app_type,ring_member)))
|
||||
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
|
|
Loading…
Reference in New Issue