Apply the specified access database
+to the DNS servers (or MX hosts) for the host or domain name given
+with the RCPT TO command.
+
+ Note: an OK result is not allowed for safety reasons.
+
+
+
check_recipient_maps Reject the request
diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c
index c35338159..e7816e442 100644
--- a/postfix/src/dns/dns_lookup.c
+++ b/postfix/src/dns/dns_lookup.c
@@ -509,6 +509,7 @@ int dns_lookup(const char *name, unsigned type, unsigned flags,
vstring_sprintf(why,
"Name service error for %s: invalid host or domain name",
name);
+ h_errno = HOST_NOT_FOUND;
return (DNS_NOTFOUND);
}
@@ -520,6 +521,7 @@ int dns_lookup(const char *name, unsigned type, unsigned flags,
vstring_sprintf(why,
"Name service error for %s: invalid host or domain name",
name);
+ h_errno = HOST_NOT_FOUND;
return (DNS_NOTFOUND);
}
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index 0d16455bf..b35f5617b 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -1314,6 +1314,13 @@ extern int var_access_map_code;
#define CHECK_RECIP_ACL "check_recipient_access"
#define CHECK_ETRN_ACL "check_etrn_access"
+#define CHECK_HELO_MX_ACL "check_helo_mx_access"
+#define CHECK_SENDER_MX_ACL "check_sender_mx_access"
+#define CHECK_RECIP_MX_ACL "check_recipient_mx_access"
+#define CHECK_HELO_NS_ACL "check_helo_ns_access"
+#define CHECK_SENDER_NS_ACL "check_sender_ns_access"
+#define CHECK_RECIP_NS_ACL "check_recipient_ns_access"
+
#define WARN_IF_REJECT "warn_if_reject"
#define REJECT_RBL "reject_rbl" /* LaMont compatibility */
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index f64592d5b..0b394b79b 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,10 +20,10 @@
* Patches change the patchlevel and the release date. Snapshots change the
* release date only, unless they include the same bugfix as a patch release.
*/
-#define MAIL_RELEASE_DATE "20030915"
+#define MAIL_RELEASE_DATE "20030917"
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "2.0.14-" MAIL_RELEASE_DATE
+#define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE
extern char *var_mail_version;
/*
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index 387ffa65d..2f0f0cc20 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -89,6 +89,18 @@
/* .IP "check_recipient_access maptype:mapname"
/* Look up the resolved recipient address in the named access table,
/* any parent domains of the recipient domain, and the localpart@.
+/* .IP "check_helo_mx_access maptype:mapname"
+/* .IP "check_sender_mx_access maptype:mapname"
+/* .IP "check_recipient_mx_access maptype:mapname"
+/* Apply the specified access table to the MX server host name and IP
+/* addresses for the helo hostname, sender, or recipient, respectively.
+/* If no MX record is found the A record is used instead.
+/* .IP "check_helo_ns_access maptype:mapname"
+/* .IP "check_sender_ns_access maptype:mapname"
+/* .IP "check_recipient_ns_access maptype:mapname"
+/* Apply the specified access table to the DNS server host name and IP
+/* addresses for the helo hostname, sender, or recipient, respectively.
+/* If no NS record is found, the parent domain is used instead.
/* .IP "check_recipient_maps"
/* Reject recipients not listed as valid local, virtual or relay
/* recipients.
@@ -482,6 +494,12 @@ static void PRINTFLIKE(3, 4) defer_if(SMTPD_DEFER *, int, const char *,...);
else \
(void) smtpd_check_reject((state), (class), (fmt), (a1), (a2), (a3)); \
} while (0)
+#define DEFER_IF_PERMIT4(state, class, fmt, a1, a2, a3, a4) do { \
+ if ((state)->warn_if_reject == 0) \
+ defer_if(&(state)->defer_if_permit, (class), (fmt), (a1), (a2), (a3), (a4)); \
+ else \
+ (void) smtpd_check_reject((state), (class), (fmt), (a1), (a2), (a3), (a4)); \
+ } while (0)
/*
* Cached RBL lookup state.
@@ -2153,6 +2171,127 @@ static int check_namadr_access(SMTPD_STATE *state, const char *table,
return (SMTPD_CHECK_DUNNO);
}
+/* check_server_access - access control by server host name or address */
+
+static int check_server_access(SMTPD_STATE *state, const char *table,
+ const char *name,
+ int type,
+ const char *reply_name,
+ const char *reply_class,
+ const char *def_acl)
+{
+ const char *myname = "check_server_access";
+ const char *domain;
+ int dns_status;
+ DNS_RR *server_list;
+ DNS_RR *server;
+ int found = 0;
+ struct in_addr addr;
+ struct hostent *hp;
+ char *addr_string;
+ int status;
+ char **cpp;
+ static DNS_FIXED fixed;
+
+ /*
+ * Sanity check.
+ */
+ if (type != T_MX && type != T_NS)
+ msg_panic("%s: unexpected resource type \"%s\" in request",
+ myname, dns_strtype(type));
+
+ if (msg_verbose)
+ msg_info("%s: %s %s", myname, dns_strtype(type), name);
+
+ /*
+ * Skip over local-part.
+ */
+ if ((domain = strrchr(name, '@')) != 0)
+ domain += 1;
+ else
+ domain = name;
+
+ /*
+ * If the domain does not exist then we apply no restriction. In all
+ * other cases, DNS lookup failure results in a "try again" status.
+ *
+ * If the domain name exists but MX lookup fails, fabricate an MX record
+ * that points to the domain name itself.
+ *
+ * If the domain name exists but NS lookup fails, look up the parent domain
+ * NS record.
+ */
+ dns_status = dns_lookup(domain, type, 0, &server_list,
+ (VSTRING *) 0, (VSTRING *) 0);
+ if (dns_status == DNS_NOTFOUND && h_errno != HOST_NOT_FOUND) {
+ if (type == T_MX) {
+ server_list = dns_rr_create(domain, &fixed, 0,
+ domain, strlen(domain) + 1);
+ dns_status = DNS_OK;
+ } else if (type == T_NS && (domain = strchr(domain, '.')) != 0
+ && strchr(++domain, '.') != 0) {
+ dns_status = dns_lookup(domain, T_NS, 0, &server_list,
+ (VSTRING *) 0, (VSTRING *) 0);
+ if (dns_status != DNS_OK)
+ dns_status = DNS_RETRY;
+ }
+ }
+ if (dns_status == DNS_NOTFOUND)
+ return (SMTPD_CHECK_DUNNO);
+ if (dns_status != DNS_OK) {
+ DEFER_IF_PERMIT3(state, MAIL_ERROR_POLICY,
+ "450 <%s>: %s rejected: unable to look up %s host",
+ reply_name, reply_class, dns_strtype(type));
+ return (SMTPD_CHECK_DUNNO);
+ }
+
+ /*
+ * No bare returns after this point or we have a memory leak.
+ */
+#define CHECK_SERVER_RETURN(x) { dns_rr_free(server_list); return(x); }
+
+ /*
+ * Check the hostnames first, then the addresses.
+ */
+ for (server = server_list; server != 0; server = server->next) {
+ if ((hp = gethostbyname((char *) server->data)) == 0) {
+ DEFER_IF_PERMIT4(state, MAIL_ERROR_POLICY,
+ "450 <%s>: %s rejected: "
+ "Unable to look up %s host %s",
+ reply_name, reply_class,
+ dns_strtype(type), (char *) server->data);
+ CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO);
+ }
+ if (hp->h_addrtype != AF_INET || hp->h_length != sizeof(addr)) {
+ if (msg_verbose)
+ msg_warn("address type %d length %d for %s",
+ hp->h_addrtype, hp->h_length, (char *) server->data);
+ continue; /* XXX */
+ }
+ if (msg_verbose)
+ msg_info("%s: %s hostname check: %s",
+ myname, dns_strtype(type), (char *) server->data);
+ if ((status = check_domain_access(state, table, (char *) server->data,
+ FULL, &found, reply_name, reply_class,
+ def_acl)) != 0 || found)
+ CHECK_SERVER_RETURN(status);
+ if (msg_verbose)
+ msg_info("%s: %s host address check: %s",
+ myname, dns_strtype(type), (char *) server->data);
+ for (cpp = hp->h_addr_list; *cpp; cpp++) {
+ memcpy((char *) &addr, *cpp, sizeof(addr));
+ addr_string = mystrdup(inet_ntoa(addr));
+ status = check_addr_access(state, table, addr_string, FULL,
+ &found, reply_name, reply_class,
+ def_acl);
+ myfree(addr_string);
+ if (status != 0 || found)
+ CHECK_SERVER_RETURN(status);
+ }
+ }
+ CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO);
+}
+
/* check_mail_access - OK/FAIL based on mail address lookup */
static int check_mail_access(SMTPD_STATE *state, const char *table,
@@ -2818,6 +2957,20 @@ static int is_map_command(SMTPD_STATE *state, const char *name,
}
}
+/* forbid_whitelist - disallow whitelisting */
+
+static void forbid_whitelist(SMTPD_STATE *state, const char *name,
+ int status, const char *target)
+{
+ if (status == SMTPD_CHECK_OK) {
+ msg_warn("restriction %s returns OK for %s", name, target);
+ msg_warn("this is not allowed for security reasons");
+ msg_warn("use DUNNO instead of OK if you want to make an exception");
+ longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
+ "451 Server configuration error"));
+ }
+}
+
/* generic_checks - generic restrictions */
static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
@@ -2979,6 +3132,20 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
state->helo_name, SMTPD_NAME_HELO)) == 0)
status = SMTPD_CHECK_OK;
}
+ } else if (is_map_command(state, name, CHECK_HELO_NS_ACL, &cpp)) {
+ if (state->helo_name) {
+ status = check_server_access(state, *cpp, state->helo_name,
+ T_NS, state->helo_name,
+ SMTPD_NAME_HELO, def_acl);
+ forbid_whitelist(state, name, status, state->helo_name);
+ }
+ } else if (is_map_command(state, name, CHECK_HELO_MX_ACL, &cpp)) {
+ if (state->helo_name) {
+ status = check_server_access(state, *cpp, state->helo_name,
+ T_MX, state->helo_name,
+ SMTPD_NAME_HELO, def_acl);
+ forbid_whitelist(state, name, status, state->helo_name);
+ }
} else if (strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) {
if (state->helo_name) {
if (*state->helo_name != '[')
@@ -3032,6 +3199,20 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
} else if (strcasecmp(name, REJECT_SENDER_LOGIN_MISMATCH) == 0) {
if (state->sender && *state->sender)
status = reject_sender_login_mismatch(state, state->sender);
+ } else if (is_map_command(state, name, CHECK_SENDER_NS_ACL, &cpp)) {
+ if (state->sender && *state->sender) {
+ status = check_server_access(state, *cpp, state->sender,
+ T_NS, state->sender,
+ SMTPD_NAME_SENDER, def_acl);
+ forbid_whitelist(state, name, status, state->sender);
+ }
+ } else if (is_map_command(state, name, CHECK_SENDER_MX_ACL, &cpp)) {
+ if (state->sender && *state->sender) {
+ status = check_server_access(state, *cpp, state->sender,
+ T_MX, state->sender,
+ SMTPD_NAME_SENDER, def_acl);
+ forbid_whitelist(state, name, status, state->sender);
+ }
} else if (strcasecmp(name, REJECT_RHSBL_SENDER) == 0) {
if (cpp[1] == 0)
msg_warn("restriction %s requires domain name argument", name);
@@ -3084,6 +3265,20 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
if (state->recipient)
status = reject_non_fqdn_address(state, state->recipient,
state->recipient, SMTPD_NAME_RECIPIENT);
+ } else if (is_map_command(state, name, CHECK_RECIP_NS_ACL, &cpp)) {
+ if (state->recipient && *state->recipient) {
+ status = check_server_access(state, *cpp, state->recipient,
+ T_NS, state->recipient,
+ SMTPD_NAME_RECIPIENT, def_acl);
+ forbid_whitelist(state, name, status, state->recipient);
+ }
+ } else if (is_map_command(state, name, CHECK_RECIP_MX_ACL, &cpp)) {
+ if (state->recipient && *state->recipient) {
+ status = check_server_access(state, *cpp, state->recipient,
+ T_MX, state->recipient,
+ SMTPD_NAME_RECIPIENT, def_acl);
+ forbid_whitelist(state, name, status, state->recipient);
+ }
} else if (strcasecmp(name, REJECT_RHSBL_RECIPIENT) == 0) {
if (cpp[1] == 0)
msg_warn("restriction %s requires domain name argument", name);
diff --git a/postfix/src/smtpd/smtpd_check_access b/postfix/src/smtpd/smtpd_check_access
index bfcb3d35f..1ee24aa48 100644
--- a/postfix/src/smtpd/smtpd_check_access
+++ b/postfix/src/smtpd/smtpd_check_access
@@ -56,3 +56,5 @@ holdtext@hold.domain hold text
discard@hold.domain discard
discardtext@hold.domain discard text
dunnotext@dunno.domain dunno text
+64.94.110.11 reject Verisign wild-card
+topica.com reject
diff --git a/postfix/src/smtpd/smtpd_exp.in b/postfix/src/smtpd/smtpd_exp.in
index 8c05d1069..71b188ef3 100644
--- a/postfix/src/smtpd/smtpd_exp.in
+++ b/postfix/src/smtpd/smtpd_exp.in
@@ -60,3 +60,32 @@ recipient_restrictions reject_rhsbl_helo,abuse.rfc-ignorant.org
helo example.tld
mail sname@sdomain
rcpt rname@rdomain
+#
+# Check MX access
+#
+helo_restrictions check_helo_mx_access,hash:smtpd_check_access
+helo verisign-wildcard.com
+helo verisign.com
+helo example.tld
+sender_restrictions check_sender_mx_access,hash:smtpd_check_access
+mail foo@verisign-wildcard.com
+mail foo@verisign.com
+recipient_restrictions check_recipient_mx_access,hash:smtpd_check_access
+rcpt foo@verisign-wildcard.com
+rcpt foo@verisign.com
+#
+# Check NS access
+#
+helo_restrictions check_helo_ns_access,hash:smtpd_check_access
+helo email-publisher.com
+helo ns1.topica.com
+helo verisign-wildcard.com
+helo example.tld
+sender_restrictions check_sender_ns_access,hash:smtpd_check_access
+mail foo@email-publisher.com
+mail foo@ns1.topica.com
+mail foo@verisign-wildcard.com
+recipient_restrictions check_recipient_ns_access,hash:smtpd_check_access
+rcpt foo@email-publisher.com
+rcpt foo@ns1.topica.com
+rcpt foo@verisign-wildcard.com
diff --git a/postfix/src/smtpd/smtpd_exp.ref b/postfix/src/smtpd/smtpd_exp.ref
index 950349874..2495de5f0 100644
--- a/postfix/src/smtpd/smtpd_exp.ref
+++ b/postfix/src/smtpd/smtpd_exp.ref
@@ -109,3 +109,64 @@ OK
>>> rcpt rname@rdomain
./smtpd_check: : reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 Service unavailable; Helo command [example.tld] blocked using abuse.rfc-ignorant.org; Not supporting abuse@domain; from= to= proto=SMTP helo=
554 Service unavailable; Helo command [example.tld] blocked using abuse.rfc-ignorant.org; Not supporting abuse@domain
+>>> #
+>>> # Check MX access
+>>> #
+>>> helo_restrictions check_helo_mx_access,hash:smtpd_check_access
+OK
+>>> helo verisign-wildcard.com
+./smtpd_check: : reject: HELO from spike.porcupine.org[168.100.189.2]: 554 : Helo command rejected: Verisign wild-card; from= proto=SMTP helo=
+554 : Helo command rejected: Verisign wild-card
+>>> helo verisign.com
+OK
+>>> helo example.tld
+OK
+>>> sender_restrictions check_sender_mx_access,hash:smtpd_check_access
+OK
+>>> mail foo@verisign-wildcard.com
+./smtpd_check: : reject: MAIL from spike.porcupine.org[168.100.189.2]: 554 : Sender address rejected: Verisign wild-card; from= proto=SMTP helo=
+554 : Sender address rejected: Verisign wild-card
+>>> mail foo@verisign.com
+OK
+>>> recipient_restrictions check_recipient_mx_access,hash:smtpd_check_access
+OK
+>>> rcpt foo@verisign-wildcard.com
+./smtpd_check: : reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 : Recipient address rejected: Verisign wild-card; from= to= proto=SMTP helo=
+554 : Recipient address rejected: Verisign wild-card
+>>> rcpt foo@verisign.com
+OK
+>>> #
+>>> # Check NS access
+>>> #
+>>> helo_restrictions check_helo_ns_access,hash:smtpd_check_access
+OK
+>>> helo email-publisher.com
+./smtpd_check: : reject: HELO from spike.porcupine.org[168.100.189.2]: 554 : Helo command rejected: Access denied; from= proto=SMTP helo=
+554 : Helo command rejected: Access denied
+>>> helo ns1.topica.com
+./smtpd_check: : reject: HELO from spike.porcupine.org[168.100.189.2]: 554 : Helo command rejected: Access denied; from= proto=SMTP helo=
+554 : Helo command rejected: Access denied
+>>> helo verisign-wildcard.com
+OK
+>>> helo example.tld
+OK
+>>> sender_restrictions check_sender_ns_access,hash:smtpd_check_access
+OK
+>>> mail foo@email-publisher.com
+./smtpd_check: : reject: MAIL from spike.porcupine.org[168.100.189.2]: 554 : Sender address rejected: Access denied; from= proto=SMTP helo=
+554 : Sender address rejected: Access denied
+>>> mail foo@ns1.topica.com
+./smtpd_check: : reject: MAIL from spike.porcupine.org[168.100.189.2]: 554 : Sender address rejected: Access denied; from= proto=SMTP helo=
+554 : Sender address rejected: Access denied
+>>> mail foo@verisign-wildcard.com
+OK
+>>> recipient_restrictions check_recipient_ns_access,hash:smtpd_check_access
+OK
+>>> rcpt foo@email-publisher.com
+./smtpd_check: : reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 : Recipient address rejected: Access denied; from= to= proto=SMTP helo=
+554 : Recipient address rejected: Access denied
+>>> rcpt foo@ns1.topica.com
+./smtpd_check: : reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 : Recipient address rejected: Access denied; from= to= proto=SMTP helo=
+554 : Recipient address rejected: Access denied
+>>> rcpt foo@verisign-wildcard.com
+OK
diff --git a/postfix/src/trivial-rewrite/resolve.c b/postfix/src/trivial-rewrite/resolve.c
index 0ea2b3698..90c3d7850 100644
--- a/postfix/src/trivial-rewrite/resolve.c
+++ b/postfix/src/trivial-rewrite/resolve.c
@@ -409,9 +409,11 @@ static void resolve_addr(RES_CONTEXT *rp, char *addr,
msg_warn("do not list domain %s in BOTH %s and %s",
rcpt_domain, VAR_VIRT_ALIAS_DOMS,
VAR_RELAY_DOMAINS);
+#if 0
if (strcasecmp(rcpt_domain, var_myorigin) == 0)
msg_warn("do not list $%s (%s) in %s",
VAR_MYORIGIN, var_myorigin, VAR_VIRT_ALIAS_DOMS);
+#endif
}
vstring_strcpy(channel, MAIL_SERVICE_ERROR);
vstring_sprintf(nexthop, "User unknown%s",
diff --git a/postfix/src/util/dict_ldap.c b/postfix/src/util/dict_ldap.c
index 1523c3397..e1c4bca74 100644
--- a/postfix/src/util/dict_ldap.c
+++ b/postfix/src/util/dict_ldap.c
@@ -180,11 +180,17 @@
#include "dict.h"
#include "dict_ldap.h"
#include "stringops.h"
+#include "binhash.h"
/* AAARGH!! */
#include "../global/mail_conf.h"
+typedef struct {
+ LDAP *conn_ld;
+ int conn_refcount;
+} LDAP_CONN;
+
/*
* Structure containing all the configuration parameters for a given
* LDAP source, plus its connection handle.
@@ -223,9 +229,14 @@ typedef struct {
char *tls_random_file;
char *tls_cipher_suite;
#endif
- LDAP *ld;
+ BINHASH_INFO *ht; /* hash entry for LDAP connection */
+ LDAP *ld; /* duplicated from conn->conn_ld */
} DICT_LDAP;
+#define DICT_LDAP_CONN(d) ((LDAP_CONN *)((d)->ht->value))
+
+static BINHASH *conn_hash = 0;
+
typedef struct {
char *(*get_str) (const char *, const char *, const char *, int, int);
int (*get_int) (const char *, const char *, int, int, int);
@@ -641,6 +652,9 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
msg_info("%s: Successful bind to server %s as %s ",
myname, dict_ldap->server_host, dict_ldap->bind_dn);
}
+ /* Save connection handle in shared container */
+ DICT_LDAP_CONN(dict_ldap)->conn_ld = dict_ldap->ld;
+
if (msg_verbose)
msg_info("%s: Cached connection handle for LDAP source %s",
myname, dict_ldap->ldapsource);
@@ -648,6 +662,58 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
return (0);
}
+/*
+ * Locate or allocate connection cache entry.
+ */
+static void dict_ldap_conn_find(DICT_LDAP *dict_ldap)
+{
+ VSTRING *keybuf = vstring_alloc(10);
+ char *key;
+ int len;
+ int sslon = dict_ldap->start_tls || dict_ldap->ldap_ssl;
+ LDAP_CONN *conn;
+
+#define ADDSTR(vp, s) vstring_memcat((vp), (s), strlen((s))+1)
+#define ADDINT(vp, i) vstring_sprintf_append((vp), "%lu", (unsigned long)(i))
+
+ ADDSTR(keybuf, dict_ldap->server_host);
+ ADDINT(keybuf, dict_ldap->server_port);
+ ADDINT(keybuf, dict_ldap->bind);
+ ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_dn : "");
+ ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_pw : "");
+ ADDINT(keybuf, dict_ldap->dereference);
+ ADDINT(keybuf, dict_ldap->chase_referrals);
+ ADDINT(keybuf, dict_ldap->debuglevel);
+ ADDINT(keybuf, dict_ldap->version);
+#ifdef LDAP_API_FEATURE_X_OPENLDAP
+ ADDINT(keybuf, dict_ldap->ldap_ssl);
+ ADDINT(keybuf, dict_ldap->start_tls);
+ ADDINT(keybuf, sslon ? dict_ldap->tls_require_cert : 0);
+ ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_file : "");
+ ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_dir : "");
+ ADDSTR(keybuf, sslon ? dict_ldap->tls_cert : "");
+ ADDSTR(keybuf, sslon ? dict_ldap->tls_key : "");
+ ADDSTR(keybuf, sslon ? dict_ldap->tls_random_file : "");
+ ADDSTR(keybuf, sslon ? dict_ldap->tls_cipher_suite : "");
+#endif
+
+ key = vstring_str(keybuf);
+ len = VSTRING_LEN(keybuf);
+
+ if (conn_hash == 0)
+ conn_hash = binhash_create(0);
+
+ if ((dict_ldap->ht = binhash_locate(conn_hash, key, len)) == 0) {
+ conn = (LDAP_CONN *) mymalloc(sizeof(LDAP_CONN));
+ conn->conn_ld = 0;
+ conn->conn_refcount = 0;
+ dict_ldap->ht = binhash_enter(conn_hash, key, len, (char *) conn);
+ }
+ ++DICT_LDAP_CONN(dict_ldap)->conn_refcount;
+
+ vstring_free(keybuf);
+}
+
/*
* expand a filter (lookup or result)
*/
@@ -907,6 +973,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
VSTRING *escaped_name = 0,
*filter_buf = 0;
int rc = 0;
+ int sizelimit;
char *sub,
*end;
@@ -923,11 +990,8 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
if (dict_ldap->domain) {
const char *p = strrchr(name, '@');
- if (p != 0)
- p = p + 1;
- else
- p = name;
- if (match_list_match(dict_ldap->domain, p) == 0) {
+ if (p == 0 || p == name ||
+ match_list_match(dict_ldap->domain, ++p) == 0) {
if (msg_verbose)
msg_info("%s: domain of %s not found in domain list", myname,
name);
@@ -942,6 +1006,13 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
result = vstring_alloc(2);
vstring_strcpy(result, "");
+ /*
+ * Because the connection may be shared and invalidated via queries for
+ * another map, update private copy of "ld" from shared connection
+ * container.
+ */
+ dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld;
+
/*
* Connect to the LDAP server, if necessary.
*/
@@ -962,6 +1033,18 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
msg_info("%s: Using existing connection for LDAP source %s",
myname, dict_ldap->ldapsource);
+ /*
+ * Connection caching, means that the connection handle may have the
+ * wrong size limit. Re-adjust before each query. This is cheap, just
+ * sets a field in the ldap connection handle. We also do this in the
+ * connect code, because we sometimes reconnect (below) in the middle of
+ * a query.
+ */
+ sizelimit = dict_ldap->size_limit ? dict_ldap->size_limit : LDAP_NO_LIMIT;
+ if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT, &sizelimit)
+ != LDAP_OPT_SUCCESS)
+ msg_warn("%s: %s: Unable to set query result size limit to %ld.",
+ myname, dict_ldap->ldapsource, dict_ldap->size_limit);
/*
* Prepare the query.
@@ -1046,7 +1129,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
myname, dict_ldap->ldapsource);
ldap_unbind(dict_ldap->ld);
- dict_ldap->ld = NULL;
+ dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
dict_ldap_connect(dict_ldap);
/*
@@ -1098,7 +1181,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
* next lookup.
*/
ldap_unbind(dict_ldap->ld);
- dict_ldap->ld = NULL;
+ dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
/*
* And tell the caller to try again later.
@@ -1129,10 +1212,18 @@ static void dict_ldap_close(DICT *dict)
{
char *myname = "dict_ldap_close";
DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
+ LDAP_CONN *conn = DICT_LDAP_CONN(dict_ldap);
+ BINHASH_INFO *ht = dict_ldap->ht;
- if (dict_ldap->ld)
- ldap_unbind(dict_ldap->ld);
-
+ if (--conn->conn_refcount == 0) {
+ if (conn->conn_ld) {
+ if (msg_verbose)
+ msg_info("%s: Closed connection handle for LDAP source %s",
+ myname, dict_ldap->ldapsource);
+ ldap_unbind(conn->conn_ld);
+ }
+ binhash_delete(conn_hash, ht->key, ht->key_len, myfree);
+ }
myfree(dict_ldap->ldapsource);
myfree(dict_ldap->server_host);
myfree(dict_ldap->search_base);
@@ -1272,6 +1363,11 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
}
}
dict_ldap->server_host = mystrdup(vstring_str(url_list) + 1);
+
+ /*
+ * With URL scheme, clear port to normalize connection cache key
+ */
+ dict_ldap->server_port = 0;
if (msg_verbose)
msg_info("%s: %s server_host URL is %s", myname, ldapsource,
dict_ldap->server_host);
@@ -1534,6 +1630,11 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
dict_ldap->debuglevel);
#endif
+ /*
+ * Find or allocate shared LDAP connection container.
+ */
+ dict_ldap_conn_find(dict_ldap);
+
/*
* Return the new dict_ldap structure.
*/