Blame fedfs/fedfs-getsrvinfo.c

Packit Bot f49f79
/*
Packit Bot f49f79
 * Copyright 2011 Oracle.  All rights reserved.
Packit Bot f49f79
 *
Packit Bot f49f79
 * This file is part of fedfs-utils.
Packit Bot f49f79
 *
Packit Bot f49f79
 * fedfs-utils is free software; you can redistribute it and/or modify
Packit Bot f49f79
 * it under the terms of the GNU General Public License version 2.0 as
Packit Bot f49f79
 * published by the Free Software Foundation.
Packit Bot f49f79
 *
Packit Bot f49f79
 * fedfs-utils is distributed in the hope that it will be useful, but
Packit Bot f49f79
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Bot f49f79
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Bot f49f79
 * GNU General Public License version 2.0 for more details.
Packit Bot f49f79
 *
Packit Bot f49f79
 * You should have received a copy of the GNU General Public License
Packit Bot f49f79
 * version 2.0 along with fedfs-utils.  If not, see:
Packit Bot f49f79
 *
Packit Bot f49f79
 *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
Packit Bot f49f79
 */
Packit Bot f49f79
Packit Bot f49f79
#ifdef HAVE_CONFIG_H
Packit Bot f49f79
#include <config.h>
Packit Bot f49f79
#endif
Packit Bot f49f79
Packit Bot f49f79
#include <sys/types.h>
Packit Bot f49f79
#include <sys/socket.h>
Packit Bot f49f79
Packit Bot f49f79
#include <stdbool.h>
Packit Bot f49f79
#include <string.h>
Packit Bot f49f79
#include <unistd.h>
Packit Bot f49f79
#include <stdlib.h>
Packit Bot f49f79
#include <stdint.h>
Packit Bot f49f79
Packit Bot f49f79
#include <stdio.h>
Packit Bot f49f79
#include <errno.h>
Packit Bot f49f79
#include <netdb.h>
Packit Bot f49f79
#include <netinet/in.h>
Packit Bot f49f79
#include <arpa/inet.h>
Packit Bot f49f79
#include <arpa/nameser.h>
Packit Bot f49f79
#include <arpa/nameser_compat.h>
Packit Bot f49f79
#include <resolv.h>
Packit Bot f49f79
Packit Bot f49f79
#include "fedfs-getsrvinfo.h"
Packit Bot f49f79
Packit Bot f49f79
/**
Packit Bot f49f79
 * Parsing overlay for DNS query result record header.  Fields are
Packit Bot f49f79
 * in network byte order.
Packit Bot f49f79
 */
Packit Bot f49f79
struct rechdr {
Packit Bot f49f79
	uint16_t		 type;
Packit Bot f49f79
	uint16_t		 class;
Packit Bot f49f79
	uint32_t		 ttl;
Packit Bot f49f79
	uint16_t		 length;
Packit Bot f49f79
} __attribute__((__packed__));
Packit Bot f49f79
Packit Bot f49f79
/**
Packit Bot f49f79
 * Parsing overlay for DNS query result SRV record data.  Fields
Packit Bot f49f79
 * are in network byte order.
Packit Bot f49f79
 */
Packit Bot f49f79
struct srv {
Packit Bot f49f79
	uint16_t		 priority;
Packit Bot f49f79
	uint16_t		 weight;
Packit Bot f49f79
	uint16_t		 port;
Packit Bot f49f79
	unsigned char		*target;
Packit Bot f49f79
} __attribute__((__packed__));
Packit Bot f49f79
Packit Bot f49f79
/**
Packit Bot f49f79
 * Return C string matching error value of "status"
Packit Bot f49f79
 *
Packit Bot f49f79
 * @param status error status returned by getsrvinfo
Packit Bot f49f79
 * @return pointer to static NUL-terminated C string containing error message
Packit Bot f49f79
 */
