blob: fa60e04fb28c0142ded9f70283054d025834f239 [file] [log] [blame]
/*
* DNS Resolver Module User-space Helper for AFSDB records
*
* Copyright (C) Wang Lei (wang840925@gmail.com) 2010
* Authors: Wang Lei (wang840925@gmail.com)
*
* Copyright (C) David Howells (dhowells@redhat.com) 2018
*
* This is a userspace tool for querying AFSDB RR records in the DNS on behalf
* of the kernel, and converting the VL server addresses to IPv4 format so that
* they can be used by the kAFS filesystem.
*
* As some function like res_init() should use the static library, which is a
* bug of libresolv, that is the reason for cifs.upcall to reimplement.
*
* To use this program, you must tell /sbin/request-key how to invoke it. You
* need to have the keyutils package installed and something like the following
* lines added to your /etc/request-key.conf file:
*
* #OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ...
* ====== ============ =========== ============ ==========================
* create dns_resolver afsdb:* * /sbin/key.dns_resolver %k
*
*
* 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
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "key.dns.h"
static unsigned long afs_ttl = ULONG_MAX;
/*
*
*/
static void afsdb_hosts_to_addrs(ns_msg handle, ns_sect section)
{
char *vllist[MAX_VLS]; /* list of name servers */
int vlsnum = 0; /* number of name servers in list */
int rrnum;
ns_rr rr;
int subtype, i, ret;
unsigned int ttl = UINT_MAX, rr_ttl;
debug("AFSDB RR count is %d", ns_msg_count(handle, section));
/* Look at all the resource records in this section. */
for (rrnum = 0; rrnum < ns_msg_count(handle, section); rrnum++) {
/* Expand the resource record number rrnum into rr. */
if (ns_parserr(&handle, section, rrnum, &rr)) {
_error("ns_parserr failed : %m");
continue;
}
/* We're only interested in AFSDB records */
if (ns_rr_type(rr) == ns_t_afsdb) {
vllist[vlsnum] = malloc(MAXDNAME);
if (!vllist[vlsnum])
error("Out of memory");
subtype = ns_get16(ns_rr_rdata(rr));
/* Expand the name server's domain name */
if (ns_name_uncompress(ns_msg_base(handle),
ns_msg_end(handle),
ns_rr_rdata(rr) + 2,
vllist[vlsnum],
MAXDNAME) < 0)
error("ns_name_uncompress failed");
rr_ttl = ns_rr_ttl(rr);
if (ttl > rr_ttl)
ttl = rr_ttl;
/* Check the domain name we've just unpacked and add it to
* the list of VL servers if it is not a duplicate.
* If it is a duplicate, just ignore it.
*/
for (i = 0; i < vlsnum; i++)
if (strcasecmp(vllist[i], vllist[vlsnum]) == 0)
goto next_one;
/* Turn the hostname into IP addresses */
ret = dns_resolver(vllist[vlsnum], NULL);
if (ret) {
debug("AFSDB RR can't resolve."
"subtype:%d, server name:%s, netmask:%u",
subtype, vllist[vlsnum], mask);
goto next_one;
}
info("AFSDB RR subtype:%d, server name:%s, ip:%*.*s, ttl:%u",
subtype, vllist[vlsnum],
(int)payload[payload_index - 1].iov_len,
(int)payload[payload_index - 1].iov_len,
(char *)payload[payload_index - 1].iov_base,
ttl);
/* prepare for the next record */
vlsnum++;
continue;
next_one:
free(vllist[vlsnum]);
}
}
afs_ttl = ttl;
info("ttl: %u", ttl);
}
/*
*
*/
static void srv_hosts_to_addrs(ns_msg handle, ns_sect section)
{
char *vllist[MAX_VLS]; /* list of name servers */
int vlsnum = 0; /* number of name servers in list */
int rrnum;
ns_rr rr;
int subtype, i, ret;
unsigned short pref, weight, port;
unsigned int ttl = UINT_MAX, rr_ttl;
char sport[8];
debug("SRV RR count is %d", ns_msg_count(handle, section));
/* Look at all the resource records in this section. */
for (rrnum = 0; rrnum < ns_msg_count(handle, section); rrnum++) {
/* Expand the resource record number rrnum into rr. */
if (ns_parserr(&handle, section, rrnum, &rr)) {
_error("ns_parserr failed : %m");
continue;
}
if (ns_rr_type(rr) == ns_t_srv) {
vllist[vlsnum] = malloc(MAXDNAME);
if (!vllist[vlsnum])
error("Out of memory");
subtype = ns_get16(ns_rr_rdata(rr));
/* Expand the name server's domain name */
if (ns_name_uncompress(ns_msg_base(handle),
ns_msg_end(handle),
ns_rr_rdata(rr) + 6,
vllist[vlsnum],
MAXDNAME) < 0) {
_error("ns_name_uncompress failed");
continue;
}
rr_ttl = ns_rr_ttl(rr);
if (ttl > rr_ttl)
ttl = rr_ttl;
pref = ns_get16(ns_rr_rdata(rr));
weight = ns_get16(ns_rr_rdata(rr) + 2);
port = ns_get16(ns_rr_rdata(rr) + 4);
info("rdata %u %u %u", pref, weight, port);
sprintf(sport, "+%hu", port);
/* Check the domain name we've just unpacked and add it to
* the list of VL servers if it is not a duplicate.
* If it is a duplicate, just ignore it.
*/
for (i = 0; i < vlsnum; i++)
if (strcasecmp(vllist[i], vllist[vlsnum]) == 0)
goto next_one;
/* Turn the hostname into IP addresses */
ret = dns_resolver(vllist[vlsnum], sport);
if (ret) {
debug("SRV RR can't resolve."
"subtype:%d, server name:%s, netmask:%u",
subtype, vllist[vlsnum], mask);
goto next_one;
}
info("SRV RR subtype:%d, server name:%s, ip:%*.*s, ttl:%u",
subtype, vllist[vlsnum],
(int)payload[payload_index - 1].iov_len,
(int)payload[payload_index - 1].iov_len,
(char *)payload[payload_index - 1].iov_base,
ttl);
/* prepare for the next record */
vlsnum++;
continue;
next_one:
free(vllist[vlsnum]);
}
}
afs_ttl = ttl;
info("ttl: %u", ttl);
}
/*
* Look up an AFSDB record to get the VL server addresses.
*/
static int dns_query_AFSDB(const char *cell)
{
int response_len; /* buffer length */
ns_msg handle; /* handle for response message */
union {
HEADER hdr;
u_char buf[NS_PACKETSZ];
} response; /* response buffers */
debug("Get AFSDB RR for cell name:'%s'", cell);
/* query the dns for an AFSDB resource record */
response_len = res_query(cell,
ns_c_in,
ns_t_afsdb,
response.buf,
sizeof(response));
if (response_len < 0) {
/* negative result */
_nsError(h_errno, cell);
return -1;
}
if (ns_initparse(response.buf, response_len, &handle) < 0)
error("ns_initparse: %m");
/* look up the hostnames we've obtained to get the actual addresses */
afsdb_hosts_to_addrs(handle, ns_s_an);
info("DNS query AFSDB RR results:%u ttl:%lu", payload_index, afs_ttl);
return 0;
}
/*
* Look up an SRV record to get the VL server addresses [RFC 5864].
*/
static int dns_query_VL_SRV(const char *cell)
{
int response_len; /* buffer length */
ns_msg handle; /* handle for response message */
union {
HEADER hdr;
u_char buf[NS_PACKETSZ];
} response;
char name[1024];
snprintf(name, sizeof(name), "_afs3-vlserver._udp.%s", cell);
debug("Get VL SRV RR for name:'%s'", name);
response_len = res_query(name,
ns_c_in,
ns_t_srv,
response.buf,
sizeof(response));
if (response_len < 0) {
/* negative result */
_nsError(h_errno, cell);
return -1;
}
if (ns_initparse(response.buf, response_len, &handle) < 0)
error("ns_initparse: %m");
/* look up the hostnames we've obtained to get the actual addresses */
srv_hosts_to_addrs(handle, ns_s_an);
info("DNS query VL SRV RR results:%u ttl:%lu", payload_index, afs_ttl);
return 0;
}
/*
* Instantiate the key.
*/
static __attribute__((noreturn))
void afs_instantiate(const char *cell)
{
int ret;
/* set the key's expiry time from the minimum TTL encountered */
if (!debug_mode) {
ret = keyctl_set_timeout(key, afs_ttl);
if (ret == -1)
error("%s: keyctl_set_timeout: %m", __func__);
}
/* handle a lack of results */
if (payload_index == 0)
nsError(NO_DATA, cell);
/* must include a NUL char at the end of the payload */
payload[payload_index].iov_base = "";
payload[payload_index++].iov_len = 1;
dump_payload();
/* load the key with data key */
if (!debug_mode) {
ret = keyctl_instantiate_iov(key, payload, payload_index, 0);
if (ret == -1)
error("%s: keyctl_instantiate: %m", __func__);
}
exit(0);
}
/*
* Look up VL servers for AFS.
*/
void afs_look_up_VL_servers(const char *cell, char *options)
{
/* Is the IP address family limited? */
if (strcmp(options, "ipv4") == 0)
mask = INET_IP4_ONLY;
else if (strcmp(options, "ipv6") == 0)
mask = INET_IP6_ONLY;
if (dns_query_VL_SRV(cell) != 0)
dns_query_AFSDB(cell);
afs_instantiate(cell);
}