This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
# define T_A ns_t_a
#endif
# define T_SRV ns_t_srv
+#if !defined(T_NS) /* AIX 5.3 already defines T_NS */
# define T_NS ns_t_ns
+#endif
#else
# ifdef HFIXEDSZ
# define NS_HFIXEDSZ HFIXEDSZ
Simple wrapper for a DNS query
*********************************************************************/
+#define DNS_FAILED_WAITTIME 30
+
static NTSTATUS dns_send_req( TALLOC_CTX *ctx, const char *name, int q_type,
uint8 **buf, int *resp_length )
{
uint8 *buffer = NULL;
size_t buf_len;
int resp_len = NS_PACKETSZ;
-
+ static time_t last_dns_check = 0;
+ static NTSTATUS last_dns_status = NT_STATUS_OK;
+ time_t now = time(NULL);
+
+ /* Try to prevent bursts of DNS lookups if the server is down */
+
+ /* Protect against large clock changes */
+
+ if ( last_dns_check > now )
+ last_dns_check = 0;
+
+ /* IF we had a DNS timeout or a bad server and we are still
+ in the 30 second cache window, just return the previous
+ status and save the network timeout. */
+
+ if ( (NT_STATUS_EQUAL(last_dns_status,NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(last_dns_status,NT_STATUS_CONNECTION_REFUSED)) &&
+ (last_dns_check+DNS_FAILED_WAITTIME) > now )
+ {
+ DEBUG(10,("last_dns_check: Returning cached status (%s)\n",
+ nt_errstr(last_dns_status) ));
+ return last_dns_status;
+ }
+
+ /* Send the Query */
do {
if ( buffer )
TALLOC_FREE( buffer );
buf_len = resp_len * sizeof(uint8);
- if ( (buffer = TALLOC_ARRAY(ctx, uint8, buf_len)) == NULL ) {
- DEBUG(0,("ads_dns_lookup_srv: talloc() failed!\n"));
- return NT_STATUS_NO_MEMORY;
+ if (buf_len) {
+ if ( (buffer = TALLOC_ARRAY(ctx, uint8, buf_len)) == NULL ) {
+ DEBUG(0,("ads_dns_lookup_srv: talloc() failed!\n"));
+ last_dns_status = NT_STATUS_NO_MEMORY;
+ last_dns_check = time(NULL);
+ return last_dns_status;
+ }
}
if ( (resp_len = res_query(name, C_IN, q_type, buffer, buf_len)) < 0 ) {
- DEBUG(1,("ads_dns_lookup_srv: Failed to resolve %s (%s)\n", name, strerror(errno)));
+ DEBUG(3,("ads_dns_lookup_srv: Failed to resolve %s (%s)\n", name, strerror(errno)));
TALLOC_FREE( buffer );
- return NT_STATUS_UNSUCCESSFUL;
+ last_dns_status = NT_STATUS_UNSUCCESSFUL;
+
+ if (errno == ETIMEDOUT) {
+ last_dns_status = NT_STATUS_IO_TIMEOUT;
+ }
+ if (errno == ECONNREFUSED) {
+ last_dns_status = NT_STATUS_CONNECTION_REFUSED;
+ }
+ last_dns_check = time(NULL);
+ return last_dns_status;
}
} while ( buf_len < resp_len && resp_len < MAX_DNS_PACKET_SIZE );
*buf = buffer;
*resp_length = resp_len;
- return NT_STATUS_OK;
+ last_dns_check = time(NULL);
+ last_dns_status = NT_STATUS_OK;
+ return last_dns_status;
}
/*********************************************************************
status = dns_send_req( ctx, name, T_SRV, &buffer, &resp_len );
if ( !NT_STATUS_IS_OK(status) ) {
- DEBUG(0,("ads_dns_lookup_srv: Failed to send DNS query (%s)\n",
+ DEBUG(3,("ads_dns_lookup_srv: Failed to send DNS query (%s)\n",
nt_errstr(status)));
return status;
}
DEBUG(4,("ads_dns_lookup_srv: %d records returned in the answer section.\n",
answer_count));
- if ( (dcs = TALLOC_ZERO_ARRAY(ctx, struct dns_rr_srv, answer_count)) == NULL ) {
- DEBUG(0,("ads_dns_lookup_srv: talloc() failure for %d char*'s\n",
- answer_count));
- return NT_STATUS_NO_MEMORY;
+ if (answer_count) {
+ if ( (dcs = TALLOC_ZERO_ARRAY(ctx, struct dns_rr_srv, answer_count)) == NULL ) {
+ DEBUG(0,("ads_dns_lookup_srv: talloc() failure for %d char*'s\n",
+ answer_count));
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ dcs = NULL;
}
/* now skip the header */
if ( (rr.type != T_A) || (rr.rdatalen != 4) )
continue;
- /* FIX ME!!! Should this be a list of IP addresses for
- each host? */
-
for ( i=0; i<idx; i++ ) {
if ( strcmp( rr.hostname, dcs[i].hostname ) == 0 ) {
int num_ips = dcs[i].num_ips;
status = dns_send_req( ctx, dnsdomain, T_NS, &buffer, &resp_len );
if ( !NT_STATUS_IS_OK(status) ) {
- DEBUG(0,("ads_dns_lookup_ns: Failed to send DNS query (%s)\n",
+ DEBUG(3,("ads_dns_lookup_ns: Failed to send DNS query (%s)\n",
nt_errstr(status)));
return status;
}
DEBUG(4,("ads_dns_lookup_ns: %d records returned in the answer section.\n",
answer_count));
- if ( (nsarray = TALLOC_ARRAY(ctx, struct dns_rr_ns, answer_count)) == NULL ) {
- DEBUG(0,("ads_dns_lookup_ns: talloc() failure for %d char*'s\n",
- answer_count));
- return NT_STATUS_NO_MEMORY;
+ if (answer_count) {
+ if ( (nsarray = TALLOC_ARRAY(ctx, struct dns_rr_ns, answer_count)) == NULL ) {
+ DEBUG(0,("ads_dns_lookup_ns: talloc() failure for %d char*'s\n",
+ answer_count));
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ nsarray = NULL;
}
/* now skip the header */
return NT_STATUS_OK;
}
+/****************************************************************************
+ Store and fetch the AD client sitename.
+****************************************************************************/
+
+#define SITENAME_KEY "AD_SITENAME/DOMAIN/%s"
+
+static char *sitename_key(const char *realm)
+{
+ char *keystr;
+
+ if (asprintf(&keystr, SITENAME_KEY, strupper_static(realm)) == -1) {
+ return NULL;
+ }
+
+ return keystr;
+}
+
+
+/****************************************************************************
+ Store the AD client sitename.
+ We store indefinately as every new CLDAP query will re-write this.
+****************************************************************************/
+
+BOOL sitename_store(const char *realm, const char *sitename)
+{
+ time_t expire;
+ BOOL ret = False;
+ char *key;
+
+ if (!gencache_init()) {
+ return False;
+ }
+
+ if (!realm || (strlen(realm) == 0)) {
+ DEBUG(0,("sitename_store: no realm\n"));
+ return False;
+ }
+
+ key = sitename_key(realm);
+
+ if (!sitename || (sitename && !*sitename)) {
+ DEBUG(5,("sitename_store: deleting empty sitename!\n"));
+ ret = gencache_del(key);
+ SAFE_FREE(key);
+ return ret;
+ }
+
+ expire = get_time_t_max(); /* Store indefinately. */
+
+ DEBUG(10,("sitename_store: realm = [%s], sitename = [%s], expire = [%u]\n",
+ realm, sitename, (unsigned int)expire ));
+
+ ret = gencache_set( key, sitename, expire );
+ SAFE_FREE(key);
+ return ret;
+}
+
+/****************************************************************************
+ Fetch the AD client sitename.
+ Caller must free.
+****************************************************************************/
+
+char *sitename_fetch(const char *realm)
+{
+ char *sitename = NULL;
+ time_t timeout;
+ BOOL ret = False;
+ const char *query_realm;
+ char *key;
+
+ if (!gencache_init()) {
+ return False;
+ }
+
+ if (!realm || (strlen(realm) == 0)) {
+ query_realm = lp_realm();
+ } else {
+ query_realm = realm;
+ }
+
+ key = sitename_key(query_realm);
+
+ ret = gencache_get( key, &sitename, &timeout );
+ SAFE_FREE(key);
+ if ( !ret ) {
+ DEBUG(5,("sitename_fetch: No stored sitename for %s\n",
+ query_realm));
+ } else {
+ DEBUG(5,("sitename_fetch: Returning sitename for %s: \"%s\"\n",
+ query_realm, sitename ));
+ }
+ return sitename;
+}
+
+/****************************************************************************
+ Did the sitename change ?
+****************************************************************************/
+
+BOOL stored_sitename_changed(const char *realm, const char *sitename)
+{
+ BOOL ret = False;
+
+ char *new_sitename;
+
+ if (!realm || (strlen(realm) == 0)) {
+ DEBUG(0,("stored_sitename_changed: no realm\n"));
+ return False;
+ }
+
+ new_sitename = sitename_fetch(realm);
+
+ if (sitename && new_sitename && !strequal(sitename, new_sitename)) {
+ ret = True;
+ } else if ((sitename && !new_sitename) ||
+ (!sitename && new_sitename)) {
+ ret = True;
+ }
+ SAFE_FREE(new_sitename);
+ return ret;
+}
+
+/********************************************************************
+ Query with optional sitename.
+********************************************************************/
+
+NTSTATUS ads_dns_query_internal(TALLOC_CTX *ctx,
+ const char *servicename,
+ const char *realm,
+ const char *sitename,
+ struct dns_rr_srv **dclist,
+ int *numdcs )
+{
+ char *name;
+ if (sitename) {
+ name = talloc_asprintf(ctx, "%s._tcp.%s._sites.dc._msdcs.%s",
+ servicename, sitename, realm );
+ } else {
+ name = talloc_asprintf(ctx, "%s._tcp.dc._msdcs.%s",
+ servicename, realm );
+ }
+ if (!name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return ads_dns_lookup_srv( ctx, name, dclist, numdcs );
+}
/********************************************************************
+ Query for AD DC's.
********************************************************************/
-NTSTATUS ads_dns_query_dcs( TALLOC_CTX *ctx, const char *domain, struct dns_rr_srv **dclist, int *numdcs )
+NTSTATUS ads_dns_query_dcs(TALLOC_CTX *ctx,
+ const char *realm,
+ const char *sitename,
+ struct dns_rr_srv **dclist,
+ int *numdcs )
{
- pstring name;
+ NTSTATUS status;
- snprintf( name, sizeof(name), "_ldap._tcp.dc._msdcs.%s", domain );
+ status = ads_dns_query_internal(ctx, "_ldap", realm, sitename,
+ dclist, numdcs);
- return ads_dns_lookup_srv( ctx, name, dclist, numdcs );
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_REFUSED)) {
+ return status;
+ }
+
+ if (sitename && !NT_STATUS_IS_OK(status)) {
+ /* Sitename DNS query may have failed. Try without. */
+ status = ads_dns_query_internal(ctx, "_ldap", realm, NULL,
+ dclist, numdcs);
+ }
+ return status;
}
+/********************************************************************
+ Query for AD KDC's.
+ Even if our underlying kerberos libraries are UDP only, this
+ is pretty safe as it's unlikely that a KDC supports TCP and not UDP.
+********************************************************************/
+
+NTSTATUS ads_dns_query_kdcs(TALLOC_CTX *ctx,
+ const char *realm,
+ const char *sitename,
+ struct dns_rr_srv **dclist,
+ int *numdcs )
+{
+ NTSTATUS status;
+
+ status = ads_dns_query_internal(ctx, "_kerberos", realm, sitename,
+ dclist, numdcs);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_REFUSED)) {
+ return status;
+ }
+
+ if (sitename && !NT_STATUS_IS_OK(status)) {
+ /* Sitename DNS query may have failed. Try without. */
+ status = ads_dns_query_internal(ctx, "_kerberos", realm, NULL,
+ dclist, numdcs);
+ }
+ return status;
+}