Packit Bot f49f79
const char *
Packit Bot f49f79
gsi_strerror(int status)
Packit Bot f49f79
{
Packit Bot f49f79
	static char buf[256];
Packit Bot f49f79
Packit Bot f49f79
	switch (status) {
Packit Bot f49f79
	case ESI_SUCCESS:
Packit Bot f49f79
		return "Success";
Packit Bot f49f79
	case ESI_NONAME:
Packit Bot f49f79
		return "Name not found";
Packit Bot f49f79
	case ESI_AGAIN:
Packit Bot f49f79
		return "Temporary failure in name resolution";
Packit Bot f49f79
	case ESI_FAIL:
Packit Bot f49f79
		return "Non-recoverable failure in name resolution";
Packit Bot f49f79
	case ESI_NODATA:
Packit Bot f49f79
		return "No SRV record returned";
Packit Bot f49f79
	case ESI_SERVICE:
Packit Bot f49f79
		return "Service is not available";
Packit Bot f49f79
	case ESI_MEMORY:
Packit Bot f49f79
		return "Memory allocation failure";
Packit Bot f49f79
	case ESI_SYSTEM:
Packit Bot f49f79
		snprintf(buf, sizeof(buf), "System error (%d): %s",
Packit Bot f49f79
				status, strerror(errno));
Packit Bot f49f79
		return buf;
Packit Bot f49f79
	case ESI_PARSE:
Packit Bot f49f79
		return "Failed to parse server response";
Packit Bot f49f79
	}
Packit Bot f49f79
Packit Bot f49f79
	snprintf(buf, sizeof(buf), "Unknown error (%d)", status);
Packit Bot f49f79
	return buf;
Packit Bot f49f79
}
Packit Bot f49f79
Packit Bot f49f79
/**
Packit Bot f49f79
 * Release a list of SRV records allocated by getsrvinfo()
Packit Bot f49f79
 *
Packit Bot f49f79
 * @param si pointer to first element of a list of struct srvinfo
Packit Bot f49f79
 *
Packit Bot f49f79
 */
Packit Bot f49f79
void freesrvinfo(struct srvinfo *si)
Packit Bot f49f79
{
Packit Bot f49f79
	struct srvinfo *tmp;
Packit Bot f49f79
Packit Bot f49f79
	while (si != NULL) {
Packit Bot f49f79
		tmp = si;
Packit Bot f49f79
		si = si->si_next;
Packit Bot f49f79
		free(tmp->si_target);
Packit Bot f49f79
		free(tmp);
Packit Bot f49f79
	}
Packit Bot f49f79
}
Packit Bot f49f79
Packit Bot f49f79
/**
Packit Bot f49f79
 * Ordering predicate for srvinfo lists
Packit Bot f49f79
 *
Packit Bot f49f79
 * @param a a srvinfo list element to compare
Packit Bot f49f79
 * @param b another srvinfo list element to compare
Packit Bot f49f79
 * @return true if "b" should fall later in the list than "a"
Packit Bot f49f79
 *
Packit Bot f49f79
 * See RFC 2782.   The list is ordered as follows:
Packit Bot f49f79
 *
Packit Bot f49f79
 *  o Lowest priority first.
Packit Bot f49f79
 *  o In each priority class, highest weight first.
Packit Bot f49f79
 */
Packit Bot f49f79
static _Bool
Packit Bot f49f79
srvinfo_is_after(const struct srvinfo *a, const struct srvinfo *b)
Packit Bot f49f79
{
Packit Bot f49f79
	if (a->si_priority > b->si_priority)
Packit Bot f49f79
		return true;
Packit Bot f49f79
	if (a->si_priority < b->si_priority)
Packit Bot f49f79
		return false;
Packit Bot f49f79
Packit Bot f49f79
	if (a->si_weight < b->si_weight)
Packit Bot f49f79
		return true;
Packit Bot f49f79
	return false;
Packit Bot f49f79
}
Packit Bot f49f79
Packit Bot f49f79
/**
Packit Bot f49f79
 * Insert a srvinfo element into a list
Packit Bot f49f79
 *
Packit Bot f49f79
 * @param head pointer to head of list of elements
Packit Bot f49f79
 * @param entry new list element to insert
Packit Bot f49f79
 *
Packit Bot f49f79
 */
