/* * 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 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]); } } key_expiry = ttl; info("ttl: %u", key_expiry); } /* * */ 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]); } } key_expiry = ttl; info("ttl: %u", key_expiry); } /* * 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:%u", payload_index, key_expiry); 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:%u", payload_index, key_expiry); 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, key_expiry); 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); }