--- /dev/null
+/* Argument resolver
+ *
+ * Copyright (C) 2020 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.
+ */
+
+#include <ctype.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <fmt/format.h>
+#include <kafs/cellserv.h>
+#include "rxrpc.H"
+#include "afs_xg.H"
+#include "arg_parse.H"
+
+using rxrpc::ref;
+
+bool kafs::Fileserver_spec::is_volser_spec() const { return false; }
+bool kafs::Volserver_spec::is_volser_spec() const { return true; }
+
+/**
+ * kafs::resolve_server_spec - Resolve a volume or fileserver server spec
+ * @ctx: The cell and authentication context
+ * @vs: The server specifier
+ *
+ * Resolve a volume server or fileserver specified as a name or address into a
+ * set of addresses that can be used to contact it. The addresses are attached
+ * to a server record, which is returned.
+ */
+ref<kafs::FS_site> kafs::resolve_server_spec(Context *ctx,
+ const FVserver_spec &spec)
+{
+ ref<FS_site> site;
+ struct addrinfo *ai = NULL, *p;
+ struct servent fs_serv, vs_serv, *serv_result;
+ const char *service = spec.is_volser_spec() ? "afs3-volser" : "afs3-fileserver";
+ char *canon = NULL, buf[1024];
+ int err, n;
+
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP,
+ };
+
+ _enter("%s", spec.name.c_str());
+
+ if (getservbyname_r("afs3-fileserver", "udp",
+ &fs_serv, buf, sizeof(buf), &serv_result) < 0)
+ throw std::system_error(
+ std::error_code(errno, std::generic_category()),
+ "Failed to look up 'afs3-fileserver' service");
+ if (!serv_result)
+ throw std::runtime_error("Service 'afs3-fileserver' not found");
+
+ if (getservbyname_r("afs3-volser", "udp",
+ &vs_serv, buf, sizeof(buf), &serv_result) < 0)
+ throw std::system_error(
+ std::error_code(errno, std::generic_category()),
+ "Failed to look up 'afs3-volser' service");
+ if (!serv_result)
+ throw std::runtime_error("Service 'afs3-volser' not found");
+
+ site = new FS_site;
+
+ try {
+ err = getaddrinfo(spec.name.c_str(), service, &hints, &ai);
+ if (err != 0)
+ throw std::runtime_error(
+ fmt::format("{}: {}", spec.name, gai_strerror(err)));
+
+ /* Count the number of usable addresses */
+ n = 0;
+ for (p = ai; p; p = p->ai_next) {
+ switch (p->ai_family) {
+ case AF_INET:
+ case AF_INET6:
+ n++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ std::vector<sockaddr_rxrpc> &vs_addrs = site->vs_addrs;
+ std::vector<sockaddr_rxrpc> &fs_addrs = site->fs_addrs;
+
+ n = 0;
+ for (p = ai; p; p = p->ai_next) {
+ switch (p->ai_family) {
+ case AF_INET:
+ case AF_INET6:
+ n++;
+ break;
+ }
+ }
+ vs_addrs.resize(n);
+ fs_addrs.resize(n);
+
+ n = 0;
+ for (p = ai; p; p = p->ai_next) {
+ struct sockaddr_rxrpc &vs_srx = vs_addrs[n];
+ struct sockaddr_rxrpc &fs_srx = fs_addrs[n];
+
+ if (p->ai_canonname && !canon)
+ canon = p->ai_canonname;
+
+ fs_srx.srx_family = AF_RXRPC;
+ fs_srx.srx_service = afs::FS_SERVICE;
+ fs_srx.transport_type = p->ai_socktype;
+ fs_srx.transport_len = p->ai_addrlen;
+
+ vs_srx.srx_family = AF_RXRPC;
+ vs_srx.srx_service = afs::VOLSERVICE_ID;
+ vs_srx.transport_type = p->ai_socktype;
+ vs_srx.transport_len = p->ai_addrlen;
+
+ switch (p->ai_family) {
+ case AF_INET:
+ memcpy(&fs_srx.transport, p->ai_addr, p->ai_addrlen);
+ memcpy(&vs_srx.transport, p->ai_addr, p->ai_addrlen);
+ fs_srx.transport.sin.sin_port = fs_serv.s_port;
+ vs_srx.transport.sin.sin_port = vs_serv.s_port;
+ n++;
+ break;
+ case AF_INET6:
+ memcpy(&fs_srx.transport, p->ai_addr, p->ai_addrlen);
+ memcpy(&vs_srx.transport, p->ai_addr, p->ai_addrlen);
+ fs_srx.transport.sin6.sin6_port = fs_serv.s_port;
+ vs_srx.transport.sin6.sin6_port = vs_serv.s_port;
+ n++;
+ break;
+ }
+ }
+
+ site->name = canon ?: spec.name;
+
+ freeaddrinfo(ai);
+ _leave(" = t [%zu,%zu]", site->vs_addrs.size(), site->fs_addrs.size());
+ return site;
+ } catch (...) {
+ freeaddrinfo(ai);
+ throw;
+ }
+}