aklog-kafs
+kafs-check-config
+kafs-preload
+kafs-dns
+*.o
+*.os
+libkafs_client.so*
CFLAGS = -g -O2 -Wall -Wsign-compare
+MKDIR = mkdir
INSTALL = install
DESTDIR =
-ETCDIR = /etc/kafs
+ETCDIR = /etc
BINDIR = /usr/bin
+LIBEXECDIR = /usr/libexec
MANDIR = /usr/share/man
DATADIR = /usr/share/kafs-client
+UNITDIR = /usr/lib/systemd/system
SPECFILE = redhat/kafs-client.spec
LNS := ln -sf
$(INSTALL) -D -m 0644 man/aklog-kafs.1 $(DESTDIR)$(MAN1)/aklog-kafs.1
$(INSTALL) -D -m 0644 man/aklog.1 $(DESTDIR)$(MAN1)/aklog.1
$(INSTALL) -D -m 0644 conf/cellservdb.conf $(DESTDIR)$(DATADIR)/cellservdb.conf
- $(INSTALL) -D -m 0644 conf/etc.conf $(DESTDIR)$(ETCDIR)/cellservdb.conf
- mkdir -m755 $(DESTDIR)$(ETCDIR)/cellservdb.d
- mkdir -m755 $(DESTDIR)/afs
+ $(INSTALL) -D -m 0644 conf/etc.conf $(DESTDIR)$(ETCDIR)/kafs/cellservdb.conf
+ $(INSTALL) -D -m 0644 conf/kafs_dns.conf $(DESTDIR)$(ETCDIR)/request-key.d/kafs_dns.conf
+ $(INSTALL) -D -m 0644 conf/kafs-config.service $(DESTDIR)$(UNITDIR)/kafs-config.service
+ $(INSTALL) -D -m 0644 conf/afs.mount $(DESTDIR)$(UNITDIR)/afs.mount
+ $(MKDIR) -m755 $(DESTDIR)$(ETCDIR)/kafs/cellservdb.d
+ $(MKDIR) -m755 $(DESTDIR)/afs
###############################################################################
#
[Unit]
Description=kAFS Dynamic Root mount
ConditionPathExists=/afs
+Wants=kafs-config.service
[Mount]
What=none
--- /dev/null
+[Unit]
+Description=Preload AFS Cell Database
+After=local-fs.target
+DefaultDependencies=no
+
+[Service]
+Type=oneshot
+ExecStartPre=/sbin/modprobe -q kafs
+ExecStart=/usr/libexec/kafs-preload
--- /dev/null
+create dns_resolver afsdb:* * /usr/libexec/kafs-dns %k
# % define buildid .local
+%global libapivermajor 0
+%global libapiversion %{libapivermajor}.1
Name: kafs-client
Version: 0.1
# kAFS config files:
# /etc/kafs/cellservdb.conf
#
-%global confdir %{_sysconfdir}/kafs
%global datadir %{_datarootdir}/kafs
Requires: keyutils
# >= 1.5.11
Provide basic AFS-compatible tools for kAFS and systemd scripts to mount the
dynamic root on /afs and preload the cell database.
+%package libs
+Summary: Library of routines for dealing with kAFS
+Requires: %{name}%{?_isa} = %{version}-%{release}
+
+%description libs
+Provide a library of shareable routines for dealing with the kAFS
+filesystem. These provide things like configuration parsing and DNS lookups.
+
+%package libs-devel
+Summary: Library of routines for dealing with kAFS
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+
+%description libs-devel
+Provide a library of shareable routines for dealing with the kAFS
+filesystem. These provide things like configuration parsing and DNS lookups.
+
#
# We generate a compatibility package that makes kafs look like OpenAFS, but it
# needs to be uninstalled be able to install OpenAFS or Auristor.
%build
make all \
- ETCDIR=%{etcdir} \
+ ETCDIR=%{_sysconfdir} \
BINDIR=%{_bindir} \
- MANDIR=%{_mandir} \
+ SBINDIR=%{_sbindir} \
DATADIR=%{datadir} \
+ INCLUDEDIR=%{_includedir} \
+ LIBDIR=%{_libdir} \
+ LIBEXECDIR=%{_libexecdir} \
+ MANDIR=%{_mandir} \
CFLAGS="-Wall -Werror $RPM_OPT_FLAGS $RPM_LD_FLAGS $ARCH_OPT_FLAGS"
%install
mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_unitdir}
mkdir -p %{buildroot}%{_mandir}/man1
-mkdir -p %{buildroot}%{confdir}
-mkdir -p %{buildroot}%{datadir}
+mkdir -p %{buildroot}%{_sysconfdir}
+mkdir -p %{buildroot}%{_datarootdir}
make DESTDIR=%{buildroot} install \
- ETCDIR=%{confdir} \
- DATADIR=%{datadir} \
+ ETCDIR=%{_sysconfdir} \
+ BINDIR=%{_bindir} \
SBINDIR=%{_sbindir} \
+ DATADIR=%{datadir} \
+ INCLUDEDIR=%{_includedir} \
+ LIBDIR=%{_libdir} \
+ LIBEXECDIR=%{_libexecdir} \
MANDIR=%{_mandir} \
- CFLAGS="-Wall $RPM_OPT_FLAGS -Werror"
-
-%{__install} -m 644 conf/afs.mount %{buildroot}%{_unitdir}/afs.mount
-%{__install} -m 644 conf/etc.conf %{buildroot}%{confdir}/cellservdb.conf
+ CFLAGS="-Wall -Werror $RPM_OPT_FLAGS $RPM_LD_FLAGS $ARCH_OPT_FLAGS"
# Compat
ln -s aklog-kafs %{buildroot}/%{_bindir}/aklog
+%ldconfig_scriptlets libs
+
%post
%systemd_post afs.mount
%license LICENCE.GPL
/afs
%{_bindir}/aklog-kafs
+%{_sbindir}/kafs-check-config
%{_unitdir}/*
%{_mandir}/man1/aklog-kafs.1*
-%{confdir}
+%{_libexecdir}/kafs-preload
+%{_libexecdir}/kafs-dns
+%{_sysconfdir}/request-key.d/kafs_dns.conf
+
+%files libs
+%{_libdir}/libkafs_client.so.%{libapiversion}
+%{_libdir}/libkafs_client.so.%{libapivermajor}
%{datadir}
-%config(noreplace) %{confdir}/cellservdb.conf
-%config(noreplace) %{confdir}/cellservdb.d
+%config(noreplace) %{_sysconfdir}/kafs/cellservdb.conf
+%config(noreplace) %{_sysconfdir}/kafs/cellservdb.d
+
+%files libs-devel
+%{_libdir}/libkafs_client.so
+%{_includedir}/*
%files compat
%{_bindir}/aklog
-CFLAGS = -g -O2 -Wall -Wsign-compare
-INSTALL = install
-DESTDIR =
-ETCDIR = /etc/kafs
-BINDIR = /usr/bin
-DATADIR = /usr/share/kafs-client
-SPECFILE = ../redhat/kafs-client.spec
+CFLAGS = -g -Wall -Wsign-compare
-LNS := ln -sf
+include Makefile.config
-###############################################################################
-#
-# Determine the current package version from the specfile
-#
-###############################################################################
-VERSION := $(word 2,$(shell grep "^Version:" $(SPECFILE)))
-CPPFLAGS += -DVERSION="\"$(VERSION)\""
+all: lib progs
+
+CPPFLAGS += -Iinclude -DETCDIR=\"$(ETCDIR)\"
###############################################################################
#
-# Guess at the appropriate word size
+# Build a library to do config parsing and other stuff.
#
###############################################################################
-BUILDFOR := $(shell file /usr/bin/make | sed -e 's!.*ELF \(32\|64\)-bit.*!\1!')-bit
+lib: $(DEVELLIB)
+
+$(DEVELLIB): $(SONAME)
+ $(LNS) $< $@
+
+$(SONAME): $(LIBNAME)
+ $(LNS) $< $@
+
+LIB_HEADERS := $(wildcard include/kafs/*.h)
+LIB_FILES := \
+ lib_cell_lookup.c \
+ lib_cellserv.c \
+ lib_dns_lookup.c \
+ lib_object.c \
+ lib_profile.c
+
+LIBVERS := -shared -Wl,-soname,$(SONAME) -Wl,--version-script,version.lds
+LIB_OBJS := $(patsubst %.c,%.os,$(LIB_FILES))
-ifeq ($(BUILDFOR),32-bit)
-CFLAGS += -m32
-else
-ifeq ($(BUILDFOR),64-bit)
-CFLAGS += -m64
-endif
-endif
+%.os: %.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -o $@ -c $<
+
+$(LIBNAME): $(LIB_OBJS) version.lds Makefile
+ $(CC) $(CFLAGS) -fPIC $(LDFLAGS) $(LIBVERS) -o $@ $(LIB_OBJS) $(LIBLIBS) \
+ -lresolv
+
+$(LIB_OBJS) : $(LIB_HEADERS) Makefile
+
+LIB_DEPENDENCY := $(DEVELLIB)
+LDFLAGS += -L.
+
+#%.o: %.c $(LIB_HEADERS) Makefile
+# $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
###############################################################################
#
# Build stuff
#
###############################################################################
-all: aklog-kafs
+progs: \
+ aklog-kafs \
+ kafs-check-config \
+ kafs-preload \
+ kafs-dns
+
+aklog-kafs: aklog-kafs.o
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lkrb5 -lcrypto -lkeyutils
+
+kafs-check-config: kafs-check-config.o $(DEVELLIB)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ kafs-check-config.o -lkafs_client
+
+kafs-preload: preload-cells.o $(DEVELLIB)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ preload-cells.o -lkafs_client
+
+KAFS_DNS_OBJS := dns_main.o dns_afsdb_text.o dns_afsdb_v1.o
+kafs-dns: $(KAFS_DNS_OBJS) $(DEVELLIB)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(KAFS_DNS_OBJS) \
+ -lkafs_client -lkeyutils
-aklog-kafs: aklog-kafs.c Makefile
- $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< -lkrb5 -lcrypto -lkeyutils
+kafs-check-config.o: $(LIB_HEADERS)
+preload-cells.o: $(LIB_HEADERS)
+$(KAFS_DNS_OBJS): $(LIB_HEADERS)
###############################################################################
#
#
###############################################################################
install: all
+ mkdir -p $(DESTDIR)$(INCLUDEDIR)/kafs
+ $(INSTALL) -D -m 0644 $(LIB_HEADERS) $(DESTDIR)$(INCLUDEDIR)/kafs/
+ $(INSTALL) -D -m 0755 $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(LIBNAME)
+ $(LNS) $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME)
+ $(LNS) $(LIBDIR)/$(SONAME) $(DESTDIR)$(LIBDIR)/$(DEVELLIB)
$(INSTALL) -D -m 0755 aklog-kafs $(DESTDIR)$(BINDIR)/aklog-kafs
+ $(INSTALL) -D -m 0755 kafs-check-config $(DESTDIR)$(SBINDIR)/kafs-check-config
+ $(INSTALL) -D -m 0755 kafs-preload $(DESTDIR)$(LIBEXECDIR)/kafs-preload
+ $(INSTALL) -D -m 0755 kafs-dns $(DESTDIR)$(LIBEXECDIR)/kafs-dns
###############################################################################
#
#
###############################################################################
clean:
- $(RM) aklog-kafs
- $(RM) *.o *~
+ $(RM) aklog-kafs kafs-check-config kafs-preload kafs-dns
+ $(RM) $(DEVELLIB) $(SONAME) $(LIBNAME)
+ $(RM) *.o *~ *.os
distclean: clean
--- /dev/null
+INSTALL = install
+DESTDIR =
+ETCDIR = /etc
+BINDIR = /usr/bin
+LIBEXECDIR = /usr/libexec
+INCLUDEDIR = /usr/include
+DATADIR = /usr/share/kafs-client
+SPECFILE = ../redhat/kafs-client.spec
+
+ifeq ($(origin LIBDIR),undefined)
+LIBDIR := $(shell ldd /usr/bin/make | grep '\(/libc\)' | sed -e 's!.*\(/.*\)/libc[.].*!\1!')
+endif
+
+LNS := ln -sf
+
+###############################################################################
+#
+# Determine the current package version from the specfile
+#
+###############################################################################
+VERSION := $(word 2,$(shell grep "^Version:" $(SPECFILE)))
+CPPFLAGS += -DVERSION="\"$(VERSION)\""
+
+###############################################################################
+#
+# Determine the current library version from the version script
+#
+###############################################################################
+libversion := $(filter KAFS_CLIENT_%,$(shell grep ^KAFS_CLIENT_ version.lds))
+libversion := $(lastword $(libversion))
+libversion := $(lastword $(libversion))
+APIVERSION := $(subst KAFS_CLIENT_,,$(libversion))
+vernumbers := $(subst ., ,$(APIVERSION))
+APIMAJOR := $(firstword $(vernumbers))
+
+DEVELLIB := libkafs_client.so
+SONAME := $(DEVELLIB).$(APIMAJOR)
+LIBNAME := $(DEVELLIB).$(APIVERSION)
+
+###############################################################################
+#
+# Guess at the appropriate word size
+#
+###############################################################################
+BUILDFOR := $(shell file /usr/bin/make | sed -e 's!.*ELF \(32\|64\)-bit.*!\1!')-bit
+
+ifeq ($(BUILDFOR),32-bit)
+CFLAGS += -m32
+else
+ifeq ($(BUILDFOR),64-bit)
+CFLAGS += -m64
+endif
+endif
+
+###############################################################################
+#
+# Guess at the appropriate lib directory and word size
+#
+###############################################################################
+ifeq ($(origin LIBDIR),undefined)
+LIBDIR := $(shell ldd /usr/bin/make | grep '\(/libc\)' | sed -e 's!.*\(/.*\)/libc[.].*!\1!')
+endif
+BUILDFOR := $(shell file /usr/bin/make | sed -e 's!.*ELF \(32\|64\)-bit.*!\1!')-bit
const EVP_MD *algo = EVP_md5(); /* We use HMAC-MD5 */
struct kdf_data kdf_data = rxkad_kdf_data;
-
+
for (i = 1; i <= 255; i++) {
/* K(i) = PRF(Ks, [i]_2 || Label || 0x00 || [L]_2) */
kdf_data.i_2 = i;
for (p = cell; *p; p++)
*p = tolower(*p);
-
+
ret = asprintf(&princ, "afs/%s@%s", cell, realm);
OSERROR(ret, "asprintf");
ret = asprintf(&desc, "afs@%s", cell);
--- /dev/null
+struct kafs_lookup_context;
+
+extern void *kafs_generate_text_payload(void *result,
+ const char *cell_name,
+ unsigned int *_ttl,
+ struct kafs_lookup_context *ctx);
+
+extern void *kafs_generate_v1_payload(void *result,
+ const char *cell_name,
+ unsigned int *_ttl,
+ struct kafs_lookup_context *ctx);
--- /dev/null
+/*
+ * Generate a set of addresses as a text string.
+ *
+ * Copyright (C) David Howells (dhowells@redhat.com) 2018
+ *
+ * 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.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <kafs/cellserv.h>
+#include <arpa/inet.h>
+#include "dns_afsdb.h"
+
+static void store_char(char **_b, char n)
+{
+ *(*_b)++ = n;
+}
+
+static void store_string(char **_b, const char *p)
+{
+ unsigned int len = strlen(p);
+
+ memcpy(*_b, p, len);
+ *_b += len;
+}
+
+/*
+ * Generate the payload to pass to the kernel as v1 server bundle.
+ */
+static void emit_text_str(char **_b,
+ struct kafs_server_list *vls,
+ unsigned short default_port)
+{
+ struct kafs_server_addr *addr;
+ struct kafs_server *server;
+ const char *p;
+ unsigned int i, j;
+ char buf[100];
+ bool need_sep = false;
+
+ for (i = 0; i < vls->nr_servers; i++) {
+ server = &vls->servers[i];
+
+ for (j = 0; j < server->nr_addrs; j++) {
+ addr = &server->addrs[j];
+
+ if (need_sep)
+ store_char(_b, ',');
+ need_sep = true;
+
+ switch (addr->sin.sin_family) {
+ case AF_INET:
+ p = inet_ntop(AF_INET, &addr->sin.sin_addr,
+ buf, sizeof(buf));
+ if (p) {
+ store_char(_b, '[');
+ store_string(_b, buf);
+ store_char(_b, ']');
+ }
+ break;
+ case AF_INET6:
+ p = inet_ntop(AF_INET6, &addr->sin6.sin6_addr,
+ buf, sizeof(buf));
+ if (p) {
+ store_char(_b, '[');
+ store_string(_b, buf);
+ store_char(_b, ']');
+ }
+ break;
+ default:
+ continue;
+ }
+
+ if (server->port && server->port != default_port) {
+ sprintf(buf, "%u", server->port);
+ store_char(_b, '+');
+ store_string(_b, buf);
+ }
+ }
+ }
+
+ store_char(_b, 0);
+}
+
+void *kafs_generate_text_payload(void *result,
+ const char *cell_name,
+ unsigned int *_ttl,
+ struct kafs_lookup_context *ctx)
+{
+ struct kafs_cell *cell;
+ char *b = result;
+
+ ctx->report.what = cell_name;
+ cell = kafs_lookup_cell(cell_name, ctx);
+ if (!cell)
+ return NULL;
+
+ if (cell->vlservers)
+ emit_text_str(&b, cell->vlservers, 7003);
+ return b;
+}
--- /dev/null
+/*
+ * Generate a v1 servers and addresses list.
+ *
+ * Copyright (C) David Howells (dhowells@redhat.com) 2018
+ *
+ * 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.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <kafs/cellserv.h>
+#include "dns_afsdb.h"
+#include "dns_resolver.h"
+
+static void store_u8(unsigned char **_b, unsigned char n)
+{
+ *(*_b)++ = n;
+}
+
+static void store_u16(unsigned char **_b, unsigned short n)
+{
+ *(*_b)++ = (n >> 0) & 0xff;
+ *(*_b)++ = (n >> 8) & 0xff;
+}
+
+static void store_octets(unsigned char **_b, const void *p, size_t n)
+{
+ memcpy(*_b, p, n);
+ *_b += n;
+}
+
+/*
+ * Generate the payload to pass to the kernel as v1 server bundle.
+ */
+static void emit_v1(unsigned char **_b, struct kafs_server_list *vls)
+{
+ struct kafs_server_addr *addr;
+ struct kafs_server *server;
+ unsigned int i, j, n;
+
+ store_u8 (_b, 0); /* It's not a string */
+ store_u8 (_b, DNS_PAYLOAD_IS_SERVER_LIST);
+ store_u8 (_b, 1); /* Encoding version */
+ store_u8 (_b, vls->source);
+ store_u8 (_b, vls->status);
+ store_u8 (_b, vls->nr_servers);
+
+ for (i = 0; i < vls->nr_servers; i++) {
+ server = &vls->servers[i];
+
+ n = strlen(server->name);
+ store_u16(_b, n);
+ store_u16(_b, server->pref);
+ store_u16(_b, server->weight);
+ store_u16(_b, server->port);
+ store_u8 (_b, server->source);
+ store_u8 (_b, server->status);
+ store_u8 (_b, server->protocol);
+ store_u8 (_b, server->nr_addrs);
+ store_octets(_b, server->name, n);
+
+ for (j = 0; j < server->nr_addrs; j++) {
+ addr = &server->addrs[j];
+
+ switch (addr->sin.sin_family) {
+ case AF_INET:
+ store_u8(_b, DNS_ADDRESS_IS_IPV4);
+ store_octets(_b, &addr->sin.sin_addr, 4);
+ break;
+ case AF_INET6:
+ store_u8(_b, DNS_ADDRESS_IS_IPV6);
+ store_octets(_b, &addr->sin6.sin6_addr, 16);
+ break;
+ default:
+ store_u8(_b, 0);
+ continue;
+ }
+ }
+ }
+}
+
+void *kafs_generate_v1_payload(void *result,
+ const char *cell_name,
+ unsigned int *_ttl,
+ struct kafs_lookup_context *ctx)
+{
+ struct kafs_cell *cell;
+ unsigned char *b = result;
+
+ ctx->report.what = cell_name;
+ cell = kafs_lookup_cell(cell_name, ctx);
+ if (!cell)
+ return NULL;
+
+ if (_ttl)
+ *_ttl = cell->vlservers->ttl;
+ if (cell->vlservers)
+ emit_v1(&b, cell->vlservers);
+ return b;
+}
--- /dev/null
+/*
+ * 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 liberary, 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
+ */
+#define _GNU_SOURCE
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <keyutils.h>
+#include <sys/mman.h>
+#include <kafs/cellserv.h>
+#include "dns_afsdb.h"
+
+static const char *DNS_PARSE_VERSION = "2.0";
+static const char prog[] = "dns_afsdb";
+static const char key_type[] = "dns_resolver";
+static const char afsdb_query_type[] = "afsdb:";
+static key_serial_t key;
+static int debug_mode;
+static bool one_addr_only = true;
+static unsigned int output_version = 0;
+
+/*
+ * Print an error to stderr or the syslog, negate the key being created and
+ * exit
+ */
+void error(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ if (isatty(2)) {
+ fputs("E: ", stderr);
+ vfprintf(stderr, fmt, va);
+ fputc('\n', stderr);
+ } else {
+ vsyslog(LOG_ERR, fmt, va);
+ }
+ va_end(va);
+
+ /*
+ * on error, negatively instantiate the key ourselves so that we can
+ * make sure the kernel doesn't hang it off of a searchable keyring
+ * and interfere with the next attempt to instantiate the key.
+ */
+ if (!debug_mode)
+ keyctl_negate(key, 1, KEY_REQKEY_DEFL_DEFAULT);
+
+ exit(1);
+}
+
+#define error(FMT, ...) error("Error: " FMT, ##__VA_ARGS__)
+
+/*
+ * Just print an error to stderr or the syslog
+ */
+static void print_error(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ if (isatty(2)) {
+ fputs("E: ", stderr);
+ vfprintf(stderr, fmt, va);
+ fputc('\n', stderr);
+ } else {
+ vsyslog(LOG_ERR, fmt, va);
+ }
+ va_end(va);
+}
+
+/*
+ * Print status information
+ */
+static void verbose(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ if (isatty(1)) {
+ fputs("I: ", stdout);
+ vfprintf(stdout, fmt, va);
+ fputc('\n', stdout);
+ } else {
+ vsyslog(LOG_INFO, fmt, va);
+ }
+ va_end(va);
+}
+
+/*
+ * Print usage details,
+ */
+static __attribute__((noreturn))
+void usage(void)
+{
+ if (isatty(2)) {
+ fprintf(stderr, "Usage: %s [OPTION]... <key_serial>\n",
+ prog);
+ fprintf(stderr, " %s -D [OPTION]... <desc> <calloutinfo>\n",
+ prog);
+ fprintf(stderr, " %s -V\n",
+ prog);
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Where [OPTION].. is a combination of one or more of:\n");
+ fprintf(stderr, "\t-c <conffile>\n");
+ fprintf(stderr, "\t-N dns\n");
+ fprintf(stderr, "\t-N vls-afsdb\n");
+ fprintf(stderr, "\t-N vls-srv\n");
+ fprintf(stderr, "\t-N vls-all\n");
+ fprintf(stderr, "\t-N vl-host\n");
+ fprintf(stderr, "\t-o <dumpfile>\n");
+ fprintf(stderr, "\t-v\n");
+ } else {
+ verbose("Usage: %s [-vv] <key_serial>", prog);
+ }
+ exit(2);
+}
+
+/*
+ * Parse the callout info string.
+ */
+static void parse_callout(char *options, struct kafs_lookup_context *ctx)
+{
+ char *k, *val;
+
+ ctx->want_ipv4_addrs = true;
+ ctx->want_ipv6_addrs = true;
+
+ if (!*options) {
+ /* legacy mode */
+ ctx->want_ipv6_addrs = false;
+ return;
+ }
+
+ do {
+ k = options;
+ options = strchr(options, ' ');
+ if (!options)
+ options = k + strlen(k);
+ else
+ *options++ = '\0';
+ if (!*k)
+ continue;
+ if (strchr(k, ','))
+ error("Option name '%s' contains a comma", k);
+
+ val = strchr(k, '=');
+ if (val)
+ *val++ = '\0';
+
+ if (ctx->report.verbose)
+ ctx->report.verbose("Opt %s", k);
+
+ if (strcmp(k, "ipv4") == 0) {
+ ctx->want_ipv4_addrs = true;
+ ctx->want_ipv6_addrs = false;
+ } else if (strcmp(k, "ipv6") == 0) {
+ ctx->want_ipv4_addrs = false;
+ ctx->want_ipv6_addrs = true;
+ } else if (strcmp(k, "list") == 0) {
+ one_addr_only = false;
+ } else if (strcmp(k, "srv") == 0) {
+ output_version = atoi(val);
+ }
+ } while (*options);
+}
+
+const struct option long_options[] = {
+ { "conf", 0, NULL, 'c' },
+ { "debug", 0, NULL, 'D' },
+ { "no", 0, NULL, 'N' },
+ { "output", 0, NULL, 'o' },
+ { "verbose", 0, NULL, 'v' },
+ { "version", 0, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+};
+
+/*
+ *
+ */
+int main(int argc, char *argv[])
+{
+ struct kafs_lookup_context ctx = { .report.error = print_error, };
+ const char *dump_file = NULL;
+ const char *filev[10], **filep = NULL;
+ char *keyend, *p;
+ char *callout_info = NULL;
+ char *buf = NULL, *name, *result, *r_end;
+ unsigned int ttl;
+ size_t ktlen;
+ int ret, filec = 0;
+
+ if (argc > 1 && strcmp(argv[1], "--help") == 0)
+ usage();
+
+ openlog(prog, 0, LOG_DAEMON);
+
+ while ((ret = getopt_long(argc, argv, "Dvc:N:o:V:", long_options, NULL)) != -1) {
+ switch (ret) {
+ case 'c':
+ if (filec >= 9) {
+ fprintf(stderr, "Max 9 files\n");
+ exit(2);
+ }
+ filev[filec++] = optarg;
+ break;
+ case 'D':
+ debug_mode = 1;
+ break;
+ case 'V':
+ printf("version: %s from %s (%s)\n",
+ DNS_PARSE_VERSION,
+ keyutils_version_string,
+ keyutils_build_string);
+ exit(0);
+ case 'v':
+ if (!ctx.report.verbose)
+ ctx.report.verbose = verbose;
+ else
+ ctx.report.verbose2 = verbose;
+ break;
+ case 'N':
+ if (strcmp(optarg, "vls-srv") == 0) {
+ ctx.no_vls_srv = true;
+ } else if (strcmp(optarg, "vls-afsdb") == 0) {
+ ctx.no_vls_afsdb = true;
+ } else if (strcmp(optarg, "vls-all") == 0) {
+ ctx.no_vls_srv = true;
+ ctx.no_vls_afsdb = true;
+ } else if (strcmp(optarg, "vl-host") == 0) {
+ ctx.no_vl_host = true;
+ } else if (strcmp(optarg, "dns") == 0) {
+ ctx.no_vls_srv = true;
+ ctx.no_vls_afsdb = true;
+ ctx.no_vl_host = true;
+ } else {
+ fprintf(stderr, "Unknown restriction '-N %s'\n", optarg);
+ usage();
+ }
+ break;
+ case 'o':
+ dump_file = optarg;
+ break;
+ default:
+ if (!isatty(2))
+ syslog(LOG_ERR, "unknown option: %c", ret);
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (!debug_mode) {
+ if (argc != 1)
+ usage();
+
+ /* get the key ID */
+ if (!**argv)
+ error("Invalid blank key ID");
+ key = strtol(*argv, &p, 10);
+ if (*p)
+ error("Invalid key ID format");
+
+ /* get the key description (of the form "x;x;x;x;<query_type>:<name>") */
+ ret = keyctl_describe_alloc(key, &buf);
+ if (ret == -1)
+ error("keyctl_describe_alloc failed: %m");
+
+ /* get the callout_info (which can supply options) */
+ ret = keyctl_read_alloc(KEY_SPEC_REQKEY_AUTH_KEY, (void **)&callout_info);
+ if (ret == -1)
+ error("Invalid key callout_info read: %m");
+ } else {
+ if (argc != 2)
+ usage();
+
+ ret = asprintf(&buf, "%s;-1;-1;0;%s", key_type, argv[0]);
+ if (ret < 0)
+ error("Error %m");
+ callout_info = argv[1];
+ }
+
+ ret = 1;
+ verbose("Key description: '%s'", buf);
+ verbose("Callout info: '%s'", callout_info);
+
+ p = strchr(buf, ';');
+ if (!p)
+ error("Badly formatted key description '%s'", buf);
+ ktlen = p - buf;
+
+ /* make sure it's the type we are expecting */
+ if (ktlen != sizeof(key_type) - 1 ||
+ memcmp(buf, key_type, ktlen) != 0)
+ error("Key type is not supported: '%*.*s'", ktlen, ktlen, buf);
+
+ keyend = buf + ktlen + 1;
+
+ /* the actual key description follows the last semicolon */
+ keyend = rindex(keyend, ';');
+ if (!keyend)
+ error("Invalid key description: %s", buf);
+ keyend++;
+
+ if (memcmp(keyend, afsdb_query_type, sizeof(afsdb_query_type) - 1) != 0)
+ error("Only 'afsdb' supported: %s", buf);
+ name = keyend + sizeof(afsdb_query_type) - 1;
+
+ verbose("Do AFS VL server query for:'%s' mask:'%s'", name, callout_info);
+
+ parse_callout(callout_info, &ctx);
+
+ /* Anything we create must fit into 1MiB buffer */
+ result = mmap(NULL, 1024 * 1024, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (result == MAP_FAILED)
+ error("mmap: %m");
+
+ if (filec > 0) {
+ filev[filec] = NULL;
+ filep = filev;
+ }
+
+ if (kafs_init_lookup_context(&ctx) < 0)
+ exit(1);
+
+ if (kafs_init_celldb(filep, &ctx.report) < 0)
+ exit(ctx.report.bad_config ? 3 : 1);
+
+ /* Generate the payload */
+ switch (output_version) {
+ case 0:
+ r_end = kafs_generate_text_payload(result, name, &ttl, &ctx);
+ break;
+
+ case 1:
+ default:
+ r_end = kafs_generate_v1_payload(result, name, &ttl, &ctx);
+ break;
+ }
+
+ if (!r_end)
+ error("failed");
+
+ verbose("version %u %zu", output_version, r_end - result);
+
+ if (dump_file) {
+ int fd = open(dump_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd == -1) {
+ perror(dump_file);
+ exit(1);
+ }
+
+ if (write(fd, result, r_end - result) != r_end - result) {
+ perror(dump_file);
+ exit(1);
+ }
+ close(fd);
+ }
+
+ /* Set the key's expiry time from the minimum TTL encountered and then
+ * pass the data to the key.
+ */
+ if (!debug_mode) {
+ if (ttl != UINT_MAX) {
+ ret = keyctl_set_timeout(key, ttl);
+ if (ret == -1)
+ error("keyctl_set_timeout: %m");
+ }
+
+ ret = keyctl_instantiate(key, result, r_end - result, 0);
+ if (ret == -1)
+ error("keyctl_instantiate: %m");
+ }
+
+ verbose("Success (%zu bytes)", r_end - result);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/* DNS resolver interface definitions.
+ *
+ * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _UAPI_LINUX_DNS_RESOLVER_H
+#define _UAPI_LINUX_DNS_RESOLVER_H
+
+#include <linux/types.h>
+
+/*
+ * Type of payload.
+ */
+enum dns_payload_content_type {
+ DNS_PAYLOAD_IS_SERVER_LIST = 0, /* List of servers, requested by srv=1 */
+};
+
+/*
+ * Type of address that might be found in an address record.
+ */
+enum dns_payload_address_type {
+ DNS_ADDRESS_IS_IPV4 = 0, /* 4-byte AF_INET address */
+ DNS_ADDRESS_IS_IPV6 = 1, /* 16-byte AF_INET6 address */
+};
+
+/*
+ * Type of protocol used to access a server.
+ */
+enum dns_payload_protocol_type {
+ DNS_SERVER_PROTOCOL_UNSPECIFIED = 0,
+ DNS_SERVER_PROTOCOL_UDP = 1, /* Use UDP to talk to the server */
+ DNS_SERVER_PROTOCOL_TCP = 2, /* Use TCP to talk to the server */
+};
+
+/*
+ * Source of record included in DNS resolver payload.
+ */
+enum dns_record_source {
+ DNS_RECORD_UNAVAILABLE = 0, /* No source available (empty record) */
+ DNS_RECORD_FROM_CONFIG = 1, /* From local configuration data */
+ DNS_RECORD_FROM_DNS_A = 2, /* From DNS A or AAAA record */
+ DNS_RECORD_FROM_DNS_AFSDB = 3, /* From DNS AFSDB record */
+ DNS_RECORD_FROM_DNS_SRV = 4, /* From DNS SRV record */
+ DNS_RECORD_FROM_NSS = 5, /* From NSS */
+ NR__dns_record_source
+};
+
+/*
+ * Status of record included in DNS resolver payload.
+ */
+enum dns_lookup_status {
+ DNS_LOOKUP_NOT_DONE = 0, /* No lookup has been made */
+ DNS_LOOKUP_GOOD = 1, /* Good records obtained */
+ DNS_LOOKUP_GOOD_WITH_BAD = 2, /* Good records, some decoding errors */
+ DNS_LOOKUP_BAD = 3, /* Couldn't decode results */
+ DNS_LOOKUP_GOT_NOT_FOUND = 4, /* Got a "Not Found" result */
+ DNS_LOOKUP_GOT_LOCAL_FAILURE = 5, /* Local failure during lookup */
+ DNS_LOOKUP_GOT_TEMP_FAILURE = 6, /* Temporary failure during lookup */
+ DNS_LOOKUP_GOT_NS_FAILURE = 7, /* Name server failure */
+ NR__dns_lookup_status
+};
+
+/*
+ * Header at the beginning of binary format payload.
+ */
+struct dns_payload_header {
+ __u8 zero; /* Zero byte: marks this as not being text */
+ __u8 content; /* enum dns_payload_content_type */
+ __u8 version; /* Encoding version */
+} __attribute__((packed));
+
+/*
+ * Header at the beginning of a V1 server list. This is followed directly by
+ * the server records. Each server records begins with a struct of type
+ * dns_server_list_v1_server.
+ */
+struct dns_server_list_v1_header {
+ struct dns_payload_header hdr;
+ __u8 source; /* enum dns_record_source */
+ __u8 status; /* enum dns_lookup_status */
+ __u8 nr_servers; /* Number of server records following this */
+} __attribute__((packed));
+
+/*
+ * Header at the beginning of each V1 server record. This is followed by the
+ * characters of the name with no NUL-terminator, followed by the address
+ * records for that server. Each address record begins with a struct of type
+ * struct dns_server_list_v1_address.
+ */
+struct dns_server_list_v1_server {
+ __u16 name_len; /* Length of name (LE) */
+ __u16 priority; /* Priority (as SRV record) (LE) */
+ __u16 weight; /* Weight (as SRV record) (LE) */
+ __u16 port; /* UDP/TCP port number (LE) */
+ __u8 source; /* enum dns_record_source */
+ __u8 status; /* enum dns_lookup_status */
+ __u8 protocol; /* enum dns_payload_protocol_type */
+ __u8 nr_addrs;
+} __attribute__((packed));
+
+/*
+ * Header at the beginning of each V1 address record. This is followed by the
+ * bytes of the address, 4 for IPV4 and 16 for IPV6.
+ */
+struct dns_server_list_v1_address {
+ __u8 address_type; /* enum dns_payload_address_type */
+} __attribute__((packed));
+
+#endif /* _UAPI_LINUX_DNS_RESOLVER_H */
--- /dev/null
+/*
+ * Cell server database parser.
+ *
+ * Copyright (C) David Howells (dhowells@redhat.com) 2018
+ *
+ * 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.
+ */
+
+#ifndef _KAFS_CELLSERV_H
+#define _KAFS_CELLSERV_H
+
+#include <stdbool.h>
+#include <resolv.h>
+#include <netinet/in.h>
+#include "reporting.h"
+
+struct kafs_profile;
+struct kafs_profile_parse;
+
+enum kafs_server_type {
+ kafs_server_is_untyped,
+ kafs_server_is_afs_vlserver,
+ kafs_server_is_afs_ptserver,
+};
+
+enum kafs_record_source {
+ kafs_record_unavailable,
+ kafs_record_from_config,
+ kafs_record_from_dns_a,
+ kafs_record_from_dns_afsdb,
+ kafs_record_from_dns_srv,
+ kafs_record_from_nss,
+ nr__kafs_record_source
+};
+
+enum kafs_lookup_status {
+ kafs_lookup_not_done,
+ kafs_lookup_good,
+ kafs_lookup_good_with_bad,
+ kafs_lookup_bad,
+ kafs_lookup_got_not_found,
+ kafs_lookup_got_local_failure,
+ kafs_lookup_got_temp_failure,
+ kafs_lookup_got_ns_failure,
+ nr__kafs_lookup_status
+};
+
+struct kafs_server_addr {
+ union {
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ };
+};
+
+struct kafs_server {
+ char *name;
+ struct kafs_server_addr *addrs;
+ unsigned int max_addrs;
+ unsigned int nr_addrs;
+ unsigned short port;
+ unsigned short pref;
+ unsigned short weight;
+ unsigned char protocol;
+ bool borrowed_name;
+ bool borrowed_addrs;
+ enum kafs_record_source source : 8;
+ enum kafs_lookup_status status : 8;
+ enum kafs_server_type type : 8;
+};
+
+struct kafs_server_list {
+ unsigned int nr_servers;
+ unsigned int max_servers;
+ unsigned int ttl;
+ enum kafs_record_source source : 8;
+ enum kafs_lookup_status status : 8;
+ struct kafs_server *servers;
+};
+
+struct kafs_cell {
+ char *name;
+ char *desc;
+ char *realm;
+ bool use_dns;
+ bool show_cell;
+ bool borrowed_name;
+ bool borrowed_desc;
+ bool borrowed_realm;
+ struct kafs_server_list *vlservers;
+};
+
+struct kafs_cell_db {
+ unsigned int nr_cells;
+ struct kafs_cell *cells[];
+};
+
+struct kafs_lookup_context {
+ struct kafs_report report;
+ struct __res_state res;
+ bool want_ipv4_addrs;
+ bool want_ipv6_addrs;
+ bool no_vls_afsdb;
+ bool no_vls_srv;
+ bool no_vl_host;
+};
+
+/*
+ * object.c
+ */
+extern int kafs_init_lookup_context(struct kafs_lookup_context *ctx);
+extern void kafs_clear_lookup_context(struct kafs_lookup_context *ctx);
+extern struct kafs_server_list *kafs_alloc_server_list(struct kafs_report *report);
+extern void kafs_free_server_list(struct kafs_server_list *sl);
+extern void kafs_free_cell(struct kafs_cell *cell);
+extern void kafs_transfer_addresses(struct kafs_server *to,
+ const struct kafs_server *from);
+extern int kafs_transfer_server_list(struct kafs_server_list *to,
+ const struct kafs_server_list *from);
+extern void kafs_transfer_cell(struct kafs_cell *to,
+ const struct kafs_cell *from);
+
+/*
+ * cellserv.c
+ */
+extern struct kafs_cell_db *kafs_cellserv_parse_conf(const struct kafs_profile *prof,
+ struct kafs_report *report);
+extern void kafs_cellserv_dump(const struct kafs_cell_db *db);
+extern const char *kafs_record_source(enum kafs_record_source source);
+extern const char *kafs_lookup_status(enum kafs_lookup_status status);
+extern void kafs_dump_cell(const struct kafs_cell *cell);
+
+/*
+ * dns_lookup.c
+ */
+extern int kafs_dns_lookup_addresses(struct kafs_server_list *sl,
+ struct kafs_lookup_context *ctx);
+extern int kafs_dns_lookup_vlservers(struct kafs_server_list *vsl,
+ const char *cell_name,
+ struct kafs_lookup_context *ctx);
+
+/*
+ * cell_lookup.c
+ */
+extern struct kafs_cell_db *kafs_cellserv_db;
+extern struct kafs_profile kafs_cellserv_profile;
+extern int kafs_init_celldb(const char *const *files,
+ struct kafs_report *report);
+extern struct kafs_cell *kafs_lookup_cell(const char *cell_name,
+ struct kafs_lookup_context *ctx);
+
+#endif /* _KAFS_CELLSERV_H */
--- /dev/null
+/*
+ * Kerberos-style profile file parser.
+ *
+ * Copyright (C) 2018 David Howells (dhowells@redhat.com)
+ *
+ * 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.
+ */
+
+#ifndef _KAFS_PROFILE_H
+#define _KAFS_PROFILE_H
+
+#include <stdbool.h>
+#include "reporting.h"
+
+enum kafs_profile_value_type {
+ kafs_profile_value_is_list,
+ kafs_profile_value_is_string,
+};
+
+struct kafs_profile {
+ enum kafs_profile_value_type type : 8;
+ bool final;
+ bool dummy;
+ unsigned int nr_relations;
+ unsigned int line;
+ const char *file;
+ char *name;
+ char *value;
+ struct kafs_profile *parent;
+ struct kafs_profile **relations;
+};
+
+extern void kafs_profile_dump(const struct kafs_profile *p,
+ unsigned int depth);
+extern int kafs_profile_parse_file(struct kafs_profile *prof,
+ const char *filename,
+ struct kafs_report *report);
+extern int kafs_profile_parse_dir(struct kafs_profile *prof,
+ const char *dirname,
+ struct kafs_report *report);
+extern const struct kafs_profile *
+kafs_profile_find_first_child(const struct kafs_profile *prof,
+ enum kafs_profile_value_type type,
+ const char *name,
+ struct kafs_report *report);
+
+typedef int (*kafs_profile_iterator)(const struct kafs_profile *child,
+ void *data,
+ struct kafs_report *report);
+extern int kafs_profile_iterate(const struct kafs_profile *prof,
+ enum kafs_profile_value_type type,
+ const char *name,
+ kafs_profile_iterator iterator,
+ void *data,
+ struct kafs_report *report);
+extern int kafs_profile_count(const struct kafs_profile *prof,
+ enum kafs_profile_value_type type,
+ const char *name,
+ unsigned int *_nr);
+
+/*
+ * Constant matching.
+ */
+struct kafs_constant_table {
+ const char *name;
+ int value;
+};
+
+extern int kafs_lookup_constant2(const struct kafs_constant_table tbl[],
+ size_t tbl_size,
+ const char *name,
+ int not_found);
+#define kafs_lookup_constant(t, n, nf) \
+ kafs_lookup_constant2(t, sizeof(t)/sizeof(t[0]), (n), (nf))
+
+extern int kafs_lookup_bool(const char *name, int not_found);
+
+
+/*
+ * Convenience relation parsers.
+ */
+static inline const char *kafs_profile_get_string(const struct kafs_profile *prof,
+ const char *name,
+ struct kafs_report *report)
+{
+ const struct kafs_profile *p;
+
+ p = kafs_profile_find_first_child(prof, kafs_profile_value_is_string, name,
+ report);
+
+ return p ? p->value : NULL;
+}
+
+static inline bool kafs_profile_get_bool(const struct kafs_profile *prof,
+ const char *name,
+ struct kafs_report *report)
+{
+ const struct kafs_profile *p;
+ int tmp;
+
+ p = kafs_profile_find_first_child(prof, kafs_profile_value_is_string, name,
+ report);
+ if (!p || !p->value)
+ return false;
+
+ tmp = kafs_lookup_bool(p->value, -1);
+ if (tmp == -1) {
+ report->error("%s:%u: Invalid bool value", p->file, p->line);
+ return false;
+ }
+
+ return tmp;
+}
+
+static inline int kafs_profile_iterate_list(const struct kafs_profile *prof,
+ const char *name,
+ kafs_profile_iterator iterator,
+ void *data,
+ struct kafs_report *report)
+{
+ return kafs_profile_iterate(prof, kafs_profile_value_is_list,
+ name, iterator, data, report);
+}
+
+static inline int kafs_profile_count_list(const struct kafs_profile *prof,
+ const char *name,
+ unsigned int *_nr)
+{
+ return kafs_profile_count(prof, kafs_profile_value_is_list,
+ name, _nr);
+}
+
+static inline int kafs_profile_iterate_strings(const struct kafs_profile *prof,
+ const char *name,
+ kafs_profile_iterator iterator,
+ void *data,
+ struct kafs_report *report)
+{
+ return kafs_profile_iterate(prof, kafs_profile_value_is_string,
+ name, iterator, data, report);
+}
+
+static inline int kafs_profile_count_strings(const struct kafs_profile *prof,
+ const char *name,
+ unsigned int *_nr)
+{
+ return kafs_profile_count(prof, kafs_profile_value_is_string,
+ name, _nr);
+}
+
+#endif /* _KAFS_PROFILE_H */
--- /dev/null
+/*
+ * Error reporting context.
+ *
+ * Copyright (C) David Howells (dhowells@redhat.com) 2018
+ *
+ * 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.
+ */
+
+#ifndef _KAFS_REPORTING_H
+#define _KAFS_REPORTING_H
+
+struct kafs_report {
+ void (*error)(const char *fmt, ...)
+ __attribute__((format(printf, 1, 2)));
+ void (*verbose)(const char *fmt, ...)
+ __attribute__((format(printf, 1, 2)));
+ void (*verbose2)(const char *fmt, ...)
+ __attribute__((format(printf, 1, 2)));
+ const char *what;
+ int line;
+ bool bad_config; /* T if bad config encountered */
+ bool bad_error; /* T if fatal system error encountered */
+ bool abandon_alloc; /* T to not clean up on error */
+};
+
+#endif /* _KAFS_REPORTING_H */
--- /dev/null
+/*
+ * kAFS filesystem configuration checker.
+ *
+ * Copyright (C) David Howells (dhowells@redhat.com) 2018
+ *
+ * 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.
+ */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <kafs/cellserv.h>
+#include <kafs/profile.h>
+
+static void error_report(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ fputc('\n', stderr);
+ va_end(va);
+}
+
+static void verbose(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ printf("[V] ");
+ vprintf(fmt, va);
+ putchar('\n');
+ va_end(va);
+}
+
+static __attribute__((noreturn))
+void usage(const char *prog)
+{
+ fprintf(stderr,
+ "Usage: %s [-46PDvv] [-c <conffile>]* [-N <restriction>] [<cellname>]*\n",
+ prog);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Where restrictions are one or more of:\n");
+ fprintf(stderr, "\t-N dns\n");
+ fprintf(stderr, "\t-N vls-afsdb\n");
+ fprintf(stderr, "\t-N vls-srv\n");
+ fprintf(stderr, "\t-N vls-all\n");
+ fprintf(stderr, "\t-N vl-host\n");
+ exit(2);
+}
+
+int main(int argc, char *argv[])
+{
+ struct kafs_lookup_context ctx = {
+ .report.error = error_report,
+ .want_ipv4_addrs = true,
+ .want_ipv6_addrs = true,
+ };
+ const char *filev[10], **filep = NULL;
+ bool dump_profile = false, dump_db = false;
+ int opt, filec = 0;
+
+ if (argc > 1 && strcmp(argv[1], "--help") == 0)
+ usage(argv[0]);
+
+ while (opt = getopt(argc, argv, "46PDc:vN:"),
+ opt != -1) {
+ switch (opt) {
+ case 'c':
+ if (filec >= 9) {
+ fprintf(stderr, "Max 9 files\n");
+ exit(2);
+ }
+ filev[filec++] = optarg;
+ break;
+ case 'v':
+ if (!ctx.report.verbose)
+ ctx.report.verbose = verbose;
+ else
+ ctx.report.verbose2 = verbose;
+ break;
+ case 'P':
+ dump_profile = true;
+ break;
+ case 'D':
+ dump_db = true;
+ break;
+ case '4':
+ ctx.want_ipv4_addrs = true;
+ ctx.want_ipv6_addrs = false;
+ break;
+ case '6':
+ ctx.want_ipv4_addrs = false;
+ ctx.want_ipv6_addrs = true;
+ break;
+ case 'N':
+ if (strcmp(optarg, "vl-srv") == 0) {
+ ctx.no_vls_srv = true;
+ } else if (strcmp(optarg, "vl-afsdb") == 0) {
+ ctx.no_vls_afsdb = true;
+ } else if (strcmp(optarg, "vl-all") == 0) {
+ ctx.no_vls_srv = true;
+ ctx.no_vls_afsdb = true;
+ } else if (strcmp(optarg, "vl-host") == 0) {
+ ctx.no_vl_host = true;
+ } else if (strcmp(optarg, "dns") == 0) {
+ ctx.no_vls_srv = true;
+ ctx.no_vls_afsdb = true;
+ ctx.no_vl_host = true;
+ } else {
+ fprintf(stderr, "Unknown restriction '-N %s'\n", optarg);
+ usage(argv[0]);
+ }
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (filec > 0) {
+ filev[filec] = NULL;
+ filep = filev;
+ }
+
+ if (kafs_init_lookup_context(&ctx) < 0)
+ exit(1);
+
+ if (kafs_init_celldb(filep, &ctx.report) < 0)
+ exit(ctx.report.bad_config ? 3 : 1);
+
+ if (dump_profile)
+ kafs_profile_dump(&kafs_cellserv_profile, 0);
+ if (dump_db)
+ kafs_cellserv_dump(kafs_cellserv_db);
+
+ for (; *argv; argv++) {
+ struct kafs_cell *cell;
+
+ cell = kafs_lookup_cell(*argv, &ctx);
+ if (cell) {
+ printf("\n");
+ printf("=== Found cell %s ===\n", cell->name);
+ kafs_dump_cell(cell);
+ }
+ }
+
+ kafs_clear_lookup_context(&ctx);
+ return 0;
+}
--- /dev/null
+/*
+ * Cell database access.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <resolv.h>
+#include <netdb.h>
+#include <errno.h>
+#include <kafs/cellserv.h>
+#include <kafs/profile.h>
+
+static const char *const kafs_std_cellservdb[] = {
+ ETCDIR "/kafs/cellservdb.conf",
+ NULL
+};
+
+struct kafs_cell_db *kafs_cellserv_db;
+struct kafs_profile kafs_cellserv_profile = { .name = "<cellservdb>" };
+
+#define verbose(r, fmt, ...) \
+ do { \
+ if ((r)->verbose) \
+ (r)->verbose(fmt, ## __VA_ARGS__); \
+ } while(0)
+
+/*
+ * Allocate a cell record.
+ */
+struct kafs_cell *kafs_alloc_cell(const char *cell_name,
+ struct kafs_lookup_context *ctx)
+{
+ struct kafs_cell *cell;
+
+ cell = calloc(1, sizeof(*cell));
+ if (!cell)
+ goto error;
+
+ cell->name = strdup(cell_name);
+ if (!cell->name)
+ goto error;
+
+ return cell;
+
+error:
+ ctx->report.error("%m");
+ return NULL;
+}
+
+/*
+ * Initialise the cell database.
+ */
+int kafs_init_celldb(const char *const *files, struct kafs_report *report)
+{
+
+ if (!files)
+ files = kafs_std_cellservdb;
+
+ for (; *files; files++)
+ if (kafs_profile_parse_file(&kafs_cellserv_profile, *files, report) == -1)
+ return -1;
+
+ kafs_cellserv_db = kafs_cellserv_parse_conf(&kafs_cellserv_profile, report);
+ if (!kafs_cellserv_db)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Deal with an unconfigured cell.
+ */
+static int kafs_unconfigured_cell(struct kafs_cell *cell,
+ struct kafs_lookup_context *ctx)
+{
+ struct kafs_server_list *vsl;
+
+ verbose(&ctx->report, "%s: Cell not found in config", cell->name);
+
+ vsl = kafs_alloc_server_list(&ctx->report);
+ if (!vsl)
+ return -1;
+ cell->vlservers = vsl;
+
+ if (kafs_dns_lookup_vlservers(vsl, cell->name, ctx) < 0 ||
+ kafs_dns_lookup_addresses(vsl, ctx) < 0)
+ return -1;
+
+ verbose(&ctx->report, "DNS query AFSDB RR results:%u ttl:%u",
+ vsl->nr_servers, vsl->ttl);
+ return 0;
+}
+
+/*
+ * Look up a cell in configuration and DNS.
+ *
+ * The rules are:
+ *
+ * (*) Look up the cell in the configuration first.
+ *
+ * (*) If there's no cell in the config, we have to try and build it entirely
+ * from the DNS.
+ *
+ * (*) Else:
+ *
+ * (*) We look at the configured no_dns setting:
+ *
+ * (*) If true, we use the list of servers listed in the config
+ *
+ * (*) If false, we try to replace that with one derived from the DNS.
+ *
+ * (*) For each server:
+ *
+ * (*) we try to look up a list of addresses in NSS/DNS.
+ *
+ * (*) If that fails, we use the list of addresses from the config.
+ */
+struct kafs_cell *kafs_lookup_cell(const char *cell_name,
+ struct kafs_lookup_context *ctx)
+{
+ const struct kafs_server_list *cvsl;
+ struct kafs_server_list *vsl;
+ const struct kafs_cell *conf_cell;
+ struct kafs_cell *cell;
+ unsigned int i, j;
+
+ if (!kafs_cellserv_db && kafs_init_celldb(NULL, &ctx->report) < 0)
+ return NULL;
+
+ cell = kafs_alloc_cell(cell_name, ctx);
+ if (!cell)
+ return NULL;
+
+ for (i = 0; i < kafs_cellserv_db->nr_cells; i++) {
+ conf_cell = kafs_cellserv_db->cells[i];
+
+ if (strcmp(cell_name, conf_cell->name) == 0)
+ goto cell_is_configured;
+ }
+
+ if (kafs_unconfigured_cell(cell, ctx) < 0)
+ goto error;
+ return cell;
+
+ /* Deal with the case where we have a configuration. */
+cell_is_configured:
+ verbose(&ctx->report, "%s: Found cell in config", cell_name);
+
+ kafs_transfer_cell(cell, conf_cell);
+
+ vsl = kafs_alloc_server_list(&ctx->report);
+ if (!vsl)
+ goto error;
+ cell->vlservers = vsl;
+
+ /* The DNS overrides the configuration if indicated. */
+ if (conf_cell->use_dns) {
+ verbose(&ctx->report, "Query DNS for server list");
+ if (kafs_dns_lookup_vlservers(vsl, cell_name, ctx) < 0)
+ goto error;
+
+ verbose(&ctx->report, "Looked up %u VL servers [%s, %s]",
+ vsl->nr_servers,
+ kafs_lookup_status(vsl->status),
+ kafs_record_source(vsl->source));
+ }
+
+ /* If we didn't get any servers, copy the server list from the
+ * configuration.
+ */
+ if (vsl->nr_servers == 0) {
+ verbose(&ctx->report, "Use configured server list");
+ if (kafs_transfer_server_list(vsl, conf_cell->vlservers) < 0)
+ goto error;
+ }
+
+ /* Try and look up addresses for all the servers in the list. */
+ if (kafs_dns_lookup_addresses(vsl, ctx) < 0)
+ goto error;
+
+ /* Borrow addresses from the config for any server that didn't find any
+ * in the DNS.
+ */
+ cvsl = conf_cell->vlservers;
+ if (cvsl) {
+ for (i = 0; i < vsl->nr_servers; i++) {
+ struct kafs_server *srv = &vsl->servers[i];
+
+ if (srv->nr_addrs)
+ continue;
+
+ verbose(&ctx->report, "Borrow addresses for '%s'", srv->name);
+ for (j = 0; j < cvsl->nr_servers; j++) {
+ const struct kafs_server *csrv = &cvsl->servers[j];
+
+ if (strcmp(srv->name, csrv->name) == 0) {
+ verbose(&ctx->report, "From '%s' %u",
+ csrv->name, csrv->nr_addrs);
+ kafs_transfer_addresses(srv, csrv);
+ break;
+ }
+ }
+ }
+ }
+
+ return cell;
+
+error:
+ if (!ctx->report.abandon_alloc)
+ kafs_free_cell(cell);
+ return NULL;
+}
--- /dev/null
+/*
+ * Parse the profile tree into a cell server database
+ *
+ * Copyright (C) David Howells (dhowells@redhat.com) 2018
+ *
+ * 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.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <kafs/cellserv.h>
+#include <kafs/profile.h>
+#include "dns_resolver.h"
+
+#define report_error(r, fmt, ...) \
+ ({ \
+ r->error(fmt, ## __VA_ARGS__); \
+ -1; \
+ })
+
+#define parse_error(r, fmt, ...) \
+ ({ \
+ r->bad_config = true; \
+ r->error("%s:%u: " fmt, r->what, r->line, ## __VA_ARGS__); \
+ -1; \
+ })
+
+#define verbose(r, fmt, ...) \
+ do { \
+ if (r->verbose) \
+ r->verbose(fmt, ## __VA_ARGS__); \
+ } while(0)
+
+#define verbose2(r, fmt, ...) \
+ do { \
+ if (r->verbose2) \
+ r->verbose2(fmt, ## __VA_ARGS__); \
+ } while(0)
+
+/*
+ * Parse an address.
+ */
+static int cellserv_parse_address(const struct kafs_profile *child,
+ void *data,
+ struct kafs_report *report)
+{
+ struct kafs_server *server = data;
+ struct kafs_server_addr *addr = &server->addrs[server->nr_addrs];
+ const char *v = child->value;
+
+ if (server->nr_addrs >= server->max_addrs) {
+ report_error(report, "%s: Address list overrun", server->name);
+ return 0;
+ }
+
+ if (inet_pton(AF_INET, v, &addr->sin.sin_addr) == 1) {
+ addr->sin.sin_family = AF_INET;
+ addr->sin.sin_port = htons(server->port);
+ server->nr_addrs++;
+ return 0;
+ }
+
+ if (v[0] == '[') {
+ char *p;
+
+ v++;
+ p = strchr(v, ']');
+ if (!p || p[1])
+ goto invalid;
+ p[0] = 0;
+ }
+
+ if (inet_pton(AF_INET6, v, &addr->sin6.sin6_addr) == 1) {
+ addr->sin6.sin6_family = AF_INET6;
+ addr->sin6.sin6_port = htons(server->port);
+ server->nr_addrs++;
+ return 0;
+ }
+
+invalid:
+ parse_error(report, "%s:%u: Invalid address '%s'",
+ child->file, child->line, child->value);
+ return 0;
+}
+
+/*
+ * Parse a server definition.
+ */
+static int cellserv_parse_server(const struct kafs_profile *child,
+ void *data,
+ struct kafs_report *report)
+{
+ struct kafs_server_list *vsl = data;
+ struct kafs_server *server = &vsl->servers[vsl->nr_servers];
+ unsigned long tmp;
+ unsigned int max_addrs = 0;
+ const char *p;
+ char *q;
+
+ if (vsl->nr_servers >= vsl->max_servers) {
+ report_error(report, "%s: Server list overrun", server->name);
+ return 0;
+ }
+
+ memset(server, 0, sizeof(*server));
+ server->source = kafs_record_from_config;
+
+ /* Strip off any protocol indicator */
+ server->name = child->name;
+ if (strncmp(server->name, "udp/", 4) == 0) {
+ server->protocol = DNS_SERVER_PROTOCOL_UDP;
+ server->name += 4;
+ } else if (strncmp(server->name, "tcp/", 4) == 0) {
+ server->protocol = DNS_SERVER_PROTOCOL_TCP;
+ server->name += 4;
+ }
+
+ if (!server->name[0])
+ return 0;
+
+ /* Strip any port number and square brackets */
+ if (server->name[0] == '[') {
+ server->name++;
+ p = strchr(server->name, ']');
+ if (!*p)
+ return 0;
+ *(char *)p = 0;
+ p++;
+ if (*p) {
+ if (*p != ':')
+ return 0;
+ p++;
+ goto extract_port;
+ }
+ }
+
+ /* Look for foo.com:port or 1.2.3.4:port, but dodge 1:2:3:4 */
+ p = strchr(server->name, ':');
+ if (!p)
+ goto no_port;
+ *(char *)p = 0;
+ p++;
+ if (strchr(p, ':'))
+ goto no_port;
+
+extract_port:
+ tmp = strtoul(p, &q, 0);
+ if (*q)
+ goto unparseable;
+ if (tmp > 65536)
+ goto unparseable;
+ server->port = tmp;
+no_port:
+
+ p = kafs_profile_get_string(child, "port", report);
+ if (p) {
+ tmp = strtoul(p, &q, 0);
+ if (*q)
+ goto unparseable;
+ if (tmp > 65536)
+ goto unparseable;
+ server->port = tmp;
+ }
+
+ /* Generate a list of addresses */
+ if (kafs_profile_count_strings(child, "address", &max_addrs) < 0)
+ return -1;
+
+ server->addrs = calloc(max_addrs, sizeof(struct kafs_server));
+ if (!server->addrs)
+ return -1;
+ server->max_addrs = max_addrs;
+
+ if (kafs_profile_iterate_strings(child, "address",
+ cellserv_parse_address, server,
+ report) < 0)
+ return -1;
+
+ p = kafs_profile_get_string(child, "type", report);
+ if (p) {
+ if (strcmp(p, "vlserver") == 0)
+ server->type = kafs_server_is_afs_vlserver;
+ else if (strcmp(p, "ptserver") == 0)
+ server->type = kafs_server_is_afs_ptserver;
+ else
+ fprintf(stderr, "Unknown type '%s'\n", p);
+ }
+
+ vsl->nr_servers++;
+ return 0;
+
+unparseable:
+ parse_error(report, "%s:%u: Invalid address\n", child->file, child->line);
+ return 0;
+}
+
+/*
+ * Find any Volume Location servers listed for a cell.
+ */
+static int kafs_cellserv_parse_vl(const struct kafs_profile *child,
+ struct kafs_cell *cell,
+ struct kafs_report *report)
+{
+ const struct kafs_profile *servers;
+ struct kafs_server_list *vsl;
+ unsigned int max_servers = 0;
+
+ /* Find any Volume Location servers listed for that cell */
+ servers = kafs_profile_find_first_child(child, kafs_profile_value_is_list,
+ "servers", report);
+ if (!servers) {
+ verbose(report, "%s: No servers list", child->name);
+ return 0;
+ }
+
+ if (kafs_profile_count_list(servers, NULL, &max_servers) < 0)
+ return -1;
+
+ vsl = calloc(1, sizeof(*vsl));
+ if (!vsl)
+ return -1;
+ vsl->source = kafs_record_from_config;
+
+ cell->vlservers = vsl;
+ vsl->servers = calloc(max_servers, sizeof(struct kafs_server));
+ if (!vsl->servers)
+ return -1;
+
+ vsl->max_servers = max_servers;
+ return kafs_profile_iterate_list(servers, NULL, cellserv_parse_server,
+ vsl, report);
+}
+
+/*
+ * Parse a cell definition.
+ */
+static int kafs_cellserv_parse_cell(const struct kafs_profile *child,
+ void *data,
+ struct kafs_report *report)
+{
+ struct kafs_cell_db *db = data;
+ struct kafs_cell *cell;
+
+ cell = calloc(1, sizeof(*cell));
+ if (!cell)
+ return -1;
+ cell->name = child->name;
+ cell->show_cell = kafs_profile_get_bool(child, "show_cell", report);
+ cell->use_dns = kafs_profile_get_bool(child, "use_dns", report);
+ cell->desc = (char *)kafs_profile_get_string(child, "description", report);
+ cell->realm = (char *)kafs_profile_get_string(child, "kerberos_realm", report);
+ cell->borrowed_name = true;
+ cell->borrowed_desc = true;
+ cell->borrowed_realm = true;
+
+ verbose2(report, "CELL: %s: %s", cell->name, cell->desc);
+ db->cells[db->nr_cells] = cell;
+ db->nr_cells++;
+
+ return kafs_cellserv_parse_vl(child, cell, report);
+}
+
+/*
+ * Extract cell information from a kafs_profile parse tree.
+ */
+struct kafs_cell_db *kafs_cellserv_parse_conf(const struct kafs_profile *prof,
+ struct kafs_report *report)
+{
+ const struct kafs_profile *cells;
+ struct kafs_cell_db *db;
+ unsigned int nr_cells = 0;
+
+ cells = kafs_profile_find_first_child(prof, kafs_profile_value_is_list, "cells", report);
+ if (!cells) {
+ report_error(report, "Cannot find [cells] section");
+ return NULL;
+ }
+
+ if (kafs_profile_count_list(cells, NULL, &nr_cells) < 0)
+ return NULL;
+
+ db = calloc(1, sizeof(*db) + nr_cells * sizeof(struct kafs_cell *));
+ if (!db)
+ return NULL;
+ if (!nr_cells)
+ return db;
+
+ if (kafs_profile_iterate_list(cells, NULL,
+ kafs_cellserv_parse_cell, db,
+ report) == -1)
+ return NULL;
+
+ return db;
+}
+
+static const char *const kafs_record_sources[nr__kafs_record_source] = {
+ [kafs_record_unavailable] = "unavailable",
+ [kafs_record_from_config] = "config",
+ [kafs_record_from_dns_a] = "A",
+ [kafs_record_from_dns_afsdb] = "AFSDB",
+ [kafs_record_from_dns_srv] = "SRV",
+ [kafs_record_from_nss] = "nss",
+};
+
+static const char *const kafs_lookup_statuses[nr__kafs_lookup_status] = {
+ [kafs_lookup_not_done] = "no-lookup",
+ [kafs_lookup_good] = "good",
+ [kafs_lookup_good_with_bad] = "good/bad",
+ [kafs_lookup_bad] = "bad",
+ [kafs_lookup_got_not_found] = "not-found",
+ [kafs_lookup_got_local_failure] = "local-failure",
+ [kafs_lookup_got_temp_failure] = "temp-failure",
+ [kafs_lookup_got_ns_failure] = "ns-failure",
+};
+
+const char *kafs_record_source(enum kafs_record_source source)
+{
+ if (source >= nr__kafs_record_source)
+ return "unknown";
+ return kafs_record_sources[source] ?: "unknown";
+}
+
+const char *kafs_lookup_status(enum kafs_lookup_status status)
+{
+ if (status >= nr__kafs_lookup_status)
+ return "unknown";
+ return kafs_lookup_statuses[status] ?: "unknown";
+}
+
+/*
+ * Dump a server set.
+ */
+void kafs_dump_server_list(const struct kafs_server_list *sl,
+ const char *server_type)
+{
+ unsigned int j, k;
+ const char *p;
+ char buf[100];
+
+ for (j = 0; j < sl->nr_servers; j++) {
+ const struct kafs_server *srv = &sl->servers[j];
+
+ printf(" - %s %s [%s; %s]\n",
+ server_type, srv->name,
+ kafs_lookup_status(srv->status),
+ kafs_record_source(srv->source));
+
+ if (srv->type)
+ printf(" - %s\n",
+ srv->type == kafs_server_is_afs_vlserver ?
+ "VLServer" : "PTServer");
+ if (srv->protocol)
+ printf(" - %s\n",
+ srv->protocol == DNS_SERVER_PROTOCOL_UDP ? "udp" : "tcp");
+ if (srv->port || srv->pref || srv->weight)
+ printf(" - port %u, pref %u, weight %u\n",
+ srv->port, srv->pref, srv->weight);
+
+ for (k = 0; k < srv->nr_addrs; k++) {
+ const struct kafs_server_addr *addr = &srv->addrs[k];
+
+ switch (addr->sin.sin_family) {
+ case AF_INET:
+ p = inet_ntop(AF_INET, &addr->sin.sin_addr,
+ buf, sizeof(buf));
+ break;
+ case AF_INET6:
+ p = inet_ntop(AF_INET6, &addr->sin6.sin6_addr,
+ buf, sizeof(buf));
+ break;
+ default:
+ p = NULL;
+ break;
+ }
+
+ if (p)
+ printf(" - address %s\n", p);
+ }
+ }
+}
+
+/*
+ * Dump a cell.
+ */
+void kafs_dump_cell(const struct kafs_cell *cell)
+{
+ const struct kafs_server_list *vsl = cell->vlservers;
+
+ if (!cell->use_dns)
+ printf(" - use-dns=no\n");
+ if (!cell->show_cell)
+ printf(" - show-cell=no\n");
+
+ if (vsl) {
+ printf(" - status: %s, from %s\n",
+ kafs_lookup_status(vsl->status),
+ kafs_record_source(vsl->source));
+ kafs_dump_server_list(vsl, "VLSERVER");
+ }
+}
+
+/*
+ * Dump the parsed afs database.
+ */
+void kafs_cellserv_dump(const struct kafs_cell_db *db)
+{
+ unsigned int i;
+
+ for (i = 0; i < db->nr_cells; i++) {
+ const struct kafs_cell *cell = db->cells[i];
+
+ printf("CELL %s\n", cell->name);
+ kafs_dump_cell(cell);
+ }
+}
--- /dev/null
+/*
+ * Build a cell VL address set based on DNS 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 liberary, 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <resolv.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <kafs/cellserv.h>
+#include "dns_resolver.h"
+
+#define AFS_VL_PORT 7003 /* volume location service port */
+
+#define verbose(fmt, ...) \
+ do { \
+ if (ctx->report.verbose) \
+ ctx->report.verbose(fmt, ## __VA_ARGS__); \
+ } while(0)
+
+/*
+ * Perform address resolution on a hostname and add the resulting address as a
+ * string to the list of payload segments.
+ */
+static int kafs_resolve_addrs(struct kafs_server *server,
+ int socktype,
+ struct kafs_lookup_context *ctx)
+{
+ struct kafs_server_addr *addr;
+ struct addrinfo hints, *addrs, *ai;
+ int ret, count = 0;
+
+ verbose("Resolve '%s'", server->name);
+
+ server->source = kafs_record_from_nss;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = socktype;
+ if (ctx->want_ipv4_addrs && !ctx->want_ipv6_addrs)
+ hints.ai_family = AF_INET;
+ else if (ctx->want_ipv6_addrs && !ctx->want_ipv4_addrs)
+ hints.ai_family = AF_INET6;
+
+ /* resolve name to ip */
+ ret = getaddrinfo(server->name, NULL, &hints, &addrs);
+ if (ret) {
+ verbose("%s: getaddrinfo() = %d", server->name, ret);
+ switch (ret) {
+ case EAI_MEMORY:
+ case EAI_SYSTEM:
+ ctx->report.error("%s: getaddrinfo(): %m", server->name);
+ goto system_error;
+ case EAI_FAMILY:
+ case EAI_SOCKTYPE:
+ ctx->report.bad_error = true;
+ server->status = kafs_lookup_got_local_failure;
+ goto fail;
+ default:
+ server->status = kafs_lookup_got_local_failure;
+ /* Fall through. */
+ fail:
+ ctx->report.error("%s: %s", server->name, gai_strerror(ret));
+ return 0;
+ case EAI_FAIL:
+#ifdef EAI_NODATA
+ case EAI_NODATA:
+#endif
+ case EAI_NONAME:
+ case EAI_SERVICE:
+ server->status = kafs_lookup_got_not_found;
+ goto fail;
+ case EAI_AGAIN:
+ server->status = kafs_lookup_got_temp_failure;
+ goto fail;
+ }
+ }
+
+ for (ai = addrs; ai; ai = ai->ai_next)
+ count++;
+
+ server->addrs = calloc(count, sizeof(*addr));
+ if (!server->addrs) {
+ ctx->report.error("%m");
+ goto system_error;
+ }
+
+ server->max_addrs = count;
+ server->source = kafs_record_from_nss;
+ server->status = kafs_lookup_good;
+
+ for (ai = addrs; ai; ai = ai->ai_next) {
+ addr = &server->addrs[server->nr_addrs];
+
+ verbose("RR: %x,%x,%x,%x,%x,%s",
+ ai->ai_flags, ai->ai_family,
+ ai->ai_socktype, ai->ai_protocol,
+ ai->ai_addrlen, ai->ai_canonname);
+
+ /* convert address to string */
+ switch (ai->ai_family) {
+ case AF_INET:
+ if (!ctx->want_ipv4_addrs)
+ continue;
+ memcpy(&addr->sin, (struct sockaddr_in *)ai->ai_addr,
+ sizeof(addr->sin));
+ server->nr_addrs++;
+ break;
+ case AF_INET6:
+ if (!ctx->want_ipv6_addrs)
+ continue;
+ memcpy(&addr->sin6, (struct sockaddr_in *)ai->ai_addr,
+ sizeof(addr->sin6));
+ server->nr_addrs++;
+ break;
+ default:
+ verbose("Address of unknown family %u", ai->ai_family);
+ continue;
+ }
+ }
+
+ freeaddrinfo(addrs);
+ return 0;
+
+system_error:
+ ctx->report.bad_error = true;
+ return -1;
+}
+
+/*
+ * Go through all the servers records and look up addresses for them.
+ */
+int kafs_dns_lookup_addresses(struct kafs_server_list *ss,
+ struct kafs_lookup_context *ctx)
+{
+ struct kafs_server *server;
+ unsigned int i;
+ int ret;
+
+ if (ss) {
+ verbose("NR_SERVERS %u", ss->nr_servers);
+
+ if (ctx->no_vl_host) {
+ verbose("Use of DNS for FS server lookup is disabled.");
+ return 0;
+ }
+
+ for (i = 0; i < ss->nr_servers; i++) {
+ server = &ss->servers[i];
+
+ /* Turn the hostname into IP addresses */
+ ret = kafs_resolve_addrs(server, SOCK_DGRAM, ctx);
+ if (ret)
+ verbose("AFSDB RR can't resolve. subtype:1, server name:%s",
+ server->name);
+ else
+ verbose("NR_ADDRS %u", server->nr_addrs);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Convert the outcome of an AFSDB record lookup into a set of server records.
+ */
+static int kafs_parse_afsdb(struct kafs_server_list *vsl,
+ const char *cell_name,
+ unsigned short subtype,
+ ns_msg handle,
+ ns_sect section,
+ struct kafs_lookup_context *ctx)
+{
+ struct kafs_server *server;
+ unsigned int rr_ttl, max_servers = 0, i;
+ ns_rr rr;
+ char buf[MAXDNAME];
+ int rrnum, rr_subtype;
+
+ verbose("AFSDB RR count is %d", ns_msg_count(handle, section));
+
+ /* Count the number of afsdb records */
+ for (rrnum = 0; rrnum < ns_msg_count(handle, section); rrnum++) {
+ if (ns_parserr(&handle, section, rrnum, &rr)) {
+ ctx->report.error("%s: afsdb parse failed", cell_name);
+ continue;
+ }
+
+ if (ns_rr_type(rr) != ns_t_afsdb)
+ continue;
+ rr_subtype = ns_get16(ns_rr_rdata(rr));
+ if (rr_subtype != subtype)
+ continue;
+ max_servers++;
+ }
+
+ verbose("NR_SERVER %u", max_servers);
+
+ vsl->max_servers = max_servers;
+ vsl->servers = calloc(max_servers, sizeof(struct kafs_server));
+ if (!vsl->servers)
+ goto system_error;
+
+ /* Look at all the resource records in this section. */
+ for (rrnum = 0; rrnum < ns_msg_count(handle, section); rrnum++) {
+ server = &vsl->servers[vsl->nr_servers];
+
+ /* Expand the resource record number rrnum into rr. */
+ if (ns_parserr(&handle, section, rrnum, &rr)) {
+ ctx->report.error("%s: afsdb parse failed", cell_name);
+ vsl->status = kafs_lookup_bad;
+ continue;
+ }
+
+ /* We're only interested in AFSDB records */
+ if (ns_rr_type(rr) != ns_t_afsdb)
+ continue;
+ rr_subtype = ns_get16(ns_rr_rdata(rr));
+ if (rr_subtype != subtype)
+ continue;
+
+ /* Expand the name server's domain name */
+ if (ns_name_uncompress(ns_msg_base(handle),
+ ns_msg_end(handle),
+ ns_rr_rdata(rr) + 2,
+ buf,
+ MAXDNAME) < 0) {
+ ctx->report.error("%s: afsdb uncompress failed", cell_name);
+ vsl->status = kafs_lookup_bad;
+ continue;
+ }
+
+ rr_ttl = ns_rr_ttl(rr);
+ if (vsl->ttl > rr_ttl)
+ vsl->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 < vsl->nr_servers; i++)
+ if (strcasecmp(buf, vsl->servers[i].name) == 0)
+ continue;
+
+ server->name = strdup(buf);
+ if (!server->name)
+ goto system_error;
+ server->port = AFS_VL_PORT;
+ server->protocol = DNS_SERVER_PROTOCOL_UDP;
+
+ verbose("SERVER[%u] %s", vsl->nr_servers, server->name);
+ vsl->nr_servers++;
+ }
+
+ if (vsl->nr_servers > 0 && vsl->status == kafs_lookup_bad)
+ vsl->status = kafs_lookup_good_with_bad;
+ if (vsl->nr_servers == 0 && vsl->status == kafs_lookup_good_with_bad)
+ vsl->status = kafs_lookup_got_not_found;
+
+ return 0;
+
+system_error:
+ ctx->report.bad_error = true;
+ ctx->report.error("%m");
+ return -1;
+}
+
+/*
+ * Look up an AFSDB record to get the VL server addresses.
+ */
+static int dns_query_AFSDB(struct kafs_server_list *vsl,
+ const char *cell_name,
+ unsigned short subtype,
+ struct kafs_lookup_context *ctx)
+{
+ int response_len; /* buffer length */
+ ns_msg handle; /* handle for response message */
+ union {
+ HEADER hdr;
+ u_char buf[NS_PACKETSZ];
+ } response; /* response buffers */
+
+ vsl->source = kafs_record_from_dns_afsdb;
+ vsl->status = kafs_lookup_good;
+
+ verbose("Get AFSDB RR for cell name:'%s'", cell_name);
+
+ /* query the dns for an AFSDB resource record */
+ response_len = res_nquery(&ctx->res,
+ cell_name,
+ ns_c_in,
+ ns_t_afsdb,
+ response.buf,
+ sizeof(response));
+
+ if (response_len < 0) {
+ ctx->report.error("%s: %s", cell_name, hstrerror(h_errno));
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ default:
+ vsl->status = kafs_lookup_got_not_found;
+ break;
+ case NO_RECOVERY:
+ vsl->status = kafs_lookup_got_ns_failure;
+ break;
+ case TRY_AGAIN:
+ vsl->status = kafs_lookup_got_temp_failure;
+ break;
+ }
+ return 0;
+ }
+
+ if (ns_initparse(response.buf, response_len, &handle) < 0) {
+ ctx->report.error("%s: ns_initparse: %s",
+ cell_name, hstrerror(h_errno));
+ vsl->status = kafs_lookup_bad;
+ return 0;
+ }
+
+ /* look up the hostnames we've obtained to get the actual addresses */
+ return kafs_parse_afsdb(vsl, cell_name, subtype, handle, ns_s_an, ctx);
+}
+
+/*
+ * Convert the outcome of an SRV record lookup into a set of server records.
+ */
+static int kafs_parse_srv(struct kafs_server_list *vsl,
+ const char *domain_name,
+ ns_msg handle,
+ ns_sect section,
+ enum dns_payload_protocol_type protocol,
+ struct kafs_lookup_context *ctx)
+{
+ struct kafs_server *server;
+ unsigned int max_servers = 0, rr_ttl, i;
+ ns_rr rr;
+ char buf[MAXDNAME];
+ int rrnum;
+
+ verbose("SRV RR count is %d", ns_msg_count(handle, section));
+
+ /* Count the number of srv records */
+ for (rrnum = 0; rrnum < ns_msg_count(handle, section); rrnum++) {
+ if (ns_parserr(&handle, section, rrnum, &rr)) {
+ ctx->report.error("%s: ns_parserr", domain_name);
+ continue;
+ }
+
+ if (ns_rr_type(rr) != ns_t_srv)
+ continue;
+ max_servers++;
+ }
+
+ verbose("NR_SERVER %u", max_servers);
+
+ vsl->max_servers = max_servers;
+ vsl->servers = calloc(max_servers, sizeof(struct kafs_server));
+ if (!vsl->servers)
+ goto system_error;
+
+ for (rrnum = 0; rrnum < ns_msg_count(handle, section); rrnum++) {
+ server = &vsl->servers[vsl->nr_servers];
+
+ /* Expand the resource record number rrnum into rr. */
+ if (ns_parserr(&handle, section, rrnum, &rr)) {
+ ctx->report.error("%s: ns_parserr", domain_name);
+ vsl->status = kafs_lookup_bad;
+ continue;
+ }
+
+ if (ns_rr_type(rr) != ns_t_srv)
+ continue;
+
+ ns_get16(ns_rr_rdata(rr)); /* subtype */
+
+ /* Expand the name server's domain name */
+ if (ns_name_uncompress(ns_msg_base(handle),
+ ns_msg_end(handle),
+ ns_rr_rdata(rr) + 6,
+ buf,
+ MAXDNAME) < 0) {
+ ctx->report.error("%s: ns_name_uncompress", domain_name);
+ vsl->status = kafs_lookup_bad;
+ continue;
+ }
+
+ rr_ttl = ns_rr_ttl(rr);
+ if (vsl->ttl > rr_ttl)
+ vsl->ttl = rr_ttl;
+
+ server->pref = ns_get16(ns_rr_rdata(rr));
+ server->weight = ns_get16(ns_rr_rdata(rr) + 2);
+ server->port = ns_get16(ns_rr_rdata(rr) + 4);
+ verbose("rdata %u %u %u", server->pref, server->weight, server->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 < vsl->nr_servers; i++)
+ if (strcasecmp(buf, vsl->servers[i].name) == 0)
+ continue;
+
+ server->name = strdup(buf);
+ if (!server->name)
+ goto system_error;
+ server->port = AFS_VL_PORT;
+ server->protocol = protocol;
+
+ verbose("SERVER[%u] %s", vsl->nr_servers, server->name);
+ vsl->nr_servers++;
+ }
+
+ if (vsl->nr_servers > 0 && vsl->status == kafs_lookup_bad)
+ vsl->status = kafs_lookup_good_with_bad;
+ if (vsl->nr_servers == 0 && vsl->status == kafs_lookup_good_with_bad)
+ vsl->status = kafs_lookup_got_not_found;
+
+ return 0;
+
+system_error:
+ ctx->report.bad_error = true;
+ ctx->report.error("%m");
+ return -1;
+}
+
+/*
+ * Look up an SRV record to get the VL server addresses [RFC 5864].
+ */
+static int dns_query_SRV(struct kafs_server_list *vsl,
+ const char *domain_name,
+ const char *service_name,
+ const char *proto_name,
+ struct kafs_lookup_context *ctx)
+{
+ int response_len; /* buffer length */
+ ns_msg handle; /* handle for response message */
+ union {
+ HEADER hdr;
+ u_char buf[NS_PACKETSZ];
+ } response;
+ enum dns_payload_protocol_type protocol;
+ char name[1024];
+
+ vsl->source = kafs_record_from_dns_srv;
+
+ snprintf(name, sizeof(name), "_%s._%s.%s",
+ service_name, proto_name, domain_name);
+
+ verbose("Get SRV RR for name:'%s'", name);
+
+ response_len = res_nquery(&ctx->res,
+ name,
+ ns_c_in,
+ ns_t_srv,
+ response.buf,
+ sizeof(response));
+
+ if (response_len < 0) {
+ ctx->report.error("%s: dns: %s",
+ domain_name, hstrerror(h_errno));
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ vsl->status = kafs_lookup_got_not_found;
+ break;
+ case NO_RECOVERY:
+ vsl->status = kafs_lookup_got_ns_failure;
+ break;
+ case TRY_AGAIN:
+ vsl->status = kafs_lookup_got_temp_failure;
+ break;
+ }
+ return 0;
+ }
+
+ if (ns_initparse(response.buf, response_len, &handle) < 0) {
+ ctx->report.error("%s: ns_initparse: %s",
+ domain_name, hstrerror(h_errno));
+ vsl->status = kafs_lookup_bad;
+ return 0;
+ }
+
+ if (strcmp(proto_name, "udp") == 0)
+ protocol = DNS_SERVER_PROTOCOL_UDP;
+ else if (strcmp(proto_name, "tcp") == 0)
+ protocol = DNS_SERVER_PROTOCOL_TCP;
+ else
+ protocol = DNS_SERVER_PROTOCOL_UNSPECIFIED;
+
+ vsl->status = kafs_lookup_good;
+ return kafs_parse_srv(vsl, domain_name, handle, ns_s_an, protocol, ctx);
+}
+
+/*
+ * Look up a cell by name in the DNS.
+ */
+int kafs_dns_lookup_vlservers(struct kafs_server_list *vsl,
+ const char *cell_name,
+ struct kafs_lookup_context *ctx)
+{
+ int ret;
+
+ vsl->status = kafs_lookup_not_done;
+
+ if (!ctx->no_vls_srv) {
+ ret = dns_query_SRV(vsl, cell_name, "afs3-vlserver", "udp", ctx);
+ if (ret == 0 && vsl->nr_servers > 0)
+ return 0;
+ } else {
+ verbose("Use of DNS/SRV for VL server lookup is disabled.");
+ }
+
+ if (!ctx->no_vls_afsdb) {
+ ret = dns_query_AFSDB(vsl, cell_name, 1, ctx);
+ if (ret == 0 && vsl->nr_servers > 0)
+ return 0;
+ } else {
+ verbose("Use of DNS/AFSDB for VL server lookup is disabled.");
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Object creation/destruction.
+ *
+ * Copyright (C) David Howells (dhowells@redhat.com) 2018
+ *
+ * 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.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <kafs/cellserv.h>
+#include <kafs/profile.h>
+
+/*
+ * Initialise state in a lookup context.
+ */
+int kafs_init_lookup_context(struct kafs_lookup_context *ctx)
+{
+ memset(&ctx->res, 0, sizeof(ctx->res));
+ if (res_ninit(&ctx->res) < 0) {
+ ctx->report.bad_error = true;
+ ctx->report.error("%m");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Clear state in a lookup context.
+ */
+void kafs_clear_lookup_context(struct kafs_lookup_context *ctx)
+{
+ res_nclose(&ctx->res);
+}
+
+/*
+ * Allocate a blank server list.
+ */
+struct kafs_server_list *kafs_alloc_server_list(struct kafs_report *report)
+{
+ struct kafs_server_list *sl;
+
+ sl = calloc(1, sizeof(*sl));
+ if (!sl) {
+ report->bad_error = true;
+ report->error("%m");
+ return NULL;
+ }
+
+ sl->ttl = UINT_MAX;
+ return sl;
+}
+
+/*
+ * Free a server list.
+ */
+void kafs_free_server_list(struct kafs_server_list *sl)
+{
+ unsigned int i;
+
+ if (sl->servers) {
+ for (i = 0; i < sl->nr_servers; i++) {
+ struct kafs_server *s = &sl->servers[i];
+ if (!s->borrowed_name)
+ free(s->name);
+ if (!s->borrowed_addrs)
+ free(s->addrs);
+ }
+ free(sl->servers);
+ }
+
+ free(sl);
+}
+
+/*
+ * Free a cell.
+ */
+void kafs_free_cell(struct kafs_cell *cell)
+{
+ if (!cell->borrowed_name) free(cell->name);
+ if (!cell->borrowed_desc) free(cell->desc);
+ if (!cell->borrowed_realm) free(cell->realm);
+
+ if (cell->vlservers)
+ kafs_free_server_list(cell->vlservers);
+
+ free(cell);
+}
+
+/*
+ * Transfer the addresses from one server to another.
+ */
+void kafs_transfer_addresses(struct kafs_server *to,
+ const struct kafs_server *from)
+{
+ to->max_addrs = 0;
+ to->nr_addrs = from->nr_addrs;
+ to->addrs = from->addrs;
+ to->borrowed_addrs = true;
+}
+
+/*
+ * Transfer the list of servers from one server list to another.
+ */
+int kafs_transfer_server_list(struct kafs_server_list *to,
+ const struct kafs_server_list *from)
+{
+ unsigned int i, nr = from->nr_servers;
+
+ to->nr_servers = nr;
+ to->max_servers = from->max_servers;
+ to->ttl = from->ttl;
+
+ if (nr == 0) {
+ to->servers = NULL;
+ return 0;
+ }
+
+ to->servers = malloc(nr * sizeof(struct kafs_server));
+ if (!to->servers)
+ return -1;
+
+ memcpy(to->servers, from->servers, nr * sizeof(struct kafs_server));
+ for (i = 0; i < nr; i++) {
+ struct kafs_server *s = &to->servers[i];
+
+ s->borrowed_name = true;
+ s->max_addrs = 0;
+ s->nr_addrs = 0;
+ s->addrs = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Transfer information from one cell record to another.
+ */
+void kafs_transfer_cell(struct kafs_cell *to, const struct kafs_cell *from)
+{
+ if (!to->name) {
+ to->name = from->name;
+ to->borrowed_name = true;
+ }
+
+ if (from->desc) {
+ to->desc = from->desc;
+ to->borrowed_desc = true;
+ }
+
+ if (from->realm) {
+ to->realm = from->realm;
+ to->borrowed_realm = true;
+ }
+
+ to->use_dns = from->use_dns;
+ to->show_cell = from->show_cell;
+}
--- /dev/null
+/*
+ * Kerberos-style profile file parser.
+ *
+ * Copyright (C) David Howells (dhowells@redhat.com) 2018
+ *
+ * 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.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <kafs/profile.h>
+
+#define report_error(r, fmt, ...) \
+ ({ \
+ r->error(fmt, ## __VA_ARGS__); \
+ -1; \
+ })
+
+#define parse_error(r, fmt, ...) \
+ ({ \
+ r->bad_config = true; \
+ r->error("%s:%u: " fmt, r->what, r->line, ## __VA_ARGS__); \
+ -1; \
+ })
+
+/*
+ * Dump a profile to stdout in tree form.
+ */
+void kafs_profile_dump(const struct kafs_profile *p, unsigned int depth)
+{
+ unsigned int i;
+
+ if (p->type == kafs_profile_value_is_list) {
+ printf("%*s [*] '%s'%s\n",
+ depth, "",
+ p->name,
+ p->final ? " [final]" : "");
+ for (i = 0; i < p->nr_relations; i++)
+ kafs_profile_dump(p->relations[i], depth + 2);
+ } else {
+ printf("%*s [=] '%s' = '%s'\n", depth, "", p->name, p->value);
+ }
+}
+
+/*
+ * Find/create relation in the list to which we're contributing.
+ *
+ * If a list relation is already closed then it is replaced unless it is final,
+ * in which case the new stuff is ignored.
+ */
+static struct kafs_profile *kafs_profile_get_relation(struct kafs_profile *parent,
+ char *name,
+ enum kafs_profile_value_type type,
+ struct kafs_report *report)
+{
+ struct kafs_profile *r, **list = parent->relations;
+ bool dummy = false;
+ int i, n = parent->nr_relations;
+
+ if (parent->type != kafs_profile_value_is_list) {
+ report->error("%s:%u: Can't insert into a non-list",
+ report->what, report->line);
+ return NULL;
+ }
+
+ if (type == kafs_profile_value_is_list) {
+ for (i = 0; i < n; i++) {
+ r = list[i];
+ if (r->type != kafs_profile_value_is_list ||
+ strcmp(r->name, name) != 0)
+ continue;
+
+ if (r->final) {
+ dummy = true;
+ goto create;
+ }
+
+ r->final |= parent->final;
+ return r;
+ }
+ }
+
+create:
+ r = malloc(sizeof(*r));
+ if (!r)
+ return NULL;
+
+ memset(r, 0, sizeof(*r));
+ r->type = type;
+ r->name = name;
+ r->parent = parent;
+ r->dummy = dummy | parent->final | parent->dummy;
+
+ if (!r->dummy) {
+ list = realloc(list, sizeof(*list) * (n + 1));
+ if (!list)
+ return NULL;
+
+ list[n] = r;
+ parent->relations = list;
+ parent->nr_relations = n + 1;
+ }
+
+ return r;
+}
+
+/*
+ * Parse the contents of a kafs_profile file.
+ */
+static int kafs_profile_parse_content(struct kafs_profile *prof, const char *file,
+ char *p, char *end,
+ struct kafs_report *report)
+{
+ struct kafs_profile *section = NULL, *list = NULL, *tmp;
+ unsigned int line = 0;
+ char *eol, *next_line = p, *key, *value;
+ bool at_left;
+
+next_line:
+ p = next_line;
+ line++;
+ report->line = line;
+ at_left = p < end && !isblank(*p);
+ while (p < end && isblank(*p)) p++;
+ if (p == end)
+ return 0;
+ eol = strpbrk(p, "\n\r");
+ if (!eol) {
+ next_line = eol = end;
+ } else {
+ next_line = eol + 1;
+ if (next_line < end && *next_line != *eol &&
+ (*next_line == '\n' || *next_line == '\r'))
+ next_line++; /* handle CRLF and LFCR */
+ while (eol > p && isblank(eol[-1]))
+ eol--;
+ *eol = 0;
+ }
+
+ if (!*p || p[0] == '#' || p[0] == ';')
+ goto next_line;
+
+ /* Deal with section markers. */
+ if (list == section && p[0] == '[') {
+ if (eol - p < 3 || eol[-1] != ']')
+ return parse_error(report, "Bad section label");
+ p++;
+ eol--;
+ *eol = 0;
+ if (strchr(p, ']'))
+ return parse_error(report, "Bad section label");
+
+ section = kafs_profile_get_relation(prof, p, kafs_profile_value_is_list,
+ report);
+ if (!section)
+ return -1;
+ section->file = file;
+ section->line = line;
+ list = section;
+ goto next_line;
+ }
+
+ /* Things before the first section are either comments or inclusion
+ * directives.
+ */
+ if (!section) {
+ if (!at_left || strncmp(p, "include", 7) != 0)
+ goto next_line;
+ p += 7;
+
+ if (isblank(*p)) {
+ /* It's an include directive */
+ while (*p && isblank(*p)) p++;
+ if (!*p)
+ return parse_error(report, "No include path");
+
+ if (kafs_profile_parse_file(prof, p, report) < 0)
+ return -1;
+ }
+
+ if (strncmp(p, "dir", 3) == 0 && (isblank(p[3]) || !p[3])) {
+ /* It's an includedir directive */
+ p += 3;
+ while (*p && isblank(*p)) p++;
+ if (!*p)
+ return parse_error(report, "No includedir path");
+
+ if (kafs_profile_parse_dir(prof, p, report) < 0)
+ return -1;
+ }
+
+ goto next_line;
+ }
+
+ /* Deal with the closure of a list */
+ if (p[0] == '}') {
+ if (list == section)
+ return parse_error(report, "Unmatched '}'");
+ p++;
+ if (p[0] == '*') {
+ list->final = true;
+ p++;
+ }
+ if (*p)
+ return parse_error(report, "Unexpected stuff after '}'");
+
+ tmp = list;
+ list = list->parent;
+ if (tmp->dummy)
+ free(tmp);
+ goto next_line;
+ }
+
+ /* Everything else should be a relation specifier of one of the
+ * following forms:
+ *
+ * x = y
+ * x = { .. }
+ */
+ key = p;
+ p = strchr(p, '=');
+ if (!p)
+ return parse_error(report, "Missing '=' in relation");
+ if (p == key)
+ return parse_error(report, "Anonymous key in relation");
+
+ value = p + 1;
+ while (value < eol && isblank(*value)) value++;
+ p--;
+ while (p > key && isblank(p[-1]))
+ p--;
+ *p = 0;
+
+ /* Handle the opening of a new list-type relation */
+ if (value[0] == '{') {
+ if (value[1])
+ return parse_error(report, "Unexpected stuff after '{'");
+
+ list = kafs_profile_get_relation(list, key, kafs_profile_value_is_list,
+ report);
+ if (!list)
+ return -1;
+ list->file = file;
+ list->line = line;
+ goto next_line;
+ }
+
+ /* Handle a relation with a quoted-string value */
+ if (value[0] == '"') {
+ char *q;
+
+ value++;
+ if (eol <= value || eol[-1] != '"')
+ return parse_error(report, "Unterminated string");
+ eol--;
+ eol[0] = 0;
+
+ /* Substitute for all the escape chars in place */
+ for (p = q = value; p < eol;) {
+ char ch = *p++;
+ if (ch == '\\') {
+ if (p >= eol)
+ return parse_error(report, "Uncompleted '\\' escape");
+
+ ch = *p++;
+ switch (ch) {
+ case 'n': ch = '\n'; break;
+ case 't': ch = '\t'; break;
+ case 'b': ch = '\b'; break;
+ }
+ }
+ *q++ = ch;
+ }
+
+ *q = 0;
+ }
+
+ tmp = kafs_profile_get_relation(list, key, kafs_profile_value_is_string, report);
+ if (!tmp)
+ return -1;
+ tmp->file = file;
+ tmp->line = line;
+ tmp->value = value;
+ goto next_line;
+}
+
+/*
+ * Parse a kafs_profile file.
+ */
+int kafs_profile_parse_file(struct kafs_profile *prof, const char *file,
+ struct kafs_report *report)
+{
+ const char *old_file = report->what;
+ struct stat st;
+ ssize_t n;
+ char *buffer;
+ int fd, ret;
+
+ report->what = file;
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ if (fstat(fd, &st) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ buffer = malloc(st.st_size + 1);
+ if (!buffer) {
+ close(fd);
+ return -1;
+ }
+
+ n = read(fd, buffer, st.st_size);
+ close(fd);
+ if (n == -1) {
+ free(buffer);
+ return -1;
+ }
+ buffer[n] = 0;
+
+ ret = kafs_profile_parse_content(prof, file, buffer, buffer + n, report);
+ if (ret == 0)
+ report->what = old_file;
+ return ret;
+}
+
+/*
+ * Parse a kafs_profile directory.
+ */
+int kafs_profile_parse_dir(struct kafs_profile *prof,
+ const char *dirname,
+ struct kafs_report *report)
+{
+ const char *old_file = report->what;
+ struct dirent *de;
+ char *filename;
+ DIR *dir;
+ int ret, n;
+
+ report->what = dirname;
+ report->line = 0;
+ dir = opendir(dirname);
+ if (!dir)
+ return report_error(report, "%s: %m", dirname);
+
+ while (errno = 0,
+ (de = readdir(dir))) {
+ if (de->d_name[0] == '.')
+ continue;
+ n = strlen(de->d_name);
+ if (n < 1 || de->d_name[n - 1] == '~')
+ continue;
+
+ if (asprintf(&filename, "%s/%s", dirname, de->d_name) == -1) {
+ closedir(dir);
+ return report_error(report, "%m");
+ }
+
+ ret = kafs_profile_parse_file(prof, filename, report);
+ if (ret < 0) {
+ closedir(dir);
+ return -1;
+ }
+ }
+
+ report->what = dirname;
+ closedir(dir);
+ if (errno != 0)
+ return -1;
+ report->what = old_file;
+ return 0;
+}
+
+/*
+ * Find the first child object of a type and name in the list attached to the
+ * given object.
+ */
+const struct kafs_profile *kafs_profile_find_first_child(const struct kafs_profile *prof,
+ enum kafs_profile_value_type type,
+ const char *name,
+ struct kafs_report *report)
+{
+ unsigned int i;
+
+ if (prof->type != kafs_profile_value_is_list) {
+ report_error(report, "Trying to find '%s' in relation '%s'",
+ name, prof->name);
+ return NULL;
+ }
+
+ for (i = 0; i < prof->nr_relations; i++) {
+ const struct kafs_profile *r = prof->relations[i];
+
+ if (r->type == type &&
+ strcmp(r->name, name) == 0)
+ return r;
+ }
+
+ return NULL;
+}
+
+/*
+ * Iterate over all the child objects of the given type and name in the list
+ * attached to the given object until the iterator function returns non-zero.
+ */
+int kafs_profile_iterate(const struct kafs_profile *prof,
+ enum kafs_profile_value_type type,
+ const char *name,
+ kafs_profile_iterator iterator,
+ void *data,
+ struct kafs_report *report)
+{
+ unsigned int i;
+ int ret;
+
+ if (prof->type != kafs_profile_value_is_list) {
+ report_error(report, "Trying to iterate over relation '%s'",
+ prof->name);
+ return -1;
+ }
+
+ for (i = 0; i < prof->nr_relations; i++) {
+ const struct kafs_profile *r = prof->relations[i];
+
+ if (r->type != type)
+ continue;
+ if (name && strcmp(r->name, name) != 0)
+ continue;
+ ret = iterator(r, data, report);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int kafs_count_objects(const struct kafs_profile *child,
+ void *data,
+ struct kafs_report *report)
+{
+ unsigned int *_nr = data;
+
+ *_nr += 1;
+ return 0;
+}
+
+/*
+ * Count the number of matching children of an object.
+ */
+int kafs_profile_count(const struct kafs_profile *prof,
+ enum kafs_profile_value_type type,
+ const char *name,
+ unsigned int *_nr)
+{
+ return kafs_profile_iterate(prof, type, NULL, kafs_count_objects, _nr, NULL);
+}
+
+static int cmp_constant(const void *name, const void *entry)
+{
+ const struct kafs_constant_table *e = entry;
+ return strcasecmp(name, e->name);
+}
+
+/*
+ * Turn a string into a constant.
+ */
+int kafs_lookup_constant2(const struct kafs_constant_table *tbl, size_t tbl_size,
+ const char *name, int not_found)
+{
+ const struct kafs_constant_table *e;
+
+ e = bsearch(name, tbl, tbl_size, sizeof(tbl[0]), cmp_constant);
+ if (!e)
+ return not_found;
+ return e->value;
+}
+
+static const struct kafs_constant_table bool_names[] = {
+ { "0", false },
+ { "1", true },
+ { "f", false },
+ { "false", false },
+ { "n", false },
+ { "no", false },
+ { "off", false },
+ { "on", true },
+ { "t", true },
+ { "true", true },
+ { "y", true },
+ { "yes", true },
+};
+
+/*
+ * Parse a string as a bool constant.
+ */
+int kafs_lookup_bool(const char *name, int not_found)
+{
+ return kafs_lookup_constant(bool_names, name, not_found);
+}
--- /dev/null
+/*
+ * Cell preloader for kAFS filesystem.
+ *
+ * Copyright (C) David Howells (dhowells@redhat.com) 2018
+ *
+ * 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+#include <kafs/profile.h>
+#include <kafs/cellserv.h>
+
+static void verbose(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ vprintf(fmt, va);
+ putchar('\n');
+ va_end(va);
+}
+
+/*
+ * Just print an error to stderr or the syslog
+ */
+static void _error(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ if (isatty(2)) {
+ vfprintf(stderr, fmt, va);
+ fputc('\n', stderr);
+ } else {
+ vsyslog(LOG_ERR, fmt, va);
+ }
+ va_end(va);
+}
+
+/*
+ * Parse the cell database file
+ */
+int do_preload(const struct kafs_cell_db *db, bool redirect_to_stdout)
+{
+ unsigned int i;
+ char buf[4096];
+ int fd;
+
+ if (!redirect_to_stdout) {
+ fd = open("/proc/fs/afs/cells", O_WRONLY);
+ if (fd == -1) {
+ _error("Can't open /proc/fs/afs/cells: %m");
+ exit(1);
+ }
+ } else {
+ fd = 1;
+ }
+
+ for (i = 0; i < db->nr_cells; i++) {
+ const struct kafs_cell *cell = db->cells[i];
+ int n;
+
+ n = snprintf(buf, sizeof(buf) - 1, "add %s", cell->name);
+ if (write(fd, buf, n) != n) {
+ if (errno != EEXIST) {
+ _error("Can't add cell '%s': %m", cell->name);
+ exit(1);
+ }
+
+ verbose("%s: Already exists", cell->name);
+ }
+ if (redirect_to_stdout)
+ if (write(1, "\n", 1) == -1)
+ perror("stdout");
+ }
+
+ if (!redirect_to_stdout) {
+ if (close(fd) == -1) {
+ _error("Can't close /proc/fs/afs/cells: %m");
+ exit(1);
+ }
+ }
+
+ exit(0);
+}
+
+static __attribute__((noreturn))
+void usage(const char *prog)
+{
+ fprintf(stderr, "Usage: %s [-Dv] [<dbfile>*]\n", prog);
+ exit(2);
+}
+
+int main(int argc, char *argv[])
+{
+ struct kafs_report report = {};
+ const char *const *files;
+ bool redirect_to_stdout = false;
+ int opt;
+
+ if (argc > 1 && strcmp(argv[1], "--help") == 0)
+ usage(argv[0]);
+
+ while (opt = getopt(argc, argv, "Dv"),
+ opt != -1) {
+ switch (opt) {
+ case 'D':
+ redirect_to_stdout = true;
+ break;
+ case 'v':
+ if (!report.verbose)
+ report.verbose = verbose;
+ else
+ report.verbose2 = verbose;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ files = NULL;
+ if (argc > 0)
+ files = (const char **)argv;
+
+ if (kafs_init_celldb(files, &report) < 0)
+ exit(3);
+
+ do_preload(kafs_cellserv_db, redirect_to_stdout);
+ return 0;
+}
--- /dev/null
+KAFS_CLIENT_0.1 {
+ kafs_alloc_cell;
+ kafs_alloc_server_list;
+ kafs_cellserv_dump;
+ kafs_cellserv_parse_conf;
+ kafs_cellserv_profile;
+ kafs_clear_lookup_context;
+ kafs_dns_lookup_addresses;
+ kafs_dns_lookup_vlservers;
+ kafs_dump_cell;
+ kafs_dump_server_list;
+ kafs_free_cell;
+ kafs_free_server_list;
+ kafs_init_celldb;
+ kafs_init_lookup_context;
+ kafs_lookup_bool;
+ kafs_lookup_cell;
+ kafs_lookup_constant2;
+ kafs_profile_count;
+ kafs_profile_dump;
+ kafs_profile_find_first_child;
+ kafs_profile_iterate;
+ kafs_profile_parse_dir;
+ kafs_profile_parse_file;
+ kafs_transfer_addresses;
+ kafs_transfer_cell;
+ kafs_transfer_server_list;
+};