Packit Bot f49f79
static void
Packit Bot f49f79
insert_srvinfo(struct srvinfo **head, struct srvinfo *entry)
Packit Bot f49f79
{
Packit Bot f49f79
	entry->si_next = *head;
Packit Bot f49f79
	*head = entry;
Packit Bot f49f79
}
Packit Bot f49f79
Packit Bot f49f79
/**
Packit Bot f49f79
 * Insert a srvinfo element into a list, in order
Packit Bot f49f79
 *
Packit Bot f49f79
 * @param head pointer to head of list of elements
Packit Bot f49f79
 * @param entry new list element to insert
Packit Bot f49f79
 *
Packit Bot f49f79
 */
Packit Bot f49f79
static void
Packit Bot f49f79
insert_srvinfo_sorted(struct srvinfo **head, struct srvinfo *entry)
Packit Bot f49f79
{
Packit Bot f49f79
	struct srvinfo *spot, *back;
Packit Bot f49f79
Packit Bot f49f79
	spot = *head;
Packit Bot f49f79
	back = NULL;
Packit Bot f49f79
	while (spot && srvinfo_is_after(spot, entry)) {
Packit Bot f49f79
		back = spot;
Packit Bot f49f79
		spot = spot->si_next;
Packit Bot f49f79
	}
Packit Bot f49f79
Packit Bot f49f79
	if (spot == (*head))
Packit Bot f49f79
		insert_srvinfo(head, entry);
Packit Bot f49f79
	else {
Packit Bot f49f79
		insert_srvinfo(&spot, entry);
Packit Bot f49f79
		/* off the end of the list? */
Packit Bot f49f79
		if (spot == entry)
Packit Bot f49f79
			back->si_next = entry;
Packit Bot f49f79
	}
Packit Bot f49f79
}
Packit Bot f49f79
Packit Bot f49f79
/**
Packit Bot f49f79
 * Retrieve list of SRV records from DNS service
Packit Bot f49f79
 *
Packit Bot f49f79
 * @param srvname NUL-terminated C string containing record type to look up
Packit Bot f49f79
 * @param domainname NUL-terminated C string containing domain name to look up
Packit Bot f49f79
 * @param si OUT: list of SRV record information retrieved
Packit Bot f49f79
 * @return zero on success, or an ESI_ status code describing the error
Packit Bot f49f79
 *
Packit Bot f49f79
 * Caller must free list of records using freesrvinfo().
Packit Bot f49f79
 */
