+static void
+init_addrlist(nfs_client *clp, const struct addrinfo *ai)
+{
+ int i;
+
+ if (ai == NULL)
+ return;
+
+ for (i = 0; (ai != NULL) && (i < NFSCLNT_ADDRMAX); i++) {
+ set_addrlist(clp, i, ai->ai_addr);
+ ai = ai->ai_next;
+ }
+
+ clp->m_naddr = i;
+}
+
+static void
+client_free(nfs_client *clp)
+{
+ free(clp->m_hostname);
+ free(clp);
+}
+
+static int
+init_netmask4(nfs_client *clp, const char *slash)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ };
+ uint32_t shift;
+
+ /*
+ * Decide what kind of netmask was specified. If there's
+ * no '/' present, assume the netmask is all ones. If
+ * there is a '/' and at least one '.', look for a spelled-
+ * out netmask. Otherwise, assume it was a prefixlen.
+ */
+ if (slash == NULL)
+ shift = 0;
+ else {
+ unsigned long prefixlen;
+
+ if (strchr(slash + 1, '.') != NULL) {
+ if (inet_pton(AF_INET, slash + 1,
+ &sin.sin_addr.s_addr) == 0)
+ goto out_badmask;
+ set_addrlist_in(clp, 1, &sin);
+ return 1;
+ } else {
+ char *endptr;
+
+ prefixlen = strtoul(slash + 1, &endptr, 10);
+ if (*endptr != '\0' && prefixlen != ULONG_MAX &&
+ errno != ERANGE)
+ goto out_badprefix;
+ }
+ if (prefixlen > 32)
+ goto out_badprefix;
+ shift = 32 - (uint32_t)prefixlen;
+ }
+
+ /*
+ * Now construct the full netmask bitmask in a sockaddr_in,
+ * and plant it in the nfs_client record.
+ */
+ sin.sin_addr.s_addr = htonl((uint32_t)~0 << shift);
+ set_addrlist_in(clp, 1, &sin);
+
+ return 1;
+
+out_badmask:
+ xlog(L_ERROR, "Invalid netmask `%s' for %s", slash + 1, clp->m_hostname);
+ return 0;
+
+out_badprefix:
+ xlog(L_ERROR, "Invalid prefix `%s' for %s", slash + 1, clp->m_hostname);
+ return 0;
+}
+
+#ifdef IPV6_SUPPORTED
+static int
+init_netmask6(nfs_client *clp, const char *slash)
+{
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ };
+ unsigned long prefixlen;
+ uint32_t shift;
+ int i;
+
+ /*
+ * Decide what kind of netmask was specified. If there's
+ * no '/' present, assume the netmask is all ones. If
+ * there is a '/' and at least one ':', look for a spelled-
+ * out netmask. Otherwise, assume it was a prefixlen.
+ */
+ if (slash == NULL)
+ prefixlen = 128;
+ else {
+ if (strchr(slash + 1, ':') != NULL) {
+ if (!inet_pton(AF_INET6, slash + 1, &sin6.sin6_addr))
+ goto out_badmask;
+ set_addrlist_in6(clp, 1, &sin6);
+ return 1;
+ } else {
+ char *endptr;
+
+ prefixlen = strtoul(slash + 1, &endptr, 10);
+ if (*endptr != '\0' && prefixlen != ULONG_MAX &&
+ errno != ERANGE)
+ goto out_badprefix;
+ }
+ if (prefixlen > 128)
+ goto out_badprefix;
+ }
+
+ /*
+ * Now construct the full netmask bitmask in a sockaddr_in6,
+ * and plant it in the nfs_client record.
+ */
+ for (i = 0; prefixlen > 32; i++) {
+ sin6.sin6_addr.s6_addr32[i] = 0xffffffff;
+ prefixlen -= 32;
+ }
+ shift = 32 - (uint32_t)prefixlen;
+ sin6.sin6_addr.s6_addr32[i] = htonl((uint32_t)~0 << shift);
+ set_addrlist_in6(clp, 1, &sin6);
+
+ return 1;
+
+out_badmask:
+ xlog(L_ERROR, "Invalid netmask `%s' for %s", slash + 1, clp->m_hostname);
+ return 0;
+
+out_badprefix:
+ xlog(L_ERROR, "Invalid prefix `%s' for %s", slash + 1, clp->m_hostname);
+ return 0;
+}
+#else /* IPV6_SUPPORTED */
+static int
+init_netmask6(nfs_client *UNUSED(clp), const char *UNUSED(slash))
+{
+ return 0;
+}
+#endif /* IPV6_SUPPORTED */
+
+/*
+ * Parse the network mask for M_SUBNETWORK type clients.
+ *
+ * Return TRUE if successful, or FALSE if some error occurred.
+ */
+static int
+init_subnetwork(nfs_client *clp)
+{
+ struct addrinfo *ai;
+ sa_family_t family;
+ int result = 0;
+ char *slash;
+
+ slash = strchr(clp->m_hostname, '/');
+ if (slash != NULL) {
+ *slash = '\0';
+ ai = host_pton(clp->m_hostname);
+ *slash = '/';
+ } else
+ ai = host_pton(clp->m_hostname);
+ if (ai == NULL) {
+ xlog(L_ERROR, "Invalid IP address %s", clp->m_hostname);
+ return result;
+ }
+
+ set_addrlist(clp, 0, ai->ai_addr);
+ family = ai->ai_addr->sa_family;
+
+ freeaddrinfo(ai);
+
+ switch (family) {
+ case AF_INET:
+ result = init_netmask4(clp, slash);
+ break;
+ case AF_INET6:
+ result = init_netmask6(clp, slash);
+ break;
+ default:
+ xlog(L_ERROR, "Unsupported address family for %s",
+ clp->m_hostname);
+ }
+
+ return result;
+}
+
+static int
+client_init(nfs_client *clp, const char *hname, const struct addrinfo *ai)
+{
+ clp->m_hostname = strdup(hname);
+ if (clp->m_hostname == NULL)
+ return 0;
+
+ clp->m_exported = 0;
+ clp->m_count = 0;
+ clp->m_naddr = 0;
+
+ if (clp->m_type == MCL_SUBNETWORK)
+ return init_subnetwork(clp);
+
+ init_addrlist(clp, ai);
+ return 1;
+}
+
+static void
+client_add(nfs_client *clp)
+{
+ nfs_client **cpp;
+
+ cpp = &clientlist[clp->m_type];
+ while (*cpp != NULL)
+ cpp = &((*cpp)->m_next);
+ clp->m_next = NULL;
+ *cpp = clp;
+}
+
+/**
+ * client_lookup - look for @hname in our list of cached nfs_clients
+ * @hname: '\0'-terminated ASCII string containing hostname to look for
+ * @canonical: if set, @hname is known to be canonical DNS name
+ *
+ * Returns pointer to a matching or freshly created nfs_client. NULL
+ * is returned if some problem occurs.