Blame library/addisco.c

Packit 8586cb
/*
Packit 8586cb
 * adcli
Packit 8586cb
 *
Packit 8586cb
 * Copyright (C) 2012 Red Hat Inc.
Packit 8586cb
 *
Packit 8586cb
 * This program is free software; you can redistribute it and/or modify
Packit 8586cb
 * it under the terms of the GNU Lesser General Public License as
Packit 8586cb
 * published by the Free Software Foundation; either version 2.1 of
Packit 8586cb
 * the License, or (at your option) any later version.
Packit 8586cb
 *
Packit 8586cb
 * This program is distributed in the hope that it will be useful, but
Packit 8586cb
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8586cb
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 8586cb
 * Lesser General Public License for more details.
Packit 8586cb
 *
Packit 8586cb
 * You should have received a copy of the GNU Lesser General Public
Packit 8586cb
 * License along with this program; if not, write to the Free Software
Packit 8586cb
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
Packit 8586cb
 * MA 02110-1301 USA
Packit 8586cb
 *
Packit 8586cb
 * Author: Stef Walter <stefw@gnome.org>
Packit 8586cb
 */
Packit 8586cb
Packit 8586cb
#include "config.h"
Packit 8586cb
Packit 8586cb
#include "adcli.h"
Packit 8586cb
#include "adprivate.h"
Packit 8586cb
#include "addisco.h"
Packit 8586cb
Packit 8586cb
#include <sys/types.h>
Packit 8586cb
#include <sys/socket.h>
Packit 8586cb
Packit 8586cb
#include <arpa/inet.h>
Packit 8586cb
#include <arpa/nameser.h>
Packit 8586cb
Packit 8586cb
#include <assert.h>
Packit 8586cb
#include <netdb.h>
Packit 8586cb
#include <resolv.h>
Packit 8586cb
#include <stdio.h>
Packit 8586cb
#include <stdlib.h>
Packit 8586cb
#include <string.h>
Packit 8586cb
#include <time.h>
Packit 8586cb
Packit 8586cb
/* Number of servers to do discovery against */
Packit 8586cb
#define DISCO_COUNT 5
Packit 8586cb
Packit 8586cb
/* The time period in which to do rapid requests */
Packit 8586cb
#define DISCO_FEVER  1
Packit 8586cb
Packit 8586cb
/* Discovery timeout in seconds */
Packit 8586cb
#define DISCO_TIME  15
Packit 8586cb
Packit 8586cb
/* Type of LDAP to use for discovery */
Packit 8586cb
#define DISCO_SCHEME "cldap"
Packit 8586cb
Packit 8586cb
typedef struct _srvinfo {
Packit 8586cb
	unsigned short priority;
Packit 8586cb
	unsigned short weight;
Packit 8586cb
	unsigned short port;
Packit 8586cb
	char *hostname;
Packit 8586cb
	struct _srvinfo *next;
Packit 8586cb
} srvinfo;
Packit 8586cb
Packit 8586cb
static void
Packit 8586cb
freesrvinfo (srvinfo *res)
Packit 8586cb
{
Packit 8586cb
	srvinfo *next;
Packit 8586cb
Packit 8586cb
	while (res != NULL) {
Packit 8586cb
		next = res->next;
Packit 8586cb
		free (res->hostname);
Packit 8586cb
		free (res);
Packit 8586cb
		res = next;
Packit 8586cb
	}
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static int
Packit 8586cb
perform_query (const char *rrname,
Packit 8586cb
               unsigned char **answer,
Packit 8586cb
               int *length)
Packit 8586cb
{
Packit 8586cb
	unsigned char *ans = NULL;
Packit 8586cb
	unsigned char *mem;
Packit 8586cb
	int len = 512;
Packit 8586cb
	int herr;
Packit 8586cb
	int ret;
Packit 8586cb
Packit 8586cb
	for (;;) {
Packit 8586cb
		len *= 2;
Packit 8586cb
		mem = realloc (ans, len);
Packit 8586cb
		if (mem == NULL) {
Packit 8586cb
			free (ans);
Packit 8586cb
			return EAI_MEMORY;
Packit 8586cb
		}
Packit 8586cb
Packit 8586cb
		ans = mem;
Packit 8586cb
		ret = res_query (rrname, C_IN, T_SRV, ans, len);
Packit 8586cb
Packit 8586cb
		/* If answer fit in the buffer then we're done */
Packit 8586cb
		if (ret < 0 || ret < len) {
Packit 8586cb
			len = ret;
Packit 8586cb
			break;
Packit 8586cb
		}
Packit 8586cb
Packit 8586cb
		/*
Packit 8586cb
		 * On overflow some res_query's return the length needed, others
Packit 8586cb
		 * return the full length entered. This code works in either case.
Packit 8586cb
		 */
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	herr = h_errno;
Packit 8586cb
	if (len <= 0) {
Packit 8586cb
		free (ans);
Packit 8586cb
		if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
Packit 8586cb
			return EAI_NONAME;
Packit 8586cb
		else if (herr == TRY_AGAIN)
Packit 8586cb
			return EAI_AGAIN;
Packit 8586cb
		else
Packit 8586cb
			return EAI_FAIL;
Packit 8586cb
	} else {
Packit 8586cb
		*answer = ans;
Packit 8586cb
		*length = len;
Packit 8586cb
		return 0;
Packit 8586cb
	}
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static unsigned short
Packit 8586cb
get_16 (unsigned char **p,
Packit 8586cb
        unsigned char *end)
Packit 8586cb
{
Packit 8586cb
	unsigned short val;
Packit 8586cb
	if (end - (*p) < 2)
Packit 8586cb
		return 0;
Packit 8586cb
	val = ns_get16 (*p);
Packit 8586cb
	(*p) += 2;
Packit 8586cb
	return val;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static unsigned long
Packit 8586cb
get_32 (unsigned char **p,
Packit 8586cb
        unsigned char *end)
Packit 8586cb
{
Packit 8586cb
	unsigned long val;
Packit 8586cb
	if (end - (*p) < 4)
Packit 8586cb
		return 0;
Packit 8586cb
	val = ns_get32 (*p);
Packit 8586cb
	(*p) += 4;
Packit 8586cb
	return val;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static char *
Packit 8586cb
get_string (unsigned char *beg,
Packit 8586cb
            unsigned char *end,
Packit 8586cb
            unsigned char **at)
Packit 8586cb
{
Packit 8586cb
	char buffer[HOST_NAME_MAX];
Packit 8586cb
	int n;
Packit 8586cb
Packit 8586cb
	n = dn_expand (beg, end, *at, buffer, sizeof (buffer));
Packit 8586cb
	if (n < 0)
Packit 8586cb
		return NULL;
Packit 8586cb
Packit 8586cb
	(*at) += n;
Packit 8586cb
	return strdup (buffer);
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static int
Packit 8586cb
parse_record (unsigned char *answer,
Packit 8586cb
              unsigned char *p,
Packit 8586cb
              unsigned char *end,
Packit 8586cb
              srvinfo **res)
Packit 8586cb
{
Packit 8586cb
	srvinfo *srv;
Packit 8586cb
Packit 8586cb
	/* Check that the below calls are sane */
Packit 8586cb
	if (end - p < 8)
Packit 8586cb
		return 0;
Packit 8586cb
Packit 8586cb
	srv = calloc (1, sizeof (srvinfo));
Packit 8586cb
	if (srv == NULL)
Packit 8586cb
		return EAI_MEMORY;
Packit 8586cb
Packit 8586cb
	srv->priority = get_16 (&p, end);
Packit 8586cb
	srv->weight = get_16 (&p, end);
Packit 8586cb
	srv->port = get_16 (&p, end);
Packit 8586cb
	srv->hostname = get_string (answer, end, &p);
Packit 8586cb
Packit 8586cb
	if (!srv->hostname) {
Packit 8586cb
		free (srv);
Packit 8586cb
		return EAI_FAIL;
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	/* This is not perfect RFC 2782 sorting */
Packit 8586cb
Packit 8586cb
	while (*res != NULL) {
Packit 8586cb
		if (srv->priority == (*res)->priority) {
Packit 8586cb
			/* Just sort zero weights first */
Packit 8586cb
			if (!!srv->weight > !!((*res)->weight))
Packit 8586cb
				break;
Packit 8586cb
		} else if (srv->priority > (*res)->priority) {
Packit 8586cb
			break;
Packit 8586cb
		}
Packit 8586cb
		res = &((*res)->next);
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	srv->next = *res;
Packit 8586cb
	*res = srv;
Packit 8586cb
Packit 8586cb
	return 0;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static int
Packit 8586cb
parse_answer (unsigned char *answer,
Packit 8586cb
              int length,
Packit 8586cb
              srvinfo **res)
Packit 8586cb
{
Packit 8586cb
	srvinfo *results = NULL;
Packit 8586cb
	unsigned char *p, *end;
Packit 8586cb
	unsigned short type, qclass, rdlength;
Packit 8586cb
	HEADER *header;
Packit 8586cb
	int count;
Packit 8586cb
	int ret;
Packit 8586cb
	int n;
Packit 8586cb
Packit 8586cb
	header = (HEADER *)answer;
Packit 8586cb
	p = answer + sizeof (HEADER);
Packit 8586cb
	end = answer + length;
Packit 8586cb
Packit 8586cb
	if (p > end)
Packit 8586cb
		return EAI_FAIL;
Packit 8586cb
Packit 8586cb
	/* Skip query */
Packit 8586cb
	count = ntohs (header->qdcount);
Packit 8586cb
	while (count-- && p < end) {
Packit 8586cb
		n = dn_skipname (p, end);
Packit 8586cb
		if (n < 0)
Packit 8586cb
			return EAI_FAIL;
Packit 8586cb
		p += (n + 4);
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	if (count >= 0)
Packit 8586cb
		return EAI_FAIL;
Packit 8586cb
Packit 8586cb
	/* Read answers */
Packit 8586cb
	count = ntohs (header->ancount);
Packit 8586cb
	while (count-- && p < end) {
Packit 8586cb
		n = dn_skipname (p, end);
Packit 8586cb
		if (n < 0 || (end - p) < (n + 10)) {
Packit 8586cb
			freesrvinfo (results);
Packit 8586cb
			return EAI_FAIL;
Packit 8586cb
		}
Packit 8586cb
		p += n;
Packit 8586cb
		type = get_16 (&p, end);
Packit 8586cb
		qclass = get_16 (&p, end);
Packit 8586cb
		get_32 (&p, end); /* skip the ttl */
Packit 8586cb
		rdlength = get_16 (&p, end);
Packit 8586cb
Packit 8586cb
		if (type == T_SRV && qclass == C_IN && (end - p) >=  rdlength) {
Packit 8586cb
			ret = parse_record (answer, p, end, &results);
Packit 8586cb
			if (ret != 0) {
Packit 8586cb
				freesrvinfo (results);
Packit 8586cb
				return ret;
Packit 8586cb
			}
Packit 8586cb
		}
Packit 8586cb
Packit 8586cb
		p += rdlength;
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	/* Note that we allow truncated results by not checking count */
Packit 8586cb
Packit 8586cb
	/* 'A Target of "." means that the service is decidedly not
Packit 8586cb
	 * available at this domain.'
Packit 8586cb
	 */
Packit 8586cb
	if (results == NULL ||
Packit 8586cb
	    (results->next == NULL &&
Packit 8586cb
	     strcmp (results->hostname, ".") == 0)) {
Packit 8586cb
		freesrvinfo (results);
Packit 8586cb
		return EAI_NONAME;
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	*res = results;
Packit 8586cb
	return 0;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static int
Packit 8586cb
getsrvinfo (const char *rrname,
Packit 8586cb
            srvinfo **res)
Packit 8586cb
{
Packit 8586cb
	unsigned char *answer;
Packit 8586cb
	int length;
Packit 8586cb
	int ret;
Packit 8586cb
Packit 8586cb
	ret = perform_query (rrname, &answer, &length);
Packit 8586cb
	if (ret != 0)
Packit 8586cb
		return ret;
Packit 8586cb
Packit 8586cb
	ret = parse_answer (answer, length, res);
Packit 8586cb
	free (answer);
Packit 8586cb
Packit 8586cb
	return ret;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static int
Packit 8586cb
parse_disco_string (unsigned char *beg,
Packit 8586cb
                    unsigned char *end,
Packit 8586cb
                    unsigned char **at,
Packit 8586cb
                    char **result)
Packit 8586cb
{
Packit 8586cb
	char *string;
Packit 8586cb
Packit 8586cb
	assert (result);
Packit 8586cb
Packit 8586cb
	string = get_string (beg, end, at);
Packit 8586cb
	if (string == NULL)
Packit 8586cb
		return 0;
Packit 8586cb
Packit 8586cb
	free (*result);
Packit 8586cb
	*result = string;
Packit 8586cb
Packit 8586cb
	return 1;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static int
Packit 8586cb
get_32_le (unsigned char **at,
Packit 8586cb
           unsigned char *end,
Packit 8586cb
           unsigned int *val)
Packit 8586cb
{
Packit 8586cb
	unsigned char *p = *at;
Packit 8586cb
	if (end - p < 4)
Packit 8586cb
		return 0;
Packit 8586cb
	*val = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
Packit 8586cb
	(*at) += 4;
Packit 8586cb
	return 1;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static int
Packit 8586cb
skip_n (unsigned char **at,
Packit 8586cb
        unsigned char *end,
Packit 8586cb
        int n)
Packit 8586cb
{
Packit 8586cb
	if (end - (*at) < n)
Packit 8586cb
		return 0;
Packit 8586cb
	(*at) += n;
Packit 8586cb
	return 1;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static adcli_disco *
Packit 8586cb
parse_disco_data (struct berval *bv)
Packit 8586cb
{
Packit 8586cb
	unsigned char *at, *end, *beg;
Packit 8586cb
	unsigned int type;
Packit 8586cb
	adcli_disco *disco;
Packit 8586cb
	char *user = NULL;
Packit 8586cb
Packit 8586cb
	beg = (unsigned char *)bv->bv_val;
Packit 8586cb
	end = beg + bv->bv_len;
Packit 8586cb
	at = beg;
Packit 8586cb
Packit 8586cb
	disco = calloc (1, sizeof (adcli_disco));
Packit 8586cb
	return_val_if_fail (disco != NULL, NULL);
Packit 8586cb
Packit 8586cb
	/* domain forest */
Packit 8586cb
	if (!get_32_le (&at, end, &type) || type != 23 ||
Packit 8586cb
	    !get_32_le (&at, end, &disco->flags) ||
Packit 8586cb
	    !skip_n (&at, end, 16) || /* guid */
Packit 8586cb
	    !parse_disco_string (beg, end, &at, &disco->forest) ||
Packit 8586cb
	    !parse_disco_string (beg, end, &at, &disco->domain) ||
Packit 8586cb
	    !parse_disco_string (beg, end, &at, &disco->host_name) ||
Packit 8586cb
	    !parse_disco_string (beg, end, &at, &disco->domain_short) ||
Packit 8586cb
	    !parse_disco_string (beg, end, &at, &disco->host_short) ||
Packit 8586cb
	    !parse_disco_string (beg, end, &at, &user) ||
Packit 8586cb
	    !parse_disco_string (beg, end, &at, &disco->server_site) ||
Packit 8586cb
	    !parse_disco_string (beg, end, &at, &disco->client_site)) {
Packit 8586cb
		_adcli_warn ("Could not parse NetLogon discovery data");
Packit 8586cb
		adcli_disco_free (disco);
Packit 8586cb
		disco = NULL;
Packit 8586cb
	} else {
Packit 8586cb
		_adcli_info ("Received NetLogon info from: %s", disco->host_name);
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	/* We don't care about these */
Packit 8586cb
	free (user);
Packit 8586cb
	return disco;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static int
Packit 8586cb
insert_disco_sorted (adcli_disco **res,
Packit 8586cb
                     adcli_disco *disco,
Packit 8586cb
                     int usability,
Packit 8586cb
                     int unique)
Packit 8586cb
{
Packit 8586cb
	adcli_disco **at = NULL;
Packit 8586cb
Packit 8586cb
	/* Sort in order of usability of this disco record */
Packit 8586cb
	while (*res != NULL) {
Packit 8586cb
		if (unique && strcasecmp (disco->host_name, (*res)->host_name) == 0)
Packit 8586cb
			return 0;
Packit 8586cb
		if (!at && usability > adcli_disco_usable (*res))
Packit 8586cb
			at = res;
Packit 8586cb
		if (at && !unique)
Packit 8586cb
			break;
Packit 8586cb
		res = &((*res)->next);
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	if (at == NULL)
Packit 8586cb
		at = res;
Packit 8586cb
Packit 8586cb
	disco->next = *at;
Packit 8586cb
	*at = disco;
Packit 8586cb
	return 1;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static int
Packit 8586cb
parse_disco (LDAP *ldap,
Packit 8586cb
             const char *host_addr,
Packit 8586cb
             LDAPMessage *message,
Packit 8586cb
             adcli_disco **res)
Packit 8586cb
{
Packit 8586cb
	adcli_disco *disco = NULL;
Packit 8586cb
	LDAPMessage *entry;
Packit 8586cb
	struct berval **bvs;
Packit 8586cb
	int usability;
Packit 8586cb
Packit 8586cb
	entry = ldap_first_entry (ldap, message);
Packit 8586cb
	if (entry != NULL) {
Packit 8586cb
		bvs = ldap_get_values_len (ldap, entry, "NetLogon");
Packit 8586cb
		if (bvs != NULL) {
Packit 8586cb
			if (!bvs[0])
Packit 8586cb
				disco = NULL;
Packit 8586cb
			else
Packit 8586cb
				disco = parse_disco_data (bvs[0]);
Packit 8586cb
			ldap_value_free_len (bvs);
Packit 8586cb
		}
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	if (!disco)
Packit 8586cb
		return ADCLI_DISCO_UNUSABLE;
Packit 8586cb
Packit 8586cb
	disco->host_addr = strdup (host_addr);
Packit 8586cb
	return_val_if_fail (disco, ADCLI_DISCO_UNUSABLE);
Packit 8586cb
Packit 8586cb
	usability = adcli_disco_usable (disco);
Packit 8586cb
	if (!insert_disco_sorted (res, disco, usability, 0))
Packit 8586cb
		assert (0 && "not reached");
Packit 8586cb
	return usability;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static int
Packit 8586cb
ldap_disco (const char *domain,
Packit 8586cb
            srvinfo *srv,
Packit 8586cb
            adcli_disco **results)
Packit 8586cb
{
Packit 8586cb
	char *attrs[] = { "NetLogon", NULL };
Packit 8586cb
	LDAP *ldap[DISCO_COUNT];
Packit 8586cb
	const char *addrs[DISCO_COUNT];
Packit 8586cb
	int found = ADCLI_DISCO_UNUSABLE;
Packit 8586cb
	LDAPMessage *message;
Packit 8586cb
	char buffer[1024];
Packit 8586cb
	struct addrinfo hints;
Packit 8586cb
	struct addrinfo *res, *ai;
Packit 8586cb
	const char *scheme;
Packit 8586cb
	int msgidp;
Packit 8586cb
	int version;
Packit 8586cb
	time_t started;
Packit 8586cb
	time_t now;
Packit 8586cb
	char *url;
Packit 8586cb
	char *filter;
Packit 8586cb
	char *value;
Packit 8586cb
	int num, i;
Packit 8586cb
	int ret;
Packit 8586cb
	int have_any = 0;
Packit 8586cb
Packit 8586cb
	if (domain) {
Packit 8586cb
		value = _adcli_ldap_escape_filter (domain);
Packit 8586cb
		return_val_if_fail (value != NULL, 0);
Packit 8586cb
		if (asprintf (&filter, "(&(DnsDomain=%s)(NtVer=\\06\\00\\00\\00))", value) < 0)
Packit 8586cb
			return_val_if_reached (0);
Packit 8586cb
		free (value);
Packit 8586cb
	} else {
Packit 8586cb
		if (asprintf (&filter, "(&(NtVer=\\06\\00\\00\\00)(AAC=\\00\\00\\00\\00))") < 0)
Packit 8586cb
			return_val_if_reached (0);
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	memset (addrs, 0, sizeof (addrs));
Packit 8586cb
	memset (ldap, 0, sizeof (ldap));
Packit 8586cb
Packit 8586cb
	/* Make sure cldap is supported, it's not always built into openldap */
Packit 8586cb
	if (ldap_is_ldap_url (DISCO_SCHEME "://hostname"))
Packit 8586cb
		scheme = DISCO_SCHEME;
Packit 8586cb
	else
Packit 8586cb
		scheme = "ldap";
Packit 8586cb
Packit 8586cb
	/*
Packit 8586cb
	 * The ai_socktype and ai_protocol hint fields are unused below,
Packit 8586cb
	 * but are set in order to prevent duplicate returns from
Packit 8586cb
	 * getaddrinfo().
Packit 8586cb
	 */
Packit 8586cb
	memset (&hints, 0, sizeof (hints));
Packit 8586cb
	hints.ai_socktype = SOCK_STREAM;
Packit 8586cb
	hints.ai_protocol = IPPROTO_TCP;
Packit 8586cb
	hints.ai_flags |= AI_NUMERICSERV;
Packit 8586cb
#ifdef AI_ADDRCONFIG
Packit 8586cb
	hints.ai_flags |= AI_ADDRCONFIG;
Packit 8586cb
#endif
Packit 8586cb
Packit 8586cb
	for (num = 0; srv != NULL; srv = srv->next) {
Packit 8586cb
		ret = getaddrinfo (srv->hostname, "389", &hints, &res;;
Packit 8586cb
		if (ret != 0) {
Packit 8586cb
			_adcli_warn ("Couldn't resolve server host: %s: %s",
Packit 8586cb
			             srv->hostname, gai_strerror (ret));
Packit 8586cb
			continue;
Packit 8586cb
		}
Packit 8586cb
Packit 8586cb
		for (ai = res; num < DISCO_COUNT && ai != NULL; ai  = ai->ai_next) {
Packit 8586cb
			if (getnameinfo (ai->ai_addr, ai->ai_addrlen, buffer, sizeof (buffer),
Packit 8586cb
			                 NULL, 0, NI_NUMERICHOST) != 0)
Packit 8586cb
				return_val_if_reached (0);
Packit 8586cb
			if (ai->ai_family == AF_INET6) {
Packit 8586cb
				/*
Packit 8586cb
				 * Currently openldap has cldap bugs when used with IPv6:
Packit 8586cb
				 * http://www.openldap.org/its/index.cgi/Incoming?id=7694
Packit 8586cb
				 */
Packit 8586cb
				if (asprintf (&url, "%s://[%s]", "ldap", buffer) < 0)
Packit 8586cb
					return_val_if_reached (0);
Packit 8586cb
			} else {
Packit 8586cb
				if (asprintf (&url, "%s://%s", scheme, buffer) < 0)
Packit 8586cb
					return_val_if_reached (0);
Packit 8586cb
			}
Packit 8586cb
Packit 8586cb
			ret = ldap_initialize (&ldap[num], url);
Packit 8586cb
			if (ret == LDAP_SUCCESS) {
Packit 8586cb
				version = LDAP_VERSION3;
Packit 8586cb
				ldap_set_option (ldap[num], LDAP_OPT_PROTOCOL_VERSION, &version);
Packit 8586cb
				ldap_set_option (ldap[num], LDAP_OPT_REFERRALS , 0);
Packit 8586cb
				_adcli_info ("Sending netlogon pings to domain controller: %s", url);
Packit 8586cb
				addrs[num] = srv->hostname;
Packit 8586cb
				have_any = 1;
Packit 8586cb
				num++;
Packit 8586cb
Packit 8586cb
			} else {
Packit 8586cb
				_adcli_err ("Couldn't perform discovery on server: %s: %s", url, ldap_err2string (ret));
Packit 8586cb
			}
Packit 8586cb
Packit 8586cb
			free (url);
Packit 8586cb
		}
Packit 8586cb
Packit 8586cb
		freeaddrinfo (res);
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	/* Wait for the first response. Poor mans fd watch */
Packit 8586cb
	for (started = now = time (NULL);
Packit 8586cb
	     have_any && found != ADCLI_DISCO_USABLE && now < started + DISCO_TIME;
Packit 8586cb
	     now = time (NULL)) {
Packit 8586cb
Packit 8586cb
		struct timeval tvpoll = { 0, 0 };
Packit 8586cb
		struct timeval interval;
Packit 8586cb
Packit 8586cb
		/* If in the initial period, send feverishly */
Packit 8586cb
		if (now < started + DISCO_FEVER) {
Packit 8586cb
			interval.tv_sec = 0;
Packit 8586cb
			interval.tv_usec = 100 * 1000;
Packit 8586cb
		} else {
Packit 8586cb
			interval.tv_sec = 1;
Packit 8586cb
			interval.tv_usec = 0;
Packit 8586cb
		}
Packit 8586cb
Packit 8586cb
		select (0, NULL, NULL, NULL, &interval);
Packit 8586cb
Packit 8586cb
		have_any = 0;
Packit 8586cb
		for (i = 0; found != ADCLI_DISCO_USABLE && i < num; i++) {
Packit 8586cb
			int close_ldap;
Packit 8586cb
			int parsed;
Packit 8586cb
Packit 8586cb
			if (ldap[i] == NULL)
Packit 8586cb
				continue;
Packit 8586cb
Packit 8586cb
			ret = 0;
Packit 8586cb
			have_any = 1;
Packit 8586cb
			switch (ldap_result (ldap[i], LDAP_RES_ANY, 1, &tvpoll, &message)) {
Packit 8586cb
			case LDAP_RES_SEARCH_ENTRY:
Packit 8586cb
			case LDAP_RES_SEARCH_RESULT:
Packit 8586cb
				parsed = parse_disco (ldap[i], addrs[i], message, results);
Packit 8586cb
				if (parsed > found)
Packit 8586cb
					found = parsed;
Packit 8586cb
				ldap_msgfree (message);
Packit 8586cb
				close_ldap = 1;
Packit 8586cb
				break;
Packit 8586cb
			case 0:
Packit 8586cb
				ret = ldap_search_ext (ldap[i], "", LDAP_SCOPE_BASE,
Packit 8586cb
				                       filter, attrs, 0, NULL, NULL, NULL,
Packit 8586cb
				                       -1, &msgidp);
Packit 8586cb
				close_ldap = (ret != 0);
Packit 8586cb
				break;
Packit 8586cb
			case -1:
Packit 8586cb
				ldap_get_option (ldap[i], LDAP_OPT_RESULT_CODE, &ret;;
Packit 8586cb
				close_ldap = 1;
Packit 8586cb
				break;
Packit 8586cb
			default:
Packit 8586cb
				ldap_msgfree (message);
Packit 8586cb
				close_ldap = 0;
Packit 8586cb
				break;
Packit 8586cb
			}
Packit 8586cb
Packit 8586cb
			if (ret != LDAP_SUCCESS) {
Packit 8586cb
				_adcli_ldap_handle_failure (ldap[i], ADCLI_ERR_CONFIG,
Packit 8586cb
				                            "Couldn't perform discovery search");
Packit 8586cb
			}
Packit 8586cb
Packit 8586cb
			/* Done with this connection */
Packit 8586cb
			if (close_ldap) {
Packit 8586cb
				ldap_unbind_ext_s (ldap[i], NULL, NULL);
Packit 8586cb
				ldap[i] = NULL;
Packit 8586cb
			}
Packit 8586cb
		}
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	for (i = 0; i < num; i++) {
Packit 8586cb
		if (ldap[i] != NULL)
Packit 8586cb
			ldap_unbind_ext_s (ldap[i], NULL, NULL);
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	free (filter);
Packit 8586cb
	return found;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static void
Packit 8586cb
fill_disco (adcli_disco **results,
Packit 8586cb
            int usability,
Packit 8586cb
            const char *domain,
Packit 8586cb
            const char *site,
Packit 8586cb
            srvinfo *srv)
Packit 8586cb
{
Packit 8586cb
	adcli_disco *disco;
Packit 8586cb
Packit 8586cb
	while (srv != NULL) {
Packit 8586cb
		disco = calloc (1, sizeof (adcli_disco));
Packit 8586cb
		return_if_fail (disco != NULL);
Packit 8586cb
		disco->client_site = site ? strdup (site) : NULL;
Packit 8586cb
		disco->server_site = site ? strdup (site) : NULL;
Packit 8586cb
		disco->domain = strdup (domain);
Packit 8586cb
		disco->host_name = strdup (srv->hostname);
Packit 8586cb
		disco->host_addr = strdup (srv->hostname);
Packit 8586cb
		if (!insert_disco_sorted (results, disco, usability, 1))
Packit 8586cb
			adcli_disco_free (disco);
Packit 8586cb
		srv = srv->next;
Packit 8586cb
	}
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
static int
Packit 8586cb
site_disco (adcli_disco *disco,
Packit 8586cb
            adcli_disco **results)
Packit 8586cb
{
Packit 8586cb
	srvinfo *srv;
Packit 8586cb
	char *rrname;
Packit 8586cb
	int found;
Packit 8586cb
	int ret;
Packit 8586cb
Packit 8586cb
	if (!disco->client_site || !disco->domain)
Packit 8586cb
		return ADCLI_DISCO_MAYBE;
Packit 8586cb
Packit 8586cb
	if (asprintf (&rrname, "_ldap._tcp.%s._sites.dc._msdcs.%s",
Packit 8586cb
	              disco->client_site, disco->domain) < 0)
Packit 8586cb
		return_val_if_reached (ADCLI_DISCO_UNUSABLE);
Packit 8586cb
Packit 8586cb
	_adcli_info ("Discovering site domain controllers: %s", rrname);
Packit 8586cb
Packit 8586cb
	ret = getsrvinfo (rrname, &srv;;
Packit 8586cb
	switch (ret) {
Packit 8586cb
	case 0:
Packit 8586cb
		break;
Packit 8586cb
Packit 8586cb
	case EAI_NONAME:
Packit 8586cb
	case EAI_AGAIN:
Packit 8586cb
		_adcli_err ("No LDAP SRV site records: %s: %s",
Packit 8586cb
		            rrname, gai_strerror (ret));
Packit 8586cb
		break;
Packit 8586cb
Packit 8586cb
	default:
Packit 8586cb
		_adcli_err ("Couldn't resolve SRV site records: %s: %s",
Packit 8586cb
		            rrname, gai_strerror (ret));
Packit 8586cb
		break;
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	free (rrname);
Packit 8586cb
Packit 8586cb
	if (ret != 0)
Packit 8586cb
		return ADCLI_DISCO_MAYBE;
Packit 8586cb
Packit 8586cb
	/*
Packit 8586cb
	 * Now that we have discovered the site domain controllers do a
Packit 8586cb
	 * second round of cldap discovery.
Packit 8586cb
	 */
Packit 8586cb
	found = ldap_disco (disco->domain, srv, results);
Packit 8586cb
Packit 8586cb
	fill_disco (results, ADCLI_DISCO_MAYBE,
Packit 8586cb
	            disco->domain, disco->client_site, srv);
Packit 8586cb
Packit 8586cb
	freesrvinfo (srv);
Packit 8586cb
Packit 8586cb
	return found;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
int
Packit 8586cb
adcli_disco_domain (const char *domain,
Packit 8586cb
                    adcli_disco **results)
Packit 8586cb
{
Packit 8586cb
	char *rrname;
Packit 8586cb
	srvinfo *srv;
Packit 8586cb
	int found;
Packit 8586cb
	int ret;
Packit 8586cb
Packit 8586cb
	return_unexpected_if_fail (domain != NULL);
Packit 8586cb
	return_unexpected_if_fail (results != NULL);
Packit 8586cb
Packit 8586cb
	*results = NULL;
Packit 8586cb
Packit 8586cb
	if (asprintf (&rrname, "_ldap._tcp.%s", domain) < 0)
Packit 8586cb
		return_unexpected_if_reached ();
Packit 8586cb
Packit 8586cb
	_adcli_info ("Discovering domain controllers: %s", rrname);
Packit 8586cb
Packit 8586cb
	ret = getsrvinfo (rrname, &srv;;
Packit 8586cb
	switch (ret) {
Packit 8586cb
	case 0:
Packit 8586cb
		break;
Packit 8586cb
Packit 8586cb
	case EAI_NONAME:
Packit 8586cb
	case EAI_AGAIN:
Packit 8586cb
		_adcli_err ("No LDAP SRV records for domain: %s: %s",
Packit 8586cb
		            rrname, gai_strerror (ret));
Packit 8586cb
		break;
Packit 8586cb
Packit 8586cb
	default:
Packit 8586cb
		_adcli_err ("Couldn't resolve SRV record: %s: %s",
Packit 8586cb
		            rrname, gai_strerror (ret));
Packit 8586cb
		break;
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	free (rrname);
Packit 8586cb
	if (ret != 0)
Packit 8586cb
		return 0;
Packit 8586cb
Packit 8586cb
	found = ldap_disco (domain, srv, results);
Packit 8586cb
	if (found == ADCLI_DISCO_MAYBE) {
Packit 8586cb
		assert (*results);
Packit 8586cb
		found = site_disco (*results, results);
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	fill_disco (results, ADCLI_DISCO_MAYBE, domain, NULL, srv);
Packit 8586cb
	freesrvinfo (srv);
Packit 8586cb
Packit 8586cb
	return found;
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
int
Packit 8586cb
adcli_disco_host (const char *host,
Packit 8586cb
                  adcli_disco **results)
Packit 8586cb
{
Packit 8586cb
	srvinfo srv;
Packit 8586cb
Packit 8586cb
	return_val_if_fail (host != NULL, 0);
Packit 8586cb
	return_val_if_fail (results != NULL, 0);
Packit 8586cb
Packit 8586cb
	*results = NULL;
Packit 8586cb
Packit 8586cb
	memset (&srv, 0, sizeof (srv));
Packit 8586cb
	srv.hostname = (char *)host;
Packit 8586cb
Packit 8586cb
	return ldap_disco (NULL, &srv, results);
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
void
Packit 8586cb
adcli_disco_free (adcli_disco *disco)
Packit 8586cb
{
Packit 8586cb
	adcli_disco *next;
Packit 8586cb
Packit 8586cb
	for (; disco != NULL; disco = next) {
Packit 8586cb
		next = disco->next;
Packit 8586cb
		free (disco->host_addr);
Packit 8586cb
		free (disco->host_name);
Packit 8586cb
		free (disco->host_short);
Packit 8586cb
		free (disco->forest);
Packit 8586cb
		free (disco->domain);
Packit 8586cb
		free (disco->domain_short);
Packit 8586cb
		free (disco->client_site);
Packit 8586cb
		free (disco->server_site);
Packit 8586cb
		free (disco);
Packit 8586cb
	}
Packit 8586cb
}
Packit 8586cb
Packit 8586cb
int
Packit 8586cb
adcli_disco_usable (adcli_disco *disco)
Packit 8586cb
{
Packit 8586cb
	return_val_if_fail (disco != NULL, ADCLI_DISCO_UNUSABLE);
Packit 8586cb
Packit 8586cb
	if (disco->flags != 0) {
Packit 8586cb
		if ((disco->flags & (ADCLI_DISCO_KDC | ADCLI_DISCO_LDAP | ADCLI_DISCO_WRITABLE)) == 0)
Packit 8586cb
			return ADCLI_DISCO_UNUSABLE;
Packit 8586cb
	}
Packit 8586cb
Packit 8586cb
	if (disco->client_site && disco->server_site &&
Packit 8586cb
	    strcasecmp (disco->client_site, disco->server_site) == 0)
Packit 8586cb
		return ADCLI_DISCO_USABLE;
Packit 8586cb
Packit 8586cb
	return ADCLI_DISCO_MAYBE;
Packit 8586cb
}