Packit Bot f49f79
int
Packit Bot f49f79
getsrvinfo(const char *srvname, const char *domainname, struct srvinfo **si)
Packit Bot f49f79
{
Packit Bot f49f79
	unsigned char *msg, *eom, *comp_dn;
Packit Bot f49f79
	struct srvinfo *results;
Packit Bot f49f79
	unsigned short count, i;
Packit Bot f49f79
	int status, len;
Packit Bot f49f79
	char *exp_dn;
Packit Bot f49f79
	HEADER *hdr;
Packit Bot f49f79
Packit Bot f49f79
	msg = calloc(1, NS_MAXMSG);
Packit Bot f49f79
	exp_dn = calloc(1, NS_MAXDNAME);
Packit Bot f49f79
	if (msg == NULL || exp_dn == NULL) {
Packit Bot f49f79
		status = ESI_MEMORY;
Packit Bot f49f79
		goto out;
Packit Bot f49f79
	}
Packit Bot f49f79
Packit Bot f49f79
	_res.options |= RES_AAONLY;
Packit Bot f49f79
	len = res_querydomain(srvname, domainname, C_IN, T_SRV, msg, NS_MAXMSG);
Packit Bot f49f79
	if (len == -1) {
Packit Bot f49f79
		switch (h_errno) {
Packit Bot f49f79
		case HOST_NOT_FOUND:
Packit Bot f49f79
			status = ESI_NONAME;
Packit Bot f49f79
			break;
Packit Bot f49f79
		case TRY_AGAIN:
Packit Bot f49f79
			status = ESI_AGAIN;
Packit Bot f49f79
			break;
Packit Bot f49f79
		case NO_RECOVERY:
Packit Bot f49f79
			status = ESI_FAIL;
Packit Bot f49f79
			break;
Packit Bot f49f79
		case NO_DATA:
Packit Bot f49f79
			status = ESI_NODATA;
Packit Bot f49f79
			break;
Packit Bot f49f79
		default:
Packit Bot f49f79
			fprintf(stderr, "SRV query failed for %s.%s: %s\n",
Packit Bot f49f79
				srvname, domainname, hstrerror(h_errno));
Packit Bot f49f79
			status = ESI_FAIL;
Packit Bot f49f79
		}
Packit Bot f49f79
		goto out;
Packit Bot f49f79
	}
Packit Bot f49f79
Packit Bot f49f79
	hdr = (HEADER *)msg;
Packit Bot f49f79
	count = ntohs(hdr->ancount);
Packit Bot f49f79
	if (count == 0) {
Packit Bot f49f79
		status = ESI_NODATA;
Packit Bot f49f79
		goto out;
Packit Bot f49f79
	}
Packit Bot f49f79
	eom = msg + len;
Packit Bot f49f79
Packit Bot f49f79
	comp_dn = &msg[HFIXEDSZ];
Packit Bot f49f79
	comp_dn += dn_skipname(comp_dn, eom) + QFIXEDSZ;
Packit Bot f49f79
Packit Bot f49f79
	results = NULL;
Packit Bot f49f79
	for (i = 0; i < count; i++) {
Packit Bot f49f79
		struct srvinfo *new;
Packit Bot f49f79
		struct srv *record;
Packit Bot f49f79
		int l;
Packit Bot f49f79
Packit Bot f49f79
		l = dn_expand(msg, eom, comp_dn, exp_dn, NS_MAXDNAME);
Packit Bot f49f79
		if (l == -1) {
Packit Bot f49f79
			status = ESI_PARSE;
Packit Bot f49f79
			goto out_free;
Packit Bot f49f79
		}
Packit Bot f49f79
		comp_dn += l;
Packit Bot f49f79
Packit Bot f49f79
		record = (struct srv *)&comp_dn[10];
Packit Bot f49f79
		comp_dn += 16;
Packit Bot f49f79
Packit Bot f49f79
		l = dn_expand(msg, eom, comp_dn, exp_dn, NS_MAXDNAME);
Packit Bot f49f79
		if (l == -1) {
Packit Bot f49f79
			status = ESI_PARSE;
Packit Bot f49f79
			goto out_free;
Packit Bot f49f79
		}
Packit Bot f49f79
		comp_dn += l;
Packit Bot f49f79
Packit Bot f49f79
		if (count == 1 && strcmp(exp_dn, ".") == 0) {
Packit Bot f49f79
			status = ESI_SERVICE;
Packit Bot f49f79
			goto out_free;
Packit Bot f49f79
		}
Packit Bot f49f79
Packit Bot f49f79
		new = calloc(1, sizeof(*new));
Packit Bot f49f79
		if (new == NULL) {
Packit Bot f49f79
			status = ESI_MEMORY;
Packit Bot f49f79
			goto out;
Packit Bot f49f79
		}
Packit Bot f49f79
Packit Bot f49f79
		new->si_target = strdup(exp_dn);
Packit Bot f49f79
		if (new->si_target == NULL) {
Packit Bot f49f79
			free(new);
Packit Bot f49f79
			status = ESI_MEMORY;
Packit Bot f49f79
			goto out;
Packit Bot f49f79
		}
Packit Bot f49f79
		new->si_priority = ntohs(record->priority);
Packit Bot f49f79
		new->si_weight = ntohs(record->weight);
Packit Bot f49f79
		new->si_port = ntohs(record->port);
Packit Bot f49f79
Packit Bot f49f79
		insert_srvinfo_sorted(&results, new);
Packit Bot f49f79
	}
Packit Bot f49f79
Packit Bot f49f79
	status = ESI_SUCCESS;
Packit Bot f49f79
	*si = results;
Packit Bot f49f79
Packit Bot f49f79
out:
Packit Bot f49f79
	free(exp_dn);
Packit Bot f49f79
	free(msg);
Packit Bot f49f79
	return status;
Packit Bot f49f79
Packit Bot f49f79
out_free:
Packit Bot f49f79
	freesrvinfo(results);
Packit Bot f49f79
	goto out;
Packit Bot f49f79
}