Blame library/addisco.c

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