diff --git a/nfs-utils-1-2-3-rc3.patch b/nfs-utils-1-2-3-rc3.patch deleted file mode 100644 index ae27e6b..0000000 --- a/nfs-utils-1-2-3-rc3.patch +++ /dev/null @@ -1,2109 +0,0 @@ -diff -up nfs-utils-1.2.2/aclocal/libcap.m4.orig nfs-utils-1.2.2/aclocal/libcap.m4 ---- nfs-utils-1.2.2/aclocal/libcap.m4.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/aclocal/libcap.m4 2010-05-06 07:16:37.851057000 -0400 -@@ -5,11 +5,19 @@ AC_DEFUN([AC_LIBCAP], [ - dnl look for prctl - AC_CHECK_FUNC([prctl], , ) - -- dnl look for the library; do not add to LIBS if found -- AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) -- AC_SUBST(LIBCAP) -+ AC_ARG_ENABLE([caps], -+ [AS_HELP_STRING([--disable-caps], [Disable capabilities support])]) -+ -+ LIBCAP= -+ -+ if test "x$enable_caps" != "xno" ; then -+ dnl look for the library; do not add to LIBS if found -+ AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) - -- AC_CHECK_HEADERS([sys/capability.h], , -- [AC_MSG_ERROR([libcap headers not found.])]) -+ AC_CHECK_HEADERS([sys/capability.h], , -+ [test "x$enable_caps" = "xyes" && AC_MSG_ERROR([libcap headers not found.])]) -+ fi -+ -+ AC_SUBST(LIBCAP) - - ])dnl -diff -up nfs-utils-1.2.2/configure.ac.orig nfs-utils-1.2.2/configure.ac ---- nfs-utils-1.2.2/configure.ac.orig 2010-05-06 07:15:49.881176000 -0400 -+++ nfs-utils-1.2.2/configure.ac 2010-05-06 07:16:37.856060000 -0400 -@@ -89,7 +89,7 @@ AC_ARG_ENABLE(nfsv41, - if test "$enable_nfsv41" = yes; then - AC_DEFINE(NFS41_SUPPORTED, 1, [Define this if you want NFSv41 support compiled in]) - else -- enable_nfsv4= -+ enable_nfsv41= - fi - AC_SUBST(enable_nfsv41) - AM_CONDITIONAL(CONFIG_NFSV41, [test "$enable_nfsv41" = "yes"]) -@@ -436,6 +436,8 @@ AC_CONFIG_FILES([ - tools/nlmtest/Makefile - tools/rpcdebug/Makefile - tools/rpcgen/Makefile -+ tools/mountstats/Makefile -+ tools/nfs-iostat/Makefile - utils/Makefile - utils/exportfs/Makefile - utils/gssd/Makefile -diff -up nfs-utils-1.2.2/support/export/client.c.orig nfs-utils-1.2.2/support/export/client.c ---- nfs-utils-1.2.2/support/export/client.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/support/export/client.c 2010-05-06 07:16:37.862057000 -0400 -@@ -17,7 +17,7 @@ - #include - #include - #include --#include "xmalloc.h" -+ - #include "misc.h" - #include "nfslib.h" - #include "exportfs.h" -@@ -28,13 +28,116 @@ - #if !defined(__GLIBC__) || __GLIBC__ < 2 - extern int innetgr(char *netgr, char *host, char *, char *); - #endif --static void client_init(nfs_client *clp, const char *hname, -- struct hostent *hp); --static int client_checkaddr(nfs_client *clp, struct in_addr addr); -+ -+static char *add_name(char *old, const char *add); - - nfs_client *clientlist[MCL_MAXTYPES] = { NULL, }; - - -+static void -+init_addrlist(nfs_client *clp, const struct hostent *hp) -+{ -+ struct sockaddr_in sin = { -+ .sin_family = AF_INET, -+ }; -+ char **ap; -+ int i; -+ -+ if (hp == NULL) -+ return; -+ -+ ap = hp->h_addr_list; -+ for (i = 0; *ap != NULL && i < NFSCLNT_ADDRMAX; i++, ap++) { -+ sin.sin_addr = *(struct in_addr *)*ap; -+ set_addrlist_in(clp, i, &sin); -+ } -+ clp->m_naddr = i; -+} -+ -+static void -+client_free(nfs_client *clp) -+{ -+ free(clp->m_hostname); -+ free(clp); -+} -+ -+static int -+init_netmask(nfs_client *clp, const char *slash) -+{ -+ struct sockaddr_in sin = { -+ .sin_family = AF_INET, -+ }; -+ -+ if (strchr(slash + 1, '.') != NULL) -+ sin.sin_addr.s_addr = inet_addr(slash + 1); -+ else { -+ int prefixlen = atoi(slash + 1); -+ if (0 < prefixlen && prefixlen <= 32) -+ sin.sin_addr.s_addr = -+ htonl((uint32_t)~0 << (32 - prefixlen)); -+ else -+ goto out_badprefix; -+ } -+ -+ set_addrlist_in(clp, 1, &sin); -+ return 1; -+ -+out_badprefix: -+ xlog(L_ERROR, "Invalid prefix `%s' for %s", slash + 1, clp->m_hostname); -+ return 0; -+} -+ -+static int -+init_subnetwork(nfs_client *clp) -+{ -+ struct sockaddr_in sin = { -+ .sin_family = AF_INET, -+ }; -+ static char slash32[] = "/32"; -+ char *cp; -+ -+ cp = strchr(clp->m_hostname, '/'); -+ if (cp == NULL) -+ cp = slash32; -+ -+ *cp = '\0'; -+ sin.sin_addr.s_addr = inet_addr(clp->m_hostname); -+ set_addrlist_in(clp, 0, &sin); -+ *cp = '/'; -+ -+ return init_netmask(clp, cp); -+} -+ -+static int -+client_init(nfs_client *clp, const char *hname, const struct hostent *hp) -+{ -+ 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, hp); -+ 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; -+} -+ - /* if canonical is set, then we *know* this is already a canonical name - * so hostname lookup is avoided. - * This is used when reading /proc/fs/nfs/exports -@@ -87,23 +190,23 @@ client_lookup(char *hname, int canonical - } - } - -- if (!clp) { -- clp = (nfs_client *) xmalloc(sizeof(*clp)); -- memset(clp, 0, sizeof(*clp)); -+ if (clp == NULL) { -+ clp = calloc(1, sizeof(*clp)); -+ if (clp == NULL) -+ goto out; - clp->m_type = htype; -- client_init(clp, hname, NULL); -+ if (!client_init(clp, hname, NULL)) { -+ client_free(clp); -+ clp = NULL; -+ goto out; -+ } - client_add(clp); - } - -- if (htype == MCL_FQDN && clp->m_naddr == 0 && hp != NULL) { -- char **ap = hp->h_addr_list; -- int i; -- -- for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) -- clp->m_addrlist[i] = *(struct in_addr *)*ap; -- clp->m_naddr = i; -- } -+ if (htype == MCL_FQDN && clp->m_naddr == 0) -+ init_addrlist(clp, hp); - -+out: - if (hp) - free (hp); - -@@ -115,78 +218,21 @@ client_dup(nfs_client *clp, struct hoste - { - nfs_client *new; - -- new = (nfs_client *) xmalloc(sizeof(*new)); -+ new = (nfs_client *)malloc(sizeof(*new)); -+ if (new == NULL) -+ return NULL; - memcpy(new, clp, sizeof(*new)); - new->m_type = MCL_FQDN; - new->m_hostname = NULL; - -- client_init(new, (char *) hp->h_name, hp); -+ if (!client_init(new, hp->h_name, hp)) { -+ client_free(new); -+ return NULL; -+ } - client_add(new); - return new; - } - --static void --client_init(nfs_client *clp, const char *hname, struct hostent *hp) --{ -- xfree(clp->m_hostname); -- if (hp) -- clp->m_hostname = xstrdup(hp->h_name); -- else -- clp->m_hostname = xstrdup(hname); -- -- clp->m_exported = 0; -- clp->m_count = 0; -- -- if (clp->m_type == MCL_SUBNETWORK) { -- char *cp = strchr(clp->m_hostname, '/'); -- static char slash32[] = "/32"; -- -- if(!cp) cp = slash32; -- *cp = '\0'; -- clp->m_addrlist[0].s_addr = inet_addr(clp->m_hostname); -- if (strchr(cp + 1, '.')) { -- clp->m_addrlist[1].s_addr = inet_addr(cp+1); -- } -- else { -- int netmask = atoi(cp + 1); -- if (0 < netmask && netmask <= 32) { -- clp->m_addrlist[1].s_addr = -- htonl ((uint32_t) ~0 << (32 - netmask)); -- } -- else { -- xlog(L_FATAL, "invalid netmask `%s' for %s", -- cp + 1, clp->m_hostname); -- } -- } -- *cp = '/'; -- clp->m_naddr = 0; -- } else if (!hp) { -- clp->m_naddr = 0; -- } else { -- char **ap = hp->h_addr_list; -- int i; -- -- for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) { -- clp->m_addrlist[i] = *(struct in_addr *)*ap; -- } -- clp->m_naddr = i; -- } --} -- --void --client_add(nfs_client *clp) --{ -- nfs_client **cpp; -- -- if (clp->m_type < 0 || clp->m_type >= MCL_MAXTYPES) -- xlog(L_FATAL, "unknown client type in client_add"); -- cpp = clientlist + clp->m_type; -- while (*cpp) -- cpp = &((*cpp)->m_next); -- clp->m_next = NULL; -- *cpp = clp; --} -- - void - client_release(nfs_client *clp) - { -@@ -205,34 +251,11 @@ client_freeall(void) - head = clientlist + i; - while (*head) { - *head = (clp = *head)->m_next; -- xfree(clp->m_hostname); -- xfree(clp); -+ client_free(clp); - } - } - } - --nfs_client * --client_find(struct hostent *hp) --{ -- nfs_client *clp; -- int i; -- -- for (i = 0; i < MCL_MAXTYPES; i++) { -- for (clp = clientlist[i]; clp; clp = clp->m_next) { -- if (!client_check(clp, hp)) -- continue; --#ifdef notdef -- if (clp->m_type == MCL_FQDN) -- return clp; -- return client_dup(clp, hp); --#else -- return clp; --#endif -- } -- } -- return NULL; --} -- - struct hostent * - client_resolve(struct in_addr addr) - { -@@ -246,14 +269,18 @@ client_resolve(struct in_addr addr) - return he; - } - --/* -- * Find client name given an IP address -- * This is found by gathering all known names that match that IP address, -- * sorting them and joining them with '+' -+/** -+ * client_compose - Make a list of cached hostnames that match an IP address -+ * @he: pointer to hostent containing IP address information to match -+ * -+ * Gather all known client hostnames that match the IP address, and sort -+ * the result into a comma-separated list. - * -+ * Returns a '\0'-terminated ASCII string containing a comma-separated -+ * sorted list of client hostnames, or NULL if no client records matched -+ * the IP address or memory could not be allocated. Caller must free the -+ * returned string with free(3). - */ --static char *add_name(char *old, char *add); -- - char * - client_compose(struct hostent *he) - { -@@ -271,13 +298,19 @@ client_compose(struct hostent *he) - return name; - } - -+/** -+ * client_member - check if @name is contained in the list @client -+ * @client: '\0'-terminated ASCII string containing -+ * comma-separated list of hostnames -+ * @name: '\0'-terminated ASCII string containing hostname to look for -+ * -+ * Returns 1 if @name was found in @client, otherwise zero is returned. -+ */ - int --client_member(char *client, char *name) -+client_member(const char *client, const char *name) - { -- /* check if "client" (a ',' separated list of names) -- * contains 'name' as a member -- */ -- int l = strlen(name); -+ size_t l = strlen(name); -+ - while (*client) { - if (strncmp(client, name, l) == 0 && - (client[l] == ',' || client[l] == '\0')) -@@ -290,9 +323,8 @@ client_member(char *client, char *name) - return 0; - } - -- --int --name_cmp(char *a, char *b) -+static int -+name_cmp(const char *a, const char *b) - { - /* compare strings a and b, but only upto ',' in a */ - while (*a && *b && *a != ',' && *a == *b) -@@ -305,9 +337,9 @@ name_cmp(char *a, char *b) - } - - static char * --add_name(char *old, char *add) -+add_name(char *old, const char *add) - { -- int len = strlen(add)+2; -+ size_t len = strlen(add) + 2; - char *new; - char *cp; - if (old) len += strlen(old); -@@ -340,101 +372,164 @@ add_name(char *old, char *add) - } - - /* -- * Match a host (given its hostent record) to a client record. This -- * is usually called from mountd. -+ * Check each address listed in @hp against each address -+ * stored in @clp. Return 1 if a match is found, otherwise -+ * zero. - */ --int --client_check(nfs_client *clp, struct hostent *hp) -+static int -+check_fqdn(const nfs_client *clp, const struct hostent *hp) - { -- char *hname = (char *) hp->h_name; -- char *cname = clp->m_hostname; -- char **ap; -+ const struct sockaddr_in *sin; -+ struct in_addr addr; -+ char **ap; -+ int i; - -- switch (clp->m_type) { -- case MCL_FQDN: -- case MCL_SUBNETWORK: -- for (ap = hp->h_addr_list; *ap; ap++) { -- if (client_checkaddr(clp, *(struct in_addr *) *ap)) -+ for (ap = hp->h_addr_list; *ap; ap++) { -+ addr = *(struct in_addr *)*ap; -+ -+ for (i = 0; i < clp->m_naddr; i++) { -+ sin = get_addrlist_in(clp, i); -+ if (sin->sin_addr.s_addr == addr.s_addr) - return 1; - } -- return 0; -- case MCL_WILDCARD: -- if (wildmat(hname, cname)) -- return 1; -- else { -- for (ap = hp->h_aliases; *ap; ap++) -- if (wildmat(*ap, cname)) -- return 1; -- } -- return 0; -- case MCL_NETGROUP: --#ifdef HAVE_INNETGR -- { -- char *dot; -- int match, i; -- struct hostent *nhp = NULL; -- struct sockaddr_in addr; -- -- /* First, try to match the hostname without -- * splitting off the domain */ -- if (innetgr(cname+1, hname, NULL, NULL)) -- return 1; -+ } -+ return 0; -+} - -- /* try the aliases as well */ -- for (i = 0; hp->h_aliases[i]; i++) { -- if (innetgr(cname+1, hp->h_aliases[i], NULL, NULL)) -- return 1; -- } -+/* -+ * Check each address listed in @hp against the subnetwork or -+ * host address stored in @clp. Return 1 if an address in @hp -+ * matches the host address stored in @clp, otherwise zero. -+ */ -+static int -+check_subnetwork(const nfs_client *clp, const struct hostent *hp) -+{ -+ const struct sockaddr_in *address, *mask; -+ struct in_addr addr; -+ char **ap; -+ -+ for (ap = hp->h_addr_list; *ap; ap++) { -+ address = get_addrlist_in(clp, 0); -+ mask = get_addrlist_in(clp, 1); -+ addr = *(struct in_addr *)*ap; - -- /* If hname is ip address convert to FQDN */ -- if (inet_aton(hname, &addr.sin_addr) && -- (nhp = gethostbyaddr((const char *)&(addr.sin_addr), -- sizeof(addr.sin_addr), AF_INET))) { -- hname = (char *)nhp->h_name; -- if (innetgr(cname+1, hname, NULL, NULL)) -- return 1; -- } -+ if (!((address->sin_addr.s_addr ^ addr.s_addr) & -+ mask->sin_addr.s_addr)) -+ return 1; -+ } -+ return 0; -+} - -- /* Okay, strip off the domain (if we have one) */ -- if ((dot = strchr(hname, '.')) == NULL) -- return 0; -- -- *dot = '\0'; -- match = innetgr(cname+1, hname, NULL, NULL); -- *dot = '.'; -+/* -+ * Check if a wildcard nfs_client record matches the canonical name -+ * or the aliases of a host. Return 1 if a match is found, otherwise -+ * zero. -+ */ -+static int -+check_wildcard(const nfs_client *clp, const struct hostent *hp) -+{ -+ char *cname = clp->m_hostname; -+ char *hname = hp->h_name; -+ char **ap; - -- return match; -- } --#else -- return 0; --#endif -- case MCL_ANONYMOUS: -+ if (wildmat(hname, cname)) - return 1; -- case MCL_GSS: -- return 0; -- default: -- xlog(L_FATAL, "internal: bad client type %d", clp->m_type); -+ -+ /* See if hname aliases listed in /etc/hosts or nis[+] -+ * match the requested wildcard */ -+ for (ap = hp->h_aliases; *ap; ap++) { -+ if (wildmat(*ap, cname)) -+ return 1; - } - - return 0; - } - -+/* -+ * Check if @hp's hostname or aliases fall in a given netgroup. -+ * Return 1 if @hp represents a host in the netgroup, otherwise zero. -+ */ -+#ifdef HAVE_INNETGR -+static int -+check_netgroup(const nfs_client *clp, const struct hostent *hp) -+{ -+ const char *netgroup = clp->m_hostname + 1; -+ const char *hname = hp->h_name; -+ struct hostent *nhp = NULL; -+ struct sockaddr_in addr; -+ int match, i; -+ char *dot; -+ -+ /* First, try to match the hostname without -+ * splitting off the domain */ -+ if (innetgr(netgroup, hname, NULL, NULL)) -+ return 1; -+ -+ /* See if hname aliases listed in /etc/hosts or nis[+] -+ * match the requested netgroup */ -+ for (i = 0; hp->h_aliases[i]; i++) { -+ if (innetgr(netgroup, hp->h_aliases[i], NULL, NULL)) -+ return 1; -+ } -+ -+ /* If hname is ip address convert to FQDN */ -+ if (inet_aton(hname, &addr.sin_addr) && -+ (nhp = gethostbyaddr((const char *)&(addr.sin_addr), -+ sizeof(addr.sin_addr), AF_INET))) { -+ hname = nhp->h_name; -+ if (innetgr(netgroup, hname, NULL, NULL)) -+ return 1; -+ } -+ -+ /* Okay, strip off the domain (if we have one) */ -+ dot = strchr(hname, '.'); -+ if (dot == NULL) -+ return 0; -+ -+ *dot = '\0'; -+ match = innetgr(netgroup, hname, NULL, NULL); -+ *dot = '.'; -+ -+ return match; -+} -+#else /* !HAVE_INNETGR */ - static int --client_checkaddr(nfs_client *clp, struct in_addr addr) -+check_netgroup(__attribute__((unused)) const nfs_client *clp, -+ __attribute__((unused)) const struct hostent *hp) - { -- int i; -+ return 0; -+} -+#endif /* !HAVE_INNETGR */ - -+/** -+ * client_check - check if IP address information matches a cached nfs_client -+ * @clp: pointer to a cached nfs_client record -+ * @hp: pointer to hostent containing host IP information -+ * -+ * Returns 1 if the address information matches the cached nfs_client, -+ * otherwise zero. -+ */ -+int -+client_check(nfs_client *clp, struct hostent *hp) -+{ - switch (clp->m_type) { - case MCL_FQDN: -- for (i = 0; i < clp->m_naddr; i++) { -- if (clp->m_addrlist[i].s_addr == addr.s_addr) -- return 1; -- } -- return 0; -+ return check_fqdn(clp, hp); - case MCL_SUBNETWORK: -- return !((clp->m_addrlist[0].s_addr ^ addr.s_addr) -- & clp->m_addrlist[1].s_addr); -+ return check_subnetwork(clp, hp); -+ case MCL_WILDCARD: -+ return check_wildcard(clp, hp); -+ case MCL_NETGROUP: -+ return check_netgroup(clp, hp); -+ case MCL_ANONYMOUS: -+ return 1; -+ case MCL_GSS: -+ return 0; -+ default: -+ xlog(D_GENERAL, "%s: unrecognized client type: %d", -+ __func__, clp->m_type); - } -+ - return 0; - } - -diff -up nfs-utils-1.2.2/support/export/export.c.orig nfs-utils-1.2.2/support/export/export.c ---- nfs-utils-1.2.2/support/export/export.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/support/export/export.c 2010-05-06 07:16:37.867057000 -0400 -@@ -28,6 +28,18 @@ static int export_check(nfs_export *, st - static nfs_export * - export_allowed_internal(struct hostent *hp, char *path); - -+static void -+export_free(nfs_export *exp) -+{ -+ xfree(exp->m_export.e_squids); -+ xfree(exp->m_export.e_sqgids); -+ free(exp->m_export.e_mountpoint); -+ free(exp->m_export.e_fslocdata); -+ -+ xfree(exp->m_export.e_hostname); -+ xfree(exp); -+} -+ - static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep) - { - if (exp->m_export.e_flags != eep->e_flags) { -@@ -117,6 +129,10 @@ export_dup(nfs_export *exp, struct hoste - if (exp->m_export.e_hostname) - new->m_export.e_hostname = xstrdup(exp->m_export.e_hostname); - clp = client_dup(exp->m_client, hp); -+ if (clp == NULL) { -+ export_free(new); -+ return NULL; -+ } - clp->m_count++; - new->m_client = clp; - new->m_mayexport = exp->m_mayexport; -@@ -128,6 +144,7 @@ export_dup(nfs_export *exp, struct hoste - - return new; - } -+ - /* - * Add export entry to hash table - */ -@@ -259,6 +276,10 @@ export_check(nfs_export *exp, struct hos - return client_check(exp->m_client, hp); - } - -+/** -+ * export_freeall - deallocate all nfs_export records -+ * -+ */ - void - export_freeall(void) - { -@@ -269,22 +290,13 @@ export_freeall(void) - for (exp = exportlist[i].p_head; exp; exp = nxt) { - nxt = exp->m_next; - client_release(exp->m_client); -- if (exp->m_export.e_squids) -- xfree(exp->m_export.e_squids); -- if (exp->m_export.e_sqgids) -- xfree(exp->m_export.e_sqgids); -- if (exp->m_export.e_mountpoint) -- free(exp->m_export.e_mountpoint); -- if (exp->m_export.e_fslocdata) -- xfree(exp->m_export.e_fslocdata); -- xfree(exp->m_export.e_hostname); -- xfree(exp); -+ export_free(exp); -+ } -+ for (j = 0; j < HASH_TABLE_SIZE; j++) { -+ exportlist[i].entries[j].p_first = NULL; -+ exportlist[i].entries[j].p_last = NULL; - } -- for(j = 0; j < HASH_TABLE_SIZE; j++) { -- exportlist[i].entries[j].p_first = NULL; -- exportlist[i].entries[j].p_last = NULL; -- } -- exportlist[i].p_head = NULL; -+ exportlist[i].p_head = NULL; - } - client_freeall(); - } -diff -up nfs-utils-1.2.2/support/export/nfsctl.c.orig nfs-utils-1.2.2/support/export/nfsctl.c ---- nfs-utils-1.2.2/support/export/nfsctl.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/support/export/nfsctl.c 2010-05-06 07:16:37.872057000 -0400 -@@ -66,7 +66,7 @@ str_tolower(char *s) - static int - cltsetup(struct nfsctl_client *cltarg, nfs_client *clp) - { -- int i; -+ int i, j; - - if (clp->m_type != MCL_FQDN) { - xlog(L_ERROR, "internal: can't export non-FQDN host"); -@@ -76,10 +76,19 @@ cltsetup(struct nfsctl_client *cltarg, n - strncpy(cltarg->cl_ident, clp->m_hostname, - sizeof (cltarg->cl_ident) - 1); - str_tolower(cltarg->cl_ident); -- cltarg->cl_naddr = clp->m_naddr; -- for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) -- cltarg->cl_addrlist[i] = clp->m_addrlist[i]; - -+ j = 0; -+ for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) { -+ struct sockaddr_in *sin = get_addrlist_in(clp, i); -+ if (sin->sin_family == AF_INET) -+ cltarg->cl_addrlist[j++] = sin->sin_addr; -+ } -+ if (j == 0) { -+ xlog(L_ERROR, "internal: no supported addresses in nfs_client"); -+ return 0; -+ } -+ -+ cltarg->cl_naddr = j; - return 1; - } - -diff -up nfs-utils-1.2.2/support/include/exportfs.h.orig nfs-utils-1.2.2/support/include/exportfs.h ---- nfs-utils-1.2.2/support/include/exportfs.h.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/support/include/exportfs.h 2010-05-06 07:16:37.877057000 -0400 -@@ -10,6 +10,8 @@ - #define EXPORTFS_H - - #include -+ -+#include "sockaddr.h" - #include "nfslib.h" - - enum { -@@ -35,11 +37,56 @@ typedef struct mclient { - char * m_hostname; - int m_type; - int m_naddr; -- struct in_addr m_addrlist[NFSCLNT_ADDRMAX]; -+ union nfs_sockaddr m_addrlist[NFSCLNT_ADDRMAX]; - int m_exported; /* exported to nfsd */ - int m_count; - } nfs_client; - -+static inline const struct sockaddr * -+get_addrlist(const nfs_client *clp, const int i) -+{ -+ return &clp->m_addrlist[i].sa; -+} -+ -+static inline const struct sockaddr_in * -+get_addrlist_in(const nfs_client *clp, const int i) -+{ -+ return &clp->m_addrlist[i].s4; -+} -+ -+static inline const struct sockaddr_in6 * -+get_addrlist_in6(const nfs_client *clp, const int i) -+{ -+ return &clp->m_addrlist[i].s6; -+} -+ -+static inline void -+set_addrlist_in(nfs_client *clp, const int i, const struct sockaddr_in *sin) -+{ -+ memcpy(&clp->m_addrlist[i].s4, sin, sizeof(*sin)); -+} -+ -+static inline void -+set_addrlist_in6(nfs_client *clp, const int i, const struct sockaddr_in6 *sin6) -+{ -+ memcpy(&clp->m_addrlist[i].s6, sin6, sizeof(*sin6)); -+} -+ -+static inline void -+set_addrlist(nfs_client *clp, const int i, const struct sockaddr *sap) -+{ -+ switch (sap->sa_family) { -+ case AF_INET: -+ memcpy(&clp->m_addrlist[i].s4, sap, sizeof(struct sockaddr_in)); -+ break; -+#ifdef IPV6_SUPPORTED -+ case AF_INET6: -+ memcpy(&clp->m_addrlist[i].s6, sap, sizeof(struct sockaddr_in6)); -+ break; -+#endif -+ } -+} -+ - typedef struct mexport { - struct mexport * m_next; - struct mclient * m_client; -@@ -69,17 +116,15 @@ extern exp_hash_table exportlist[MCL_MAX - extern nfs_client * clientlist[MCL_MAXTYPES]; - - nfs_client * client_lookup(char *hname, int canonical); --nfs_client * client_find(struct hostent *); --void client_add(nfs_client *); - nfs_client * client_dup(nfs_client *, struct hostent *); - int client_gettype(char *hname); - int client_check(nfs_client *, struct hostent *); --int client_match(nfs_client *, char *hname); - void client_release(nfs_client *); - void client_freeall(void); - char * client_compose(struct hostent *he); - struct hostent * client_resolve(struct in_addr addr); --int client_member(char *client, char *name); -+int client_member(const char *client, -+ const char *name); - - int export_read(char *fname); - void export_add(nfs_export *); -diff -up nfs-utils-1.2.2/support/include/nfslib.h.orig nfs-utils-1.2.2/support/include/nfslib.h ---- nfs-utils-1.2.2/support/include/nfslib.h.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/support/include/nfslib.h 2010-05-06 07:16:37.882059000 -0400 -@@ -152,6 +152,8 @@ void qword_addhex(char **bpp, int *lp, c - void qword_addint(char **bpp, int *lp, int n); - void qword_adduint(char **bpp, int *lp, unsigned int n); - void qword_addeol(char **bpp, int *lp); -+int qword_get_uint(char **bpp, unsigned int *anint); -+void qword_printuint(FILE *f, unsigned int num); - - void closeall(int min); - -diff -up nfs-utils-1.2.2/support/include/nfsrpc.h.orig nfs-utils-1.2.2/support/include/nfsrpc.h ---- nfs-utils-1.2.2/support/include/nfsrpc.h.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/support/include/nfsrpc.h 2010-05-06 07:16:37.888057000 -0400 -@@ -160,4 +160,7 @@ extern int nfs_rpc_ping(const struct so - const unsigned short protocol, - const struct timeval *timeout); - -+/* create AUTH_SYS handle with no supplemental groups */ -+extern AUTH * nfs_authsys_create(void); -+ - #endif /* !__NFS_UTILS_NFSRPC_H */ -diff -up nfs-utils-1.2.2/support/nfs/cacheio.c.orig nfs-utils-1.2.2/support/nfs/cacheio.c ---- nfs-utils-1.2.2/support/nfs/cacheio.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/support/nfs/cacheio.c 2010-05-06 07:16:37.893057000 -0400 -@@ -148,6 +148,11 @@ void qword_printint(FILE *f, int num) - fprintf(f, "%d ", num); - } - -+void qword_printuint(FILE *f, unsigned int num) -+{ -+ fprintf(f, "%u ", num); -+} -+ - int qword_eol(FILE *f) - { - int err; -@@ -236,6 +241,20 @@ int qword_get_int(char **bpp, int *anint - return 0; - } - -+int qword_get_uint(char **bpp, unsigned int *anint) -+{ -+ char buf[50]; -+ char *ep; -+ unsigned int rv; -+ int len = qword_get(bpp, buf, 50); -+ if (len < 0) return -1; -+ if (len ==0) return -1; -+ rv = strtoul(buf, &ep, 0); -+ if (*ep) return -1; -+ *anint = rv; -+ return 0; -+} -+ - #define READLINE_BUFFER_INCREMENT 2048 - - int readline(int fd, char **buf, int *lenp) -diff -up nfs-utils-1.2.2/support/nfs/rpc_socket.c.orig nfs-utils-1.2.2/support/nfs/rpc_socket.c ---- nfs-utils-1.2.2/support/nfs/rpc_socket.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/support/nfs/rpc_socket.c 2010-05-06 07:16:37.898057000 -0400 -@@ -557,3 +557,24 @@ rpcprog_t nfs_getrpcbyname(const rpcprog - - return program; - } -+ -+/* -+ * AUTH_SYS doesn't allow more than 16 gids in the supplemental group list. -+ * If there are more than that, trying to determine which ones to include -+ * in the list is problematic. This function creates an auth handle that -+ * only has the primary gid in the supplemental gids list. It's intended to -+ * be used for protocols where credentials really don't matter much (the MNT -+ * protocol, for instance). -+ */ -+AUTH * -+nfs_authsys_create(void) -+{ -+ char machname[MAXHOSTNAMELEN + 1]; -+ uid_t uid = geteuid(); -+ gid_t gid = getegid(); -+ -+ if (gethostname(machname, sizeof(machname)) == -1) -+ return NULL; -+ -+ return authunix_create(machname, uid, gid, 1, &gid); -+} -diff -up nfs-utils-1.2.2/support/nsm/file.c.orig nfs-utils-1.2.2/support/nsm/file.c ---- nfs-utils-1.2.2/support/nsm/file.c.orig 2010-05-06 07:15:49.888166000 -0400 -+++ nfs-utils-1.2.2/support/nsm/file.c 2010-05-06 07:16:37.917057000 -0400 -@@ -67,7 +67,9 @@ - #endif - - #include -+#ifdef HAVE_SYS_CAPABILITY_H - #include -+#endif - #include - #include - -@@ -348,6 +350,7 @@ nsm_is_default_parentdir(void) - static _Bool - nsm_clear_capabilities(void) - { -+#ifdef HAVE_SYS_CAPABILITY_H - cap_t caps; - - caps = cap_from_text("cap_net_bind_service=ep"); -@@ -363,6 +366,7 @@ nsm_clear_capabilities(void) - } - - (void)cap_free(caps); -+#endif - return true; - } - -diff -up nfs-utils-1.2.2/tools/Makefile.am.orig nfs-utils-1.2.2/tools/Makefile.am ---- nfs-utils-1.2.2/tools/Makefile.am.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/tools/Makefile.am 2010-05-06 07:16:37.923062000 -0400 -@@ -6,6 +6,6 @@ if CONFIG_RPCGEN - OPTDIRS += rpcgen - endif - --SUBDIRS = locktest rpcdebug nlmtest $(OPTDIRS) -+SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat $(OPTDIRS) - - MAINTAINERCLEANFILES = Makefile.in -diff -up nfs-utils-1.2.2/tools/mountstats/Makefile.am.orig nfs-utils-1.2.2/tools/mountstats/Makefile.am ---- nfs-utils-1.2.2/tools/mountstats/Makefile.am.orig 2010-05-06 07:16:37.939040000 -0400 -+++ nfs-utils-1.2.2/tools/mountstats/Makefile.am 2010-05-06 07:16:37.941038000 -0400 -@@ -0,0 +1,13 @@ -+## Process this file with automake to produce Makefile.in -+PYTHON_FILES = mountstats.py -+ -+man8_MANS = mountstats.man -+ -+EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) -+ -+all-local: $(PYTHON_FILES) -+ -+install-data-hook: -+ $(INSTALL) --mode 755 mountstats.py $(DESTDIR)$(sbindir)/mountstats -+ -+MAINTAINERCLEANFILES=Makefile.in -diff -up nfs-utils-1.2.2/tools/mountstats/mountstats.man.orig nfs-utils-1.2.2/tools/mountstats/mountstats.man ---- nfs-utils-1.2.2/tools/mountstats/mountstats.man.orig 2010-05-06 07:16:37.944036000 -0400 -+++ nfs-utils-1.2.2/tools/mountstats/mountstats.man 2010-05-06 07:16:37.945043000 -0400 -@@ -0,0 +1,32 @@ -+.\" -+.\" mountstats(8) -+.\" -+.TH mountstats 8 "15 Apr 2010" -+.SH NAME -+mountstats \- Displays NFS client per-mount statistics -+.SH SYNOPSIS -+.BI "mountstats [" "] " " [ " "]" -+.SH DESCRIPTION -+The -+.B mountstats -+command displays NFS client statisitics on each given -+.I -+.SH OPTIONS -+.TP -+.B " \-\-nfs -+display only the NFS statistics -+.TP -+.B " \-\-rpc -+display only the RPC statistics -+.TP -+.B " \-\-version -+display the version of this command -+.SH FILES -+.TP -+.B /proc/self/mountstats -+.SH SEE ALSO -+.BR iostat (8), -+.BR nfsiostat (8), -+.BR nfsstat(8) -+.SH AUTHOR -+Chuck Lever -diff -up nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am.orig nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am ---- nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am.orig 2010-05-06 07:16:37.958021000 -0400 -+++ nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am 2010-05-06 07:16:37.960024000 -0400 -@@ -0,0 +1,13 @@ -+## Process this file with automake to produce Makefile.in -+PYTHON_FILES = nfs-iostat.py -+ -+man8_MANS = nfsiostat.man -+ -+EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) -+ -+all-local: $(PYTHON_FILES) -+ -+install-data-hook: -+ $(INSTALL) --mode 755 nfs-iostat.py $(DESTDIR)$(sbindir)/nfsiostat -+ -+MAINTAINERCLEANFILES=Makefile.in -diff -up nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man.orig nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man ---- nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man.orig 2010-05-06 07:16:37.963021000 -0400 -+++ nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man 2010-05-06 07:16:37.965018000 -0400 -@@ -0,0 +1,70 @@ -+.\" -+.\" nfsiostat(8) -+.\" -+.TH nfsiostat 8 "15 Apr 2010" -+.SH NAME -+nfsiostat \- Emulate iostat for NFS mount points using /proc/self/mountstats -+.SH SYNOPSIS -+.BI "nfsiostat [[" "] [" "]] [" "][" "] -+.SH DESCRIPTION -+The -+.B nfsiostat -+command displays NFS client per-mount statisitics. -+.TP -+ -+specifies the amount of time in seconds between each report. -+The first report contains statistics for the time since each file -+system was mounted. Each subsequent report contains statistics collected -+during the interval since the previous report. -+.TP -+ -+If the -+.I -+parameter is -+specified, the value of -+.I -+determines the number of reports generated at -+. -+seconds apart. if the interval parameter is -+specified without the -+.I -+parameter, the command generates reports continuously. -+.TP -+ -+Define below -+.TP -+ -+If one or more -+.I -+names are specified, statistics for only these mount points will -+be displayed. Otherwise, all NFS mount points on the client are listed. -+.SH OPTIONS -+.TP -+.B \-a " or " \-\-attr -+displays statistics related to the attribute cache -+.TP -+.B \-d " or " \-\-dir -+displays statistics related to directory operations -+.TP -+.B \-h " or " \-\-help -+shows help message and exit -+.TP -+.B \-l LIST or " \-\-list=LIST -+only print stats for first LIST mount points -+.TP -+.B \-p " or " \-\-page -+displays statistics related to the page cache -+.TP -+.B \-s " or " \-\-sort -+Sort NFS mount points by ops/second -+.B \-\-version -+show program's version number and exit -+.SH FILES -+.TP -+.B /proc/self/mountstats -+.SH SEE ALSO -+.BR iostat (8), -+.BR mountstats (8), -+.BR nfsstat(8) -+.SH AUTHOR -+Chuck Lever -diff -up nfs-utils-1.2.2/utils/gssd/context.h.orig nfs-utils-1.2.2/utils/gssd/context.h ---- nfs-utils-1.2.2/utils/gssd/context.h.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/utils/gssd/context.h 2010-05-06 07:16:37.970009000 -0400 -@@ -1,5 +1,5 @@ - /* -- Copyright (c) 2004 The Regents of the University of Michigan. -+ Copyright (c) 2004,2008 The Regents of the University of Michigan. - All rights reserved. - - Redistribution and use in source and binary forms, with or without -@@ -36,6 +36,10 @@ - /* Hopefully big enough to hold any serialized context */ - #define MAX_CTX_LEN 4096 - -+/* New context format flag values */ -+#define KRB5_CTX_FLAG_INITIATOR 0x00000001 -+#define KRB5_CTX_FLAG_CFX 0x00000002 -+#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 - - int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf, - gss_OID mech, int32_t *endtime); -diff -up nfs-utils-1.2.2/utils/gssd/context_lucid.c.orig nfs-utils-1.2.2/utils/gssd/context_lucid.c ---- nfs-utils-1.2.2/utils/gssd/context_lucid.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/utils/gssd/context_lucid.c 2010-05-06 07:16:37.975007000 -0400 -@@ -42,6 +42,7 @@ - #include - #include - #include -+#include - - #include - -@@ -119,15 +120,13 @@ prepare_krb5_rfc1964_buffer(gss_krb5_luc - * Note that the rfc1964 version only supports DES enctypes. - */ - if (lctx->rfc1964_kd.ctx_key.type != 4) { -- printerr(1, "prepare_krb5_rfc1964_buffer: " -- "overriding heimdal keytype (%d => %d)\n", -- lctx->rfc1964_kd.ctx_key.type, 4); -+ printerr(2, "%s: overriding heimdal keytype (%d => %d)\n", -+ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, 4); - lctx->rfc1964_kd.ctx_key.type = 4; - } - #endif -- printerr(2, "prepare_krb5_rfc1964_buffer: serializing keys with " -- "enctype %d and length %d\n", -- lctx->rfc1964_kd.ctx_key.type, -+ printerr(2, "%s: serializing keys with enctype %d and length %d\n", -+ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, - lctx->rfc1964_kd.ctx_key.length); - - /* derive the encryption key and copy it into buffer */ -@@ -158,11 +157,100 @@ out_err: - return -1; - } - -+/* Flags for version 2 context flags */ -+#define KRB5_CTX_FLAG_INITIATOR 0x00000001 -+#define KRB5_CTX_FLAG_CFX 0x00000002 -+#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 -+ -+/* -+ * Prepare a new-style buffer, as defined in rfc4121 (a.k.a. cfx), -+ * to send to the kernel for newer encryption types -- or for DES3. -+ * -+ * The new format is: -+ * -+ * u32 flags; -+ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 -+ * #define KRB5_CTX_FLAG_CFX 0x00000002 -+ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 -+ * s32 endtime; -+ * u64 seq_send; -+ * u32 enctype; ( encrption type of key ) -+ * raw key; ( raw key bytes (kernel will derive)) -+ * -+ */ - static int --prepare_krb5_rfc_cfx_buffer(gss_krb5_lucid_context_v1_t *lctx, -+prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx, - gss_buffer_desc *buf, int32_t *endtime) - { -- printerr(0, "ERROR: prepare_krb5_rfc_cfx_buffer: not implemented\n"); -+ char *p, *end; -+ uint32_t v2_flags = 0; -+ uint32_t enctype; -+ uint32_t keysize; -+ -+ if (!(buf->value = calloc(1, MAX_CTX_LEN))) -+ goto out_err; -+ p = buf->value; -+ end = buf->value + MAX_CTX_LEN; -+ -+ /* Version 2 */ -+ if (lctx->initiate) -+ v2_flags |= KRB5_CTX_FLAG_INITIATOR; -+ if (lctx->protocol != 0) -+ v2_flags |= KRB5_CTX_FLAG_CFX; -+ if (lctx->protocol != 0 && lctx->cfx_kd.have_acceptor_subkey == 1) -+ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; -+ -+ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; -+ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err; -+ if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err; -+ -+ /* Protocol 0 here implies DES3 or RC4 */ -+ printerr(2, "%s: protocol %d\n", __FUNCTION__, lctx->protocol); -+ if (lctx->protocol == 0) { -+ enctype = lctx->rfc1964_kd.ctx_key.type; -+ keysize = lctx->rfc1964_kd.ctx_key.length; -+ } else { -+ if (lctx->cfx_kd.have_acceptor_subkey) { -+ enctype = lctx->cfx_kd.acceptor_subkey.type; -+ keysize = lctx->cfx_kd.acceptor_subkey.length; -+ } else { -+ enctype = lctx->cfx_kd.ctx_key.type; -+ keysize = lctx->cfx_kd.ctx_key.length; -+ } -+ } -+ printerr(2, "%s: serializing key with enctype %d and size %d\n", -+ __FUNCTION__, enctype, keysize); -+ -+ if (WRITE_BYTES(&p, end, enctype)) goto out_err; -+ -+ if (lctx->protocol == 0) { -+ if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data, -+ lctx->rfc1964_kd.ctx_key.length)) -+ goto out_err; -+ } else { -+ if (lctx->cfx_kd.have_acceptor_subkey) { -+ if (write_bytes(&p, end, -+ lctx->cfx_kd.acceptor_subkey.data, -+ lctx->cfx_kd.acceptor_subkey.length)) -+ goto out_err; -+ } else { -+ if (write_bytes(&p, end, lctx->cfx_kd.ctx_key.data, -+ lctx->cfx_kd.ctx_key.length)) -+ goto out_err; -+ } -+ } -+ -+ buf->length = p - (char *)buf->value; -+ return 0; -+ -+out_err: -+ printerr(0, "ERROR: %s: failed serializing krb5 context for kernel\n", -+ __FUNCTION__); -+ if (buf->value) { -+ free(buf->value); -+ buf->value = NULL; -+ } -+ buf->length = 0; - return -1; - } - -@@ -176,7 +264,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss - gss_krb5_lucid_context_v1_t *lctx = 0; - int retcode = 0; - -- printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n"); -+ printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__); - maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx, - 1, &return_ctx); - if (maj_stat != GSS_S_COMPLETE) { -@@ -198,11 +286,20 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss - break; - } - -- /* Now lctx points to a lucid context that we can send down to kernel */ -- if (lctx->protocol == 0) -+ /* -+ * Now lctx points to a lucid context that we can send down to kernel -+ * -+ * Note: we send down different information to the kernel depending -+ * on the protocol version and the enctyption type. -+ * For protocol version 0 with all enctypes besides DES3, we use -+ * the original format. For protocol version != 0 or DES3, we -+ * send down the new style information. -+ */ -+ -+ if (lctx->protocol == 0 && lctx->rfc1964_kd.ctx_key.type <= 4) - retcode = prepare_krb5_rfc1964_buffer(lctx, buf, endtime); - else -- retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf, endtime); -+ retcode = prepare_krb5_rfc4121_buffer(lctx, buf, endtime); - - maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx); - if (maj_stat != GSS_S_COMPLETE) { -@@ -212,8 +309,8 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss - } - - if (retcode) { -- printerr(1, "serialize_krb5_ctx: prepare_krb5_*_buffer " -- "failed (retcode = %d)\n", retcode); -+ printerr(1, "%s: prepare_krb5_*_buffer failed (retcode = %d)\n", -+ __FUNCTION__, retcode); - goto out_err; - } - -@@ -223,4 +320,7 @@ out_err: - printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); - return -1; - } -+ -+ -+ - #endif /* HAVE_LUCID_CONTEXT_SUPPORT */ -diff -up nfs-utils-1.2.2/utils/gssd/context_mit.c.orig nfs-utils-1.2.2/utils/gssd/context_mit.c ---- nfs-utils-1.2.2/utils/gssd/context_mit.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/utils/gssd/context_mit.c 2010-05-06 07:16:37.980998000 -0400 -@@ -1,5 +1,5 @@ - /* -- Copyright (c) 2004 The Regents of the University of Michigan. -+ Copyright (c) 2004-2006 The Regents of the University of Michigan. - All rights reserved. - - Redistribution and use in source and binary forms, with or without -@@ -38,6 +38,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -52,8 +53,7 @@ - /* XXX argggg, there's gotta be a better way than just duplicating this - * whole struct. Unfortunately, this is in a "private" header file, - * so this is our best choice at this point :-/ -- * -- * XXX Does this match the Heimdal definition? */ -+ */ - - typedef struct _krb5_gss_ctx_id_rec { - unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */ -@@ -156,50 +156,120 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss - { - krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)ctx)->internal_ctx_id; - char *p, *end; -- static int constant_one = 1; - static int constant_zero = 0; -+ static int constant_one = 1; -+ static int constant_two = 2; - uint32_t word_seq_send; -+ u_int64_t seq_send_64bit; -+ uint32_t v2_flags = 0; - - if (!(buf->value = calloc(1, MAX_CTX_LEN))) - goto out_err; - p = buf->value; - end = buf->value + MAX_CTX_LEN; - -- if (kctx->initiate) { -- if (WRITE_BYTES(&p, end, constant_one)) goto out_err; -- } -- else { -- if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; -- } -- if (kctx->seed_init) { -- if (WRITE_BYTES(&p, end, constant_one)) goto out_err; -- } -- else { -- if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; -- } -- if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) -+ switch (kctx->enc->enctype) { -+ case ENCTYPE_DES_CBC_CRC: -+ case ENCTYPE_DES_CBC_MD4: -+ case ENCTYPE_DES_CBC_MD5: -+ case ENCTYPE_DES_CBC_RAW: -+ /* Old format of context to the kernel */ -+ if (kctx->initiate) { -+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err; -+ } -+ else { -+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; -+ } -+ if (kctx->seed_init) { -+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err; -+ } -+ else { -+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; -+ } -+ if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) -+ goto out_err; -+ if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; -+ if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; -+ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; -+ word_seq_send = kctx->seq_send; -+ if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; -+ if (write_oid(&p, end, kctx->mech_used)) goto out_err; -+ -+ printerr(2, "serialize_krb5_ctx: serializing keys with " -+ "enctype %d and length %d\n", -+ kctx->enc->enctype, kctx->enc->length); -+ -+ if (write_keyblock(&p, end, kctx->enc)) goto out_err; -+ if (write_keyblock(&p, end, kctx->seq)) goto out_err; -+ break; -+ case ENCTYPE_DES3_CBC_RAW: -+ case ENCTYPE_DES3_CBC_SHA1: -+ case ENCTYPE_ARCFOUR_HMAC: -+ case ENCTYPE_ARCFOUR_HMAC_EXP: -+ case ENCTYPE_AES128_CTS_HMAC_SHA1_96: -+ case ENCTYPE_AES256_CTS_HMAC_SHA1_96: -+ /* New format of context to the kernel */ -+ /* u32 flags; -+ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 -+ * #define KRB5_CTX_FLAG_CFX 0x00000002 -+ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 -+ * s32 endtime; -+ * u64 seq_send; -+ * u32 enctype; -+ * rawkey data -+ */ -+ -+ if (kctx->initiate) -+ v2_flags |= KRB5_CTX_FLAG_INITIATOR; -+ if (kctx->proto == 1) -+ v2_flags |= KRB5_CTX_FLAG_CFX; -+ if (kctx->have_acceptor_subkey) -+ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; -+ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; -+ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; -+ -+ seq_send_64bit = kctx->seq_send; -+ if (WRITE_BYTES(&p, end, seq_send_64bit)) goto out_err; -+ -+ if (kctx->have_acceptor_subkey) { -+ if (WRITE_BYTES(&p, end, kctx->acceptor_subkey->enctype)) -+ goto out_err; -+ printerr(2, "serialize_krb5_ctx: serializing subkey " -+ "with enctype %d and size %d\n", -+ kctx->acceptor_subkey->enctype, -+ kctx->acceptor_subkey->length); -+ -+ if (write_bytes(&p, end, -+ kctx->acceptor_subkey->contents, -+ kctx->acceptor_subkey->length)) -+ goto out_err; -+ } else { -+ if (WRITE_BYTES(&p, end, kctx->enc->enctype)) -+ goto out_err; -+ printerr(2, "serialize_krb5_ctx: serializing key " -+ "with enctype %d and size %d\n", -+ kctx->enc->enctype, kctx->enc->length); -+ -+ if (write_bytes(&p, end, kctx->enc->contents, -+ kctx->enc->length)) -+ goto out_err; -+ } -+ break; -+ default: -+ printerr(0, "ERROR: serialize_krb5_ctx: unsupported encryption " -+ "algorithm %d\n", kctx->enc->enctype); - goto out_err; -- if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; -- if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; -- if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; -- if (endtime) -- *endtime = kctx->endtime; -- word_seq_send = kctx->seq_send; -- if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; -- if (write_oid(&p, end, kctx->mech_used)) goto out_err; -- -- printerr(2, "serialize_krb5_ctx: serializing keys with " -- "enctype %d and length %d\n", -- kctx->enc->enctype, kctx->enc->length); -- -- if (write_keyblock(&p, end, kctx->enc)) goto out_err; -- if (write_keyblock(&p, end, kctx->seq)) goto out_err; -+ } - - buf->length = p - (char *)buf->value; - return 0; -+ - out_err: - printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); -- if (buf->value) free(buf->value); -+ if (buf->value) { -+ free(buf->value); -+ } -+ buf->value = NULL; - buf->length = 0; - return -1; - } -diff -up nfs-utils-1.2.2/utils/gssd/gssd_proc.c.orig nfs-utils-1.2.2/utils/gssd/gssd_proc.c ---- nfs-utils-1.2.2/utils/gssd/gssd_proc.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/utils/gssd/gssd_proc.c 2010-05-06 07:16:37.986992000 -0400 -@@ -600,6 +600,67 @@ update_client_list(void) - return retval; - } - -+/* Encryption types supported by the kernel rpcsec_gss code */ -+int num_krb5_enctypes = 0; -+krb5_enctype *krb5_enctypes = NULL; -+ -+/* -+ * Parse the supported encryption type information -+ */ -+static int -+parse_enctypes(char *enctypes) -+{ -+ int n = 0; -+ char *curr, *comma; -+ int i; -+ static char *cached_types; -+ -+ if (cached_types && strcmp(cached_types, enctypes) == 0) -+ return 0; -+ free(cached_types); -+ -+ if (krb5_enctypes != NULL) { -+ free(krb5_enctypes); -+ krb5_enctypes = NULL; -+ num_krb5_enctypes = 0; -+ } -+ -+ /* count the number of commas */ -+ for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) { -+ comma = strchr(curr, ','); -+ if (comma != NULL) -+ n++; -+ else -+ break; -+ } -+ /* If no more commas and we're not at the end, there's one more value */ -+ if (*curr != '\0') -+ n++; -+ -+ /* Empty string, return an error */ -+ if (n == 0) -+ return ENOENT; -+ -+ /* Allocate space for enctypes array */ -+ if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) { -+ return ENOMEM; -+ } -+ -+ /* Now parse each value into the array */ -+ for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) { -+ krb5_enctypes[i++] = atoi(curr); -+ comma = strchr(curr, ','); -+ if (comma == NULL) -+ break; -+ } -+ -+ num_krb5_enctypes = n; -+ if ((cached_types = malloc(strlen(enctypes)+1))) -+ strcpy(cached_types, enctypes); -+ -+ return 0; -+} -+ - static int - do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, - gss_buffer_desc *context_token) -@@ -1133,6 +1194,7 @@ handle_gssd_upcall(struct clnt_info *clp - char *mech = NULL; - char *target = NULL; - char *service = NULL; -+ char *enctypes = NULL; - - printerr(1, "handling gssd upcall (%s)\n", clp->dirname); - -@@ -1176,6 +1238,23 @@ handle_gssd_upcall(struct clnt_info *clp - goto out; - } - -+ /* read supported encryption types if supplied */ -+ if ((p = strstr(lbuf, "enctypes=")) != NULL) { -+ enctypes = malloc(lbuflen); -+ if (!enctypes) -+ goto out; -+ if (sscanf(p, "enctypes=%s", enctypes) != 1) { -+ printerr(0, "WARNING: handle_gssd_upcall: " -+ "failed to parse target name " -+ "in upcall string '%s'\n", lbuf); -+ goto out; -+ } -+ if (parse_enctypes(enctypes) != 0) { -+ printerr(0, "WARNING: handle_gssd_upcall: " -+ "parsing encryption types failed: errno %d\n", errno); -+ } -+ } -+ - /* read target name */ - if ((p = strstr(lbuf, "target=")) != NULL) { - target = malloc(lbuflen); -@@ -1222,6 +1301,7 @@ handle_gssd_upcall(struct clnt_info *clp - out: - free(lbuf); - free(mech); -+ free(enctypes); - free(target); - free(service); - return; -diff -up nfs-utils-1.2.2/utils/gssd/krb5_util.c.orig nfs-utils-1.2.2/utils/gssd/krb5_util.c ---- nfs-utils-1.2.2/utils/gssd/krb5_util.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/utils/gssd/krb5_util.c 2010-05-06 07:16:37.992992000 -0400 -@@ -292,61 +292,6 @@ gssd_find_existing_krb5_ccache(uid_t uid - return err; - } - -- --#ifdef HAVE_SET_ALLOWABLE_ENCTYPES --/* -- * this routine obtains a credentials handle via gss_acquire_cred() -- * then calls gss_krb5_set_allowable_enctypes() to limit the encryption -- * types negotiated. -- * -- * XXX Should call some function to determine the enctypes supported -- * by the kernel. (Only need to do that once!) -- * -- * Returns: -- * 0 => all went well -- * -1 => there was an error -- */ -- --int --limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) --{ -- u_int maj_stat, min_stat; -- gss_cred_id_t credh; -- gss_OID_set_desc desired_mechs; -- krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, -- ENCTYPE_DES_CBC_MD5, -- ENCTYPE_DES_CBC_MD4 }; -- int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); -- -- /* We only care about getting a krb5 cred */ -- desired_mechs.count = 1; -- desired_mechs.elements = &krb5oid; -- -- maj_stat = gss_acquire_cred(&min_stat, NULL, 0, -- &desired_mechs, GSS_C_INITIATE, -- &credh, NULL, NULL); -- -- if (maj_stat != GSS_S_COMPLETE) { -- if (get_verbosity() > 0) -- pgsserr("gss_acquire_cred", -- maj_stat, min_stat, &krb5oid); -- return -1; -- } -- -- maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid, -- num_enctypes, &enctypes); -- if (maj_stat != GSS_S_COMPLETE) { -- pgsserr("gss_set_allowable_enctypes", -- maj_stat, min_stat, &krb5oid); -- gss_release_cred(&min_stat, &credh); -- return -1; -- } -- sec->cred = credh; -- -- return 0; --} --#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ -- - /* - * Obtain credentials via a key in the keytab given - * a keytab handle and a gssd_k5_kt_princ structure. -@@ -1304,3 +1249,68 @@ gssd_k5_get_default_realm(char **def_rea - - krb5_free_context(context); - } -+ -+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES -+/* -+ * this routine obtains a credentials handle via gss_acquire_cred() -+ * then calls gss_krb5_set_allowable_enctypes() to limit the encryption -+ * types negotiated. -+ * -+ * XXX Should call some function to determine the enctypes supported -+ * by the kernel. (Only need to do that once!) -+ * -+ * Returns: -+ * 0 => all went well -+ * -1 => there was an error -+ */ -+ -+int -+limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) -+{ -+ u_int maj_stat, min_stat; -+ gss_cred_id_t credh; -+ gss_OID_set_desc desired_mechs; -+ krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, -+ ENCTYPE_DES_CBC_MD5, -+ ENCTYPE_DES_CBC_MD4 }; -+ int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); -+ extern int num_krb5_enctypes; -+ extern krb5_enctype *krb5_enctypes; -+ -+ /* We only care about getting a krb5 cred */ -+ desired_mechs.count = 1; -+ desired_mechs.elements = &krb5oid; -+ -+ maj_stat = gss_acquire_cred(&min_stat, NULL, 0, -+ &desired_mechs, GSS_C_INITIATE, -+ &credh, NULL, NULL); -+ -+ if (maj_stat != GSS_S_COMPLETE) { -+ if (get_verbosity() > 0) -+ pgsserr("gss_acquire_cred", -+ maj_stat, min_stat, &krb5oid); -+ return -1; -+ } -+ -+ /* -+ * If we failed for any reason to produce global -+ * list of supported enctypes, use local default here. -+ */ -+ if (krb5_enctypes == NULL) -+ maj_stat = gss_set_allowable_enctypes(&min_stat, credh, -+ &krb5oid, num_enctypes, enctypes); -+ else -+ maj_stat = gss_set_allowable_enctypes(&min_stat, credh, -+ &krb5oid, num_krb5_enctypes, krb5_enctypes); -+ -+ if (maj_stat != GSS_S_COMPLETE) { -+ pgsserr("gss_set_allowable_enctypes", -+ maj_stat, min_stat, &krb5oid); -+ gss_release_cred(&min_stat, &credh); -+ return -1; -+ } -+ sec->cred = credh; -+ -+ return 0; -+} -+#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ -diff -up nfs-utils-1.2.2/utils/mountd/auth.c.orig nfs-utils-1.2.2/utils/mountd/auth.c ---- nfs-utils-1.2.2/utils/mountd/auth.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/utils/mountd/auth.c 2010-05-06 07:16:38.009992000 -0400 -@@ -142,7 +142,7 @@ auth_authenticate_newcache(char *what, s - return NULL; - - my_client.m_naddr = 1; -- my_client.m_addrlist[0] = caller->sin_addr; -+ set_addrlist_in(&my_client, 0, caller); - my_exp.m_client = &my_client; - - exp = NULL; -diff -up nfs-utils-1.2.2/utils/mountd/cache.c.orig nfs-utils-1.2.2/utils/mountd/cache.c ---- nfs-utils-1.2.2/utils/mountd/cache.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/utils/mountd/cache.c 2010-05-06 07:16:38.014995000 -0400 -@@ -125,7 +125,7 @@ void auth_unix_gid(FILE *f) - * reply is - * uid expiry count list of group ids - */ -- int uid; -+ uid_t uid; - struct passwd *pw; - gid_t glist[100], *groups = glist; - int ngroups = 100; -@@ -136,7 +136,7 @@ void auth_unix_gid(FILE *f) - return; - - cp = lbuf; -- if (qword_get_int(&cp, &uid) != 0) -+ if (qword_get_uint(&cp, &uid) != 0) - return; - - pw = getpwuid(uid); -@@ -153,14 +153,14 @@ void auth_unix_gid(FILE *f) - groups, &ngroups); - } - } -- qword_printint(f, uid); -- qword_printint(f, time(0)+30*60); -+ qword_printuint(f, uid); -+ qword_printuint(f, time(0)+30*60); - if (rv >= 0) { -- qword_printint(f, ngroups); -+ qword_printuint(f, ngroups); - for (i=0; im_client, 0); - int err; - FILE *f; - -@@ -871,7 +872,7 @@ int cache_export(nfs_export *exp, char * - return -1; - - qword_print(f, "nfsd"); -- qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0])); -+ qword_print(f, inet_ntoa(sin->sin_addr)); - qword_printint(f, time(0)+30*60); - qword_print(f, exp->m_client->m_hostname); - err = qword_eol(f); -diff -up nfs-utils-1.2.2/utils/mount/network.c.orig nfs-utils-1.2.2/utils/mount/network.c ---- nfs-utils-1.2.2/utils/mount/network.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/utils/mount/network.c 2010-05-06 07:16:37.998992000 -0400 -@@ -857,7 +857,14 @@ int nfs_advise_umount(const struct socka - return 0; - } - -- client->cl_auth = authunix_create_default(); -+ client->cl_auth = nfs_authsys_create(); -+ if (client->cl_auth == NULL) { -+ if (verbose) -+ nfs_error(_("%s: Failed to create RPC auth handle"), -+ progname); -+ CLNT_DESTROY(client); -+ return 0; -+ } - - res = CLNT_CALL(client, MOUNTPROC_UMNT, - (xdrproc_t)xdr_dirpath, (caddr_t)argp, -@@ -957,8 +964,10 @@ CLIENT *mnt_openclnt(clnt_addr_t *mnt_se - } - if (clnt) { - /* try to mount hostname:dirname */ -- clnt->cl_auth = authunix_create_default(); -- return clnt; -+ clnt->cl_auth = nfs_authsys_create(); -+ if (clnt->cl_auth) -+ return clnt; -+ CLNT_DESTROY(clnt); - } - return NULL; - } -diff -up nfs-utils-1.2.2/utils/mount/stropts.c.orig nfs-utils-1.2.2/utils/mount/stropts.c ---- nfs-utils-1.2.2/utils/mount/stropts.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/utils/mount/stropts.c 2010-05-06 07:16:38.004992000 -0400 -@@ -799,6 +799,7 @@ static int nfs_is_permanent_error(int er - case ESTALE: - case ETIMEDOUT: - case ECONNREFUSED: -+ case EHOSTUNREACH: - return 0; /* temporary */ - default: - return 1; /* permanent */ -diff -up nfs-utils-1.2.2/utils/showmount/showmount.c.orig nfs-utils-1.2.2/utils/showmount/showmount.c ---- nfs-utils-1.2.2/utils/showmount/showmount.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/utils/showmount/showmount.c 2010-05-06 07:16:38.019995000 -0400 -@@ -194,7 +194,13 @@ int main(int argc, char **argv) - } - - mclient = nfs_get_mount_client(hostname, mount_vers_tbl[vers]); -- mclient->cl_auth = authunix_create_default(); -+ mclient->cl_auth = nfs_authsys_create(); -+ if (mclient->cl_auth == NULL) { -+ fprintf(stderr, "%s: unable to create RPC auth handle.\n", -+ program_name); -+ clnt_destroy(mclient); -+ exit(1); -+ } - total_timeout.tv_sec = TOTAL_TIMEOUT; - total_timeout.tv_usec = 0; - -diff -up nfs-utils-1.2.2/utils/statd/sm-notify.c.orig nfs-utils-1.2.2/utils/statd/sm-notify.c ---- nfs-utils-1.2.2/utils/statd/sm-notify.c.orig 2010-02-18 07:35:00.000000000 -0500 -+++ nfs-utils-1.2.2/utils/statd/sm-notify.c 2010-05-06 07:16:38.025992000 -0400 -@@ -54,7 +54,7 @@ struct nsm_host { - uint32_t xid; - }; - --static char nsm_hostname[256]; -+static char nsm_hostname[SM_MAXSTRLEN + 1]; - static int nsm_state; - static int nsm_family = AF_INET; - static int opt_debug = 0; -@@ -412,12 +412,33 @@ usage: fprintf(stderr, - } - } - -- if (opt_srcaddr) { -- strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1); -- } else -- if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) { -- xlog(L_ERROR, "Failed to obtain name of local host: %m"); -- exit(1); -+ if (opt_srcaddr != NULL) { -+ struct addrinfo *ai = NULL; -+ struct addrinfo hint = { -+ .ai_family = AF_UNSPEC, -+ .ai_flags = AI_NUMERICHOST, -+ }; -+ -+ if (getaddrinfo(opt_srcaddr, NULL, &hint, &ai)) -+ /* not a presentation address - use it */ -+ strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)); -+ else { -+ /* was a presentation address - look it up in -+ * /etc/hosts, so it can be used for my_name */ -+ int error; -+ -+ freeaddrinfo(ai); -+ hint.ai_flags = AI_CANONNAME; -+ error = getaddrinfo(opt_srcaddr, NULL, &hint, &ai); -+ if (error != 0) { -+ xlog(L_ERROR, "Bind address %s is unusable: %s", -+ opt_srcaddr, gai_strerror(error)); -+ exit(1); -+ } -+ strncpy(nsm_hostname, ai->ai_canonname, -+ sizeof(nsm_hostname)); -+ freeaddrinfo(ai); -+ } - } - - (void)nsm_retire_monitored_hosts(); -@@ -535,6 +556,8 @@ notify(const int sock) - static int - notify_host(int sock, struct nsm_host *host) - { -+ const char *my_name = (opt_srcaddr != NULL ? -+ nsm_hostname : host->my_name); - struct sockaddr *sap; - socklen_t salen; - -@@ -580,8 +603,8 @@ notify_host(int sock, struct nsm_host *h - host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS); - else - host->xid = nsm_xmit_notify(sock, sap, salen, -- SM_PROG, nsm_hostname, nsm_state); -- -+ SM_PROG, my_name, nsm_state); -+ - return 0; - } - -@@ -611,15 +634,28 @@ recv_rpcbind_reply(struct sockaddr *sap, - } - - /* -- * Successful NOTIFY call. Server returns void, so nothing -- * we need to do here. -+ * Successful NOTIFY call. Server returns void. -+ * -+ * Try sending another SM_NOTIFY with an unqualified "my_name" -+ * argument. Reuse the port number. If "my_name" is already -+ * unqualified, we're done. - */ - static void - recv_notify_reply(struct nsm_host *host) - { -- xlog(D_GENERAL, "Host %s notified successfully", host->name); -+ char *dot = strchr(host->my_name, '.'); - -- smn_forget_host(host); -+ if (dot != NULL) { -+ *dot = '\0'; -+ host->send_next = time(NULL); -+ host->xid = 0; -+ if (host->timeout >= NSM_MAX_TIMEOUT / 4) -+ host->timeout = NSM_MAX_TIMEOUT / 4; -+ insert_host(host); -+ } else { -+ xlog(D_GENERAL, "Host %s notified successfully", host->name); -+ smn_forget_host(host); -+ } - } - - /* -diff -up nfs-utils-1.2.2/utils/statd/sm-notify.man.orig nfs-utils-1.2.2/utils/statd/sm-notify.man ---- nfs-utils-1.2.2/utils/statd/sm-notify.man.orig 2010-05-06 07:15:49.867180000 -0400 -+++ nfs-utils-1.2.2/utils/statd/sm-notify.man 2010-05-06 07:16:38.030995000 -0400 -@@ -97,11 +97,9 @@ It uses the - string as the destination. - To identify which host has rebooted, the - .B sm-notify --command normally sends the results of --.BR gethostname (3) --as the -+command normally sends - .I my_name --string. -+string recorded when that remote was monitored. - The remote - .B rpc.statd - matches incoming SM_NOTIFY requests using this string, -@@ -202,15 +200,22 @@ argument to use when sending SM_NOTIFY r - If this option is not specified, - .B sm-notify - uses a wildcard address as the transport bind address, --and uses the results of --.BR gethostname (3) --as the -+and uses the -+.I my_name -+recorded when the remote was monitored as the - .I mon_name --argument. -+argument when sending SM_NOTIFY requests. - .IP - The - .I ipaddr - form can be expressed as either an IPv4 or an IPv6 presentation address. -+If the -+.I ipaddr -+form is used, the -+.B sm-notify -+command converts this address to a hostname for use as the -+.I mon_name -+argument when sending SM_NOTIFY requests. - .IP - This option can be useful in multi-homed configurations where - the remote requires notification from a specific network address. -@@ -252,13 +257,6 @@ consistent - The hostname the client uses to mount the server should match the server's - .I mon_name - in SM_NOTIFY requests it sends --.IP --The use of network addresses as a --.I mon_name --or a --.I my_name --string should be avoided when --interoperating with non-Linux NFS implementations. - .PP - Unmounting an NFS file system does not necessarily stop - either the NFS client or server from monitoring each other. -diff -up nfs-utils-1.2.2/utils/statd/statd.man.orig nfs-utils-1.2.2/utils/statd/statd.man ---- nfs-utils-1.2.2/utils/statd/statd.man.orig 2010-05-06 07:15:49.874177000 -0400 -+++ nfs-utils-1.2.2/utils/statd/statd.man 2010-05-06 07:16:38.036992000 -0400 -@@ -100,11 +100,9 @@ It uses the - string as the destination. - To identify which host has rebooted, the - .B sm-notify --command normally sends the results of --.BR gethostname (3) --as the -+command sends the - .I my_name --string. -+string recorded when that remote was monitored. - The remote - .B rpc.statd - matches incoming SM_NOTIFY requests using this string, -@@ -292,7 +290,6 @@ man pages. - .SH ADDITIONAL NOTES - Lock recovery after a reboot is critical to maintaining data integrity - and preventing unnecessary application hangs. --.PP - To help - .B rpc.statd - match SM_NOTIFY requests to NLM requests, a number of best practices -@@ -309,13 +306,6 @@ consistent - The hostname the client uses to mount the server should match the server's - .I mon_name - in SM_NOTIFY requests it sends --.IP --The use of network addresses as a --.I mon_name --or a --.I my_name --string should be avoided when --interoperating with non-Linux NFS implementations. - .PP - Unmounting an NFS file system does not necessarily stop - either the NFS client or server from monitoring each other. diff --git a/nfs-utils-1-2-3-rc4.patch b/nfs-utils-1-2-3-rc4.patch new file mode 100644 index 0000000..eb9983a --- /dev/null +++ b/nfs-utils-1-2-3-rc4.patch @@ -0,0 +1,4323 @@ +diff --git a/aclocal/libcap.m4 b/aclocal/libcap.m4 +index eabe507..68a624c 100644 +--- a/aclocal/libcap.m4 ++++ b/aclocal/libcap.m4 +@@ -5,11 +5,19 @@ AC_DEFUN([AC_LIBCAP], [ + dnl look for prctl + AC_CHECK_FUNC([prctl], , ) + +- dnl look for the library; do not add to LIBS if found +- AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) +- AC_SUBST(LIBCAP) ++ AC_ARG_ENABLE([caps], ++ [AS_HELP_STRING([--disable-caps], [Disable capabilities support])]) ++ ++ LIBCAP= ++ ++ if test "x$enable_caps" != "xno" ; then ++ dnl look for the library; do not add to LIBS if found ++ AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) + +- AC_CHECK_HEADERS([sys/capability.h], , +- [AC_MSG_ERROR([libcap headers not found.])]) ++ AC_CHECK_HEADERS([sys/capability.h], , ++ [test "x$enable_caps" = "xyes" && AC_MSG_ERROR([libcap headers not found.])]) ++ fi ++ ++ AC_SUBST(LIBCAP) + + ])dnl +diff --git a/autogen.sh b/autogen.sh +old mode 100644 +new mode 100755 +diff --git a/configure.ac b/configure.ac +index b7520d8..d90a88f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -81,7 +81,7 @@ AC_ARG_ENABLE(nfsv41, + if test "$enable_nfsv41" = yes; then + AC_DEFINE(NFS41_SUPPORTED, 1, [Define this if you want NFSv41 support compiled in]) + else +- enable_nfsv4= ++ enable_nfsv41= + fi + AC_SUBST(enable_nfsv41) + AM_CONDITIONAL(CONFIG_NFSV41, [test "$enable_nfsv41" = "yes"]) +@@ -425,6 +425,8 @@ AC_CONFIG_FILES([ + tools/nlmtest/Makefile + tools/rpcdebug/Makefile + tools/rpcgen/Makefile ++ tools/mountstats/Makefile ++ tools/nfs-iostat/Makefile + utils/Makefile + utils/exportfs/Makefile + utils/gssd/Makefile +diff --git a/support/export/client.c b/support/export/client.c +index 6236561..dc01067 100644 +--- a/support/export/client.c ++++ b/support/export/client.c +@@ -17,7 +17,7 @@ + #include + #include + #include +-#include "xmalloc.h" ++ + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" +@@ -28,58 +28,140 @@ + #if !defined(__GLIBC__) || __GLIBC__ < 2 + extern int innetgr(char *netgr, char *host, char *, char *); + #endif +-static void client_init(nfs_client *clp, const char *hname, +- struct hostent *hp); +-static int client_checkaddr(nfs_client *clp, struct in_addr addr); ++ ++static char *add_name(char *old, const char *add); + + nfs_client *clientlist[MCL_MAXTYPES] = { NULL, }; + + +-/* if canonical is set, then we *know* this is already a canonical name +- * so hostname lookup is avoided. +- * This is used when reading /proc/fs/nfs/exports ++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_netmask(nfs_client *clp, const char *slash) ++{ ++ struct sockaddr_in sin = { ++ .sin_family = AF_INET, ++ }; ++ ++ if (strchr(slash + 1, '.') != NULL) ++ sin.sin_addr.s_addr = inet_addr(slash + 1); ++ else { ++ int prefixlen = atoi(slash + 1); ++ if (0 < prefixlen && prefixlen <= 32) ++ sin.sin_addr.s_addr = ++ htonl((uint32_t)~0 << (32 - prefixlen)); ++ else ++ goto out_badprefix; ++ } ++ ++ set_addrlist_in(clp, 1, &sin); ++ return 1; ++ ++out_badprefix: ++ xlog(L_ERROR, "Invalid prefix `%s' for %s", slash + 1, clp->m_hostname); ++ return 0; ++} ++ ++static int ++init_subnetwork(nfs_client *clp) ++{ ++ struct sockaddr_in sin = { ++ .sin_family = AF_INET, ++ }; ++ static char slash32[] = "/32"; ++ char *cp; ++ ++ cp = strchr(clp->m_hostname, '/'); ++ if (cp == NULL) ++ cp = slash32; ++ ++ *cp = '\0'; ++ sin.sin_addr.s_addr = inet_addr(clp->m_hostname); ++ set_addrlist_in(clp, 0, &sin); ++ *cp = '/'; ++ ++ return init_netmask(clp, cp); ++} ++ ++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. + */ + nfs_client * + client_lookup(char *hname, int canonical) + { + nfs_client *clp = NULL; + int htype; +- struct hostent *hp = NULL; ++ struct addrinfo *ai = NULL; + + htype = client_gettype(hname); + + if (htype == MCL_FQDN && !canonical) { +- struct hostent *hp2; +- hp = gethostbyname(hname); +- if (hp == NULL || hp->h_addrtype != AF_INET) { +- xlog(L_ERROR, "%s has non-inet addr", hname); +- return NULL; ++ ai = host_addrinfo(hname); ++ if (!ai) { ++ xlog(L_ERROR, "Failed to resolve %s", hname); ++ goto out; + } +- /* make sure we have canonical name */ +- hp2 = hostent_dup(hp); +- hp = gethostbyaddr(hp2->h_addr, hp2->h_length, +- hp2->h_addrtype); +- if (hp) { +- hp = hostent_dup(hp); +- /* but now we might not have all addresses... */ +- if (hp2->h_addr_list[1]) { +- struct hostent *hp3 = +- gethostbyname(hp->h_name); +- if (hp3) { +- free(hp); +- hp = hostent_dup(hp3); +- } +- } +- free(hp2); +- } else +- hp = hp2; +- +- hname = (char *) hp->h_name; ++ hname = ai->ai_canonname; + +- for (clp = clientlist[htype]; clp; clp = clp->m_next) { +- if (client_check(clp, hp)) ++ for (clp = clientlist[htype]; clp; clp = clp->m_next) ++ if (client_check(clp, ai)) + break; +- } + } else { + for (clp = clientlist[htype]; clp; clp = clp->m_next) { + if (strcasecmp(hname, clp->m_hostname)==0) +@@ -87,106 +169,60 @@ client_lookup(char *hname, int canonical) + } + } + +- if (!clp) { +- clp = (nfs_client *) xmalloc(sizeof(*clp)); +- memset(clp, 0, sizeof(*clp)); ++ if (clp == NULL) { ++ clp = calloc(1, sizeof(*clp)); ++ if (clp == NULL) ++ goto out; + clp->m_type = htype; +- client_init(clp, hname, NULL); ++ if (!client_init(clp, hname, NULL)) { ++ client_free(clp); ++ clp = NULL; ++ goto out; ++ } + client_add(clp); + } + +- if (htype == MCL_FQDN && clp->m_naddr == 0 && hp != NULL) { +- char **ap = hp->h_addr_list; +- int i; +- +- for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) +- clp->m_addrlist[i] = *(struct in_addr *)*ap; +- clp->m_naddr = i; +- } +- +- if (hp) +- free (hp); ++ if (htype == MCL_FQDN && clp->m_naddr == 0) ++ init_addrlist(clp, ai); + ++out: ++ freeaddrinfo(ai); + return clp; + } + ++/** ++ * client_dup - create a copy of an nfs_client ++ * @clp: pointer to nfs_client to copy ++ * @ai: pointer to addrinfo used to initialize the new client's addrlist ++ * ++ * Returns a dynamically allocated nfs_client if successful, or ++ * NULL if some problem occurs. Caller must free the returned ++ * nfs_client with free(3). ++ */ + nfs_client * +-client_dup(nfs_client *clp, struct hostent *hp) ++client_dup(const nfs_client *clp, const struct addrinfo *ai) + { + nfs_client *new; + +- new = (nfs_client *) xmalloc(sizeof(*new)); ++ new = (nfs_client *)malloc(sizeof(*new)); ++ if (new == NULL) ++ return NULL; + memcpy(new, clp, sizeof(*new)); + new->m_type = MCL_FQDN; + new->m_hostname = NULL; + +- client_init(new, (char *) hp->h_name, hp); ++ if (!client_init(new, ai->ai_canonname, ai)) { ++ client_free(new); ++ return NULL; ++ } + client_add(new); + return new; + } + +-static void +-client_init(nfs_client *clp, const char *hname, struct hostent *hp) +-{ +- xfree(clp->m_hostname); +- if (hp) +- clp->m_hostname = xstrdup(hp->h_name); +- else +- clp->m_hostname = xstrdup(hname); +- +- clp->m_exported = 0; +- clp->m_count = 0; +- +- if (clp->m_type == MCL_SUBNETWORK) { +- char *cp = strchr(clp->m_hostname, '/'); +- static char slash32[] = "/32"; +- +- if(!cp) cp = slash32; +- *cp = '\0'; +- clp->m_addrlist[0].s_addr = inet_addr(clp->m_hostname); +- if (strchr(cp + 1, '.')) { +- clp->m_addrlist[1].s_addr = inet_addr(cp+1); +- } +- else { +- int netmask = atoi(cp + 1); +- if (0 < netmask && netmask <= 32) { +- clp->m_addrlist[1].s_addr = +- htonl ((uint32_t) ~0 << (32 - netmask)); +- } +- else { +- xlog(L_FATAL, "invalid netmask `%s' for %s", +- cp + 1, clp->m_hostname); +- } +- } +- *cp = '/'; +- clp->m_naddr = 0; +- } else if (!hp) { +- clp->m_naddr = 0; +- } else { +- char **ap = hp->h_addr_list; +- int i; +- +- for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) { +- clp->m_addrlist[i] = *(struct in_addr *)*ap; +- } +- clp->m_naddr = i; +- } +-} +- +-void +-client_add(nfs_client *clp) +-{ +- nfs_client **cpp; +- +- if (clp->m_type < 0 || clp->m_type >= MCL_MAXTYPES) +- xlog(L_FATAL, "unknown client type in client_add"); +- cpp = clientlist + clp->m_type; +- while (*cpp) +- cpp = &((*cpp)->m_next); +- clp->m_next = NULL; +- *cpp = clp; +-} +- ++/** ++ * client_release - drop a reference to an nfs_client record ++ * ++ */ + void + client_release(nfs_client *clp) + { +@@ -195,6 +231,10 @@ client_release(nfs_client *clp) + clp->m_count--; + } + ++/** ++ * client_freeall - deallocate all nfs_client records ++ * ++ */ + void + client_freeall(void) + { +@@ -205,57 +245,45 @@ client_freeall(void) + head = clientlist + i; + while (*head) { + *head = (clp = *head)->m_next; +- xfree(clp->m_hostname); +- xfree(clp); +- } +- } +-} +- +-nfs_client * +-client_find(struct hostent *hp) +-{ +- nfs_client *clp; +- int i; +- +- for (i = 0; i < MCL_MAXTYPES; i++) { +- for (clp = clientlist[i]; clp; clp = clp->m_next) { +- if (!client_check(clp, hp)) +- continue; +-#ifdef notdef +- if (clp->m_type == MCL_FQDN) +- return clp; +- return client_dup(clp, hp); +-#else +- return clp; +-#endif ++ client_free(clp); + } + } +- return NULL; + } + +-struct hostent * +-client_resolve(struct in_addr addr) ++/** ++ * client_resolve - look up an IP address ++ * @sap: pointer to socket address to resolve ++ * ++ * Returns an addrinfo structure, or NULL if some problem occurred. ++ * Caller must free the result with freeaddrinfo(3). ++ */ ++struct addrinfo * ++client_resolve(const struct sockaddr *sap) + { +- struct hostent *he = NULL; ++ struct addrinfo *ai = NULL; + + if (clientlist[MCL_WILDCARD] || clientlist[MCL_NETGROUP]) +- he = get_reliable_hostbyaddr((const char*)&addr, sizeof(addr), AF_INET); +- if (he == NULL) +- he = get_hostent((const char*)&addr, sizeof(addr), AF_INET); ++ ai = host_reliable_addrinfo(sap); ++ if (ai == NULL) ++ ai = host_numeric_addrinfo(sap); + +- return he; ++ return ai; + } + +-/* +- * Find client name given an IP address +- * This is found by gathering all known names that match that IP address, +- * sorting them and joining them with '+' ++/** ++ * client_compose - Make a list of cached hostnames that match an IP address ++ * @ai: pointer to addrinfo containing IP address information to match + * ++ * Gather all known client hostnames that match the IP address, and sort ++ * the result into a comma-separated list. ++ * ++ * Returns a '\0'-terminated ASCII string containing a comma-separated ++ * sorted list of client hostnames, or NULL if no client records matched ++ * the IP address or memory could not be allocated. Caller must free the ++ * returned string with free(3). + */ +-static char *add_name(char *old, char *add); +- + char * +-client_compose(struct hostent *he) ++client_compose(const struct addrinfo *ai) + { + char *name = NULL; + int i; +@@ -263,7 +291,7 @@ client_compose(struct hostent *he) + for (i = 0 ; i < MCL_MAXTYPES; i++) { + nfs_client *clp; + for (clp = clientlist[i]; clp ; clp = clp->m_next) { +- if (!client_check(clp, he)) ++ if (!client_check(clp, ai)) + continue; + name = add_name(name, clp->m_hostname); + } +@@ -271,13 +299,19 @@ client_compose(struct hostent *he) + return name; + } + ++/** ++ * client_member - check if @name is contained in the list @client ++ * @client: '\0'-terminated ASCII string containing ++ * comma-separated list of hostnames ++ * @name: '\0'-terminated ASCII string containing hostname to look for ++ * ++ * Returns 1 if @name was found in @client, otherwise zero is returned. ++ */ + int +-client_member(char *client, char *name) ++client_member(const char *client, const char *name) + { +- /* check if "client" (a ',' separated list of names) +- * contains 'name' as a member +- */ +- int l = strlen(name); ++ size_t l = strlen(name); ++ + while (*client) { + if (strncmp(client, name, l) == 0 && + (client[l] == ',' || client[l] == '\0')) +@@ -290,9 +324,8 @@ client_member(char *client, char *name) + return 0; + } + +- +-int +-name_cmp(char *a, char *b) ++static int ++name_cmp(const char *a, const char *b) + { + /* compare strings a and b, but only upto ',' in a */ + while (*a && *b && *a != ',' && *a == *b) +@@ -305,9 +338,9 @@ name_cmp(char *a, char *b) + } + + static char * +-add_name(char *old, char *add) ++add_name(char *old, const char *add) + { +- int len = strlen(add)+2; ++ size_t len = strlen(add) + 2; + char *new; + char *cp; + if (old) len += strlen(old); +@@ -339,105 +372,211 @@ add_name(char *old, char *add) + return new; + } + ++static _Bool ++addrs_match4(const struct sockaddr *sa1, const struct sockaddr *sa2) ++{ ++ const struct sockaddr_in *si1 = (const struct sockaddr_in *)sa1; ++ const struct sockaddr_in *si2 = (const struct sockaddr_in *)sa2; ++ ++ return si1->sin_addr.s_addr == si2->sin_addr.s_addr; ++} ++ ++static _Bool ++addrs_match(const struct sockaddr *sa1, const struct sockaddr *sa2) ++{ ++ if (sa1->sa_family == sa2->sa_family) ++ switch (sa1->sa_family) { ++ case AF_INET: ++ return addrs_match4(sa1, sa2); ++ } ++ ++ return false; ++} ++ + /* +- * Match a host (given its hostent record) to a client record. This +- * is usually called from mountd. ++ * Check each address listed in @ai against each address ++ * stored in @clp. Return 1 if a match is found, otherwise ++ * zero. + */ +-int +-client_check(nfs_client *clp, struct hostent *hp) ++static int ++check_fqdn(const nfs_client *clp, const struct addrinfo *ai) + { +- char *hname = (char *) hp->h_name; +- char *cname = clp->m_hostname; +- char **ap; ++ int i; + +- switch (clp->m_type) { +- case MCL_FQDN: +- case MCL_SUBNETWORK: +- for (ap = hp->h_addr_list; *ap; ap++) { +- if (client_checkaddr(clp, *(struct in_addr *) *ap)) ++ for (; ai; ai = ai->ai_next) ++ for (i = 0; i < clp->m_naddr; i++) ++ if (addrs_match(ai->ai_addr, get_addrlist(clp, i))) + return 1; +- } +- return 0; +- case MCL_WILDCARD: +- if (wildmat(hname, cname)) ++ ++ return 0; ++} ++ ++static _Bool ++mask_match(const uint32_t a, const uint32_t b, const uint32_t m) ++{ ++ return ((a ^ b) & m) == 0; ++} ++ ++static int ++check_subnet_v4(const struct sockaddr_in *address, ++ const struct sockaddr_in *mask, const struct addrinfo *ai) ++{ ++ for (; ai; ai = ai->ai_next) { ++ struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; ++ ++ if (sin->sin_family != AF_INET) ++ continue; ++ ++ if (mask_match(address->sin_addr.s_addr, ++ sin->sin_addr.s_addr, ++ mask->sin_addr.s_addr)) + return 1; +- else { +- for (ap = hp->h_aliases; *ap; ap++) +- if (wildmat(*ap, cname)) +- return 1; +- } +- return 0; +- case MCL_NETGROUP: +-#ifdef HAVE_INNETGR +- { +- char *dot; +- int match, i; +- struct hostent *nhp = NULL; +- struct sockaddr_in addr; +- +- /* First, try to match the hostname without +- * splitting off the domain */ +- if (innetgr(cname+1, hname, NULL, NULL)) +- return 1; ++ } ++ return 0; ++} + +- /* try the aliases as well */ +- for (i = 0; hp->h_aliases[i]; i++) { +- if (innetgr(cname+1, hp->h_aliases[i], NULL, NULL)) +- return 1; +- } +- +- /* If hname is ip address convert to FQDN */ +- if (inet_aton(hname, &addr.sin_addr) && +- (nhp = gethostbyaddr((const char *)&(addr.sin_addr), +- sizeof(addr.sin_addr), AF_INET))) { +- hname = (char *)nhp->h_name; +- if (innetgr(cname+1, hname, NULL, NULL)) +- return 1; +- } +- +- /* Okay, strip off the domain (if we have one) */ +- if ((dot = strchr(hname, '.')) == NULL) +- return 0; +- +- *dot = '\0'; +- match = innetgr(cname+1, hname, NULL, NULL); +- *dot = '.'; +- +- return match; +- } +-#else +- return 0; +-#endif +- case MCL_ANONYMOUS: ++/* ++ * Check each address listed in @ai against the subnetwork or ++ * host address stored in @clp. Return 1 if an address in @hp ++ * matches the host address stored in @clp, otherwise zero. ++ */ ++static int ++check_subnetwork(const nfs_client *clp, const struct addrinfo *ai) ++{ ++ switch (get_addrlist(clp, 0)->sa_family) { ++ case AF_INET: ++ return check_subnet_v4(get_addrlist_in(clp, 0), ++ get_addrlist_in(clp, 1), ai); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Check if a wildcard nfs_client record matches the canonical name ++ * or the aliases of a host. Return 1 if a match is found, otherwise ++ * zero. ++ */ ++static int ++check_wildcard(const nfs_client *clp, const struct addrinfo *ai) ++{ ++ char *cname = clp->m_hostname; ++ char *hname = ai->ai_canonname; ++ struct hostent *hp; ++ char **ap; ++ ++ if (wildmat(hname, cname)) + return 1; +- case MCL_GSS: +- return 0; +- default: +- xlog(L_FATAL, "internal: bad client type %d", clp->m_type); ++ ++ /* See if hname aliases listed in /etc/hosts or nis[+] ++ * match the requested wildcard */ ++ hp = gethostbyname(hname); ++ if (hp != NULL) { ++ for (ap = hp->h_aliases; *ap; ap++) ++ if (wildmat(*ap, cname)) ++ return 1; + } + + return 0; + } + ++/* ++ * Check if @ai's hostname or aliases fall in a given netgroup. ++ * Return 1 if @ai represents a host in the netgroup, otherwise ++ * zero. ++ */ ++#ifdef HAVE_INNETGR + static int +-client_checkaddr(nfs_client *clp, struct in_addr addr) ++check_netgroup(const nfs_client *clp, const struct addrinfo *ai) + { +- int i; ++ const char *netgroup = clp->m_hostname + 1; ++ const char *hname = ai->ai_canonname; ++ struct addrinfo *tmp = NULL; ++ struct hostent *hp; ++ int i, match; ++ char *dot; ++ ++ /* First, try to match the hostname without ++ * splitting off the domain */ ++ if (innetgr(netgroup, hname, NULL, NULL)) ++ return 1; + +- switch (clp->m_type) { +- case MCL_FQDN: +- for (i = 0; i < clp->m_naddr; i++) { +- if (clp->m_addrlist[i].s_addr == addr.s_addr) ++ /* See if hname aliases listed in /etc/hosts or nis[+] ++ * match the requested netgroup */ ++ hp = gethostbyname(hname); ++ if (hp != NULL) { ++ for (i = 0; hp->h_aliases[i]; i++) ++ if (innetgr(netgroup, hp->h_aliases[i], NULL, NULL)) + return 1; +- } ++ } ++ ++ /* If hname is ip address convert to FQDN */ ++ tmp = host_pton(hname); ++ if (tmp != NULL) { ++ freeaddrinfo(tmp); ++ if (innetgr(netgroup, hname, NULL, NULL)) ++ return 1; ++ } ++ ++ /* Okay, strip off the domain (if we have one) */ ++ dot = strchr(hname, '.'); ++ if (dot == NULL) + return 0; ++ ++ *dot = '\0'; ++ match = innetgr(netgroup, hname, NULL, NULL); ++ *dot = '.'; ++ ++ return match; ++} ++#else /* !HAVE_INNETGR */ ++static int ++check_netgroup(__attribute__((unused)) const nfs_client *clp, ++ __attribute__((unused)) const struct addrinfo *ai) ++{ ++ return 0; ++} ++#endif /* !HAVE_INNETGR */ ++ ++/** ++ * client_check - check if IP address information matches a cached nfs_client ++ * @clp: pointer to a cached nfs_client record ++ * @ai: pointer to addrinfo to compare it with ++ * ++ * Returns 1 if the address information matches the cached nfs_client, ++ * otherwise zero. ++ */ ++int ++client_check(const nfs_client *clp, const struct addrinfo *ai) ++{ ++ switch (clp->m_type) { ++ case MCL_FQDN: ++ return check_fqdn(clp, ai); + case MCL_SUBNETWORK: +- return !((clp->m_addrlist[0].s_addr ^ addr.s_addr) +- & clp->m_addrlist[1].s_addr); ++ return check_subnetwork(clp, ai); ++ case MCL_WILDCARD: ++ return check_wildcard(clp, ai); ++ case MCL_NETGROUP: ++ return check_netgroup(clp, ai); ++ case MCL_ANONYMOUS: ++ return 1; ++ case MCL_GSS: ++ return 0; ++ default: ++ xlog(D_GENERAL, "%s: unrecognized client type: %d", ++ __func__, clp->m_type); + } ++ + return 0; + } + ++/** ++ * client_gettype - determine type of nfs_client given an identifier ++ * @ident: '\0'-terminated ASCII string containing a client identifier ++ * ++ * Returns the type of nfs_client record that would be used for ++ * this client. ++ */ + int + client_gettype(char *ident) + { +diff --git a/support/export/export.c b/support/export/export.c +index 2943466..f528603 100644 +--- a/support/export/export.c ++++ b/support/export/export.c +@@ -24,9 +24,24 @@ static int export_hash(char *); + + static void export_init(nfs_export *exp, nfs_client *clp, + struct exportent *nep); +-static int export_check(nfs_export *, struct hostent *, char *); ++static void export_add(nfs_export *exp); ++static int export_check(const nfs_export *exp, const struct addrinfo *ai, ++ const char *path); + static nfs_export * +- export_allowed_internal(struct hostent *hp, char *path); ++ export_allowed_internal(const struct addrinfo *ai, ++ const char *path); ++ ++static void ++export_free(nfs_export *exp) ++{ ++ xfree(exp->m_export.e_squids); ++ xfree(exp->m_export.e_sqgids); ++ free(exp->m_export.e_mountpoint); ++ free(exp->m_export.e_fslocdata); ++ ++ xfree(exp->m_export.e_hostname); ++ xfree(exp); ++} + + static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep) + { +@@ -44,7 +59,12 @@ static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep) + } + } + +-int ++/** ++ * export_read - read entries from /etc/exports ++ * @fname: name of file to read from ++ * ++ */ ++void + export_read(char *fname) + { + struct exportent *eep; +@@ -59,11 +79,15 @@ export_read(char *fname) + warn_duplicated_exports(exp, eep); + } + endexportent(); +- return 0; + } + +-/* +- * Create an in-core export struct from an export entry. ++/** ++ * export_create - create an in-core nfs_export record from an export entry ++ * @xep: export entry to lookup ++ * @canonical: if set, e_hostname is known to be canonical DNS name ++ * ++ * Returns a freshly instantiated export record, or NULL if ++ * a problem occurred. + */ + nfs_export * + export_create(struct exportent *xep, int canonical) +@@ -105,8 +129,8 @@ export_init(nfs_export *exp, nfs_client *clp, struct exportent *nep) + * original hostname from /etc/exports, while the in-core client struct + * gets the newly found FQDN. + */ +-nfs_export * +-export_dup(nfs_export *exp, struct hostent *hp) ++static nfs_export * ++export_dup(nfs_export *exp, const struct addrinfo *ai) + { + nfs_export *new; + nfs_client *clp; +@@ -116,7 +140,11 @@ export_dup(nfs_export *exp, struct hostent *hp) + dupexportent(&new->m_export, &exp->m_export); + if (exp->m_export.e_hostname) + new->m_export.e_hostname = xstrdup(exp->m_export.e_hostname); +- clp = client_dup(exp->m_client, hp); ++ clp = client_dup(exp->m_client, ai); ++ if (clp == NULL) { ++ export_free(new); ++ return NULL; ++ } + clp->m_count++; + new->m_client = clp; + new->m_mayexport = exp->m_mayexport; +@@ -128,10 +156,8 @@ export_dup(nfs_export *exp, struct hostent *hp) + + return new; + } +-/* +- * Add export entry to hash table +- */ +-void ++ ++static void + export_add(nfs_export *exp) + { + exp_hash_table *p_tbl; +@@ -159,19 +185,27 @@ export_add(nfs_export *exp) + } + } + ++/** ++ * export_find - find or create a suitable nfs_export for @ai and @path ++ * @ai: pointer to addrinfo for client ++ * @path: '\0'-terminated ASCII string containing export path ++ * ++ * Returns a pointer to nfs_export data matching @ai and @path, ++ * or NULL if an error occurs. ++ */ + nfs_export * +-export_find(struct hostent *hp, char *path) ++export_find(const struct addrinfo *ai, const char *path) + { + nfs_export *exp; + int i; + + for (i = 0; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { +- if (!export_check(exp, hp, path)) ++ if (!export_check(exp, ai, path)) + continue; + if (exp->m_client->m_type == MCL_FQDN) + return exp; +- return export_dup(exp, hp); ++ return export_dup(exp, ai); + } + } + +@@ -179,7 +213,7 @@ export_find(struct hostent *hp, char *path) + } + + static nfs_export * +-export_allowed_internal (struct hostent *hp, char *path) ++export_allowed_internal(const struct addrinfo *ai, const char *path) + { + nfs_export *exp; + int i; +@@ -187,7 +221,7 @@ export_allowed_internal (struct hostent *hp, char *path) + for (i = 0; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + if (!exp->m_mayexport || +- !export_check(exp, hp, path)) ++ !export_check(exp, ai, path)) + continue; + return exp; + } +@@ -196,8 +230,16 @@ export_allowed_internal (struct hostent *hp, char *path) + return NULL; + } + ++/** ++ * export_allowed - determine if this export is allowed ++ * @ai: pointer to addrinfo for client ++ * @path: '\0'-terminated ASCII string containing export path ++ * ++ * Returns a pointer to nfs_export data matching @ai and @path, ++ * or NULL if the export is not allowed. ++ */ + nfs_export * +-export_allowed(struct hostent *hp, char *path) ++export_allowed(const struct addrinfo *ai, const char *path) + { + nfs_export *exp; + char epath[MAXPATHLEN+1]; +@@ -210,7 +252,7 @@ export_allowed(struct hostent *hp, char *path) + + /* Try the longest matching exported pathname. */ + while (1) { +- exp = export_allowed_internal (hp, epath); ++ exp = export_allowed_internal(ai, epath); + if (exp) + return exp; + /* We have to treat the root, "/", specially. */ +@@ -223,11 +265,17 @@ export_allowed(struct hostent *hp, char *path) + return NULL; + } + +-/* +- * Search hash table for export entry. +- */ ++/** ++ * export_lookup - search hash table for export entry ++ * @hname: '\0'-terminated ASCII string containing client hostname to look for ++ * @path: '\0'-terminated ASCII string containing export path to look for ++ * @canonical: if set, @hname is known to be canonical DNS name ++ * ++ * Returns a pointer to nfs_export record matching @hname and @path, ++ * or NULL if the export was not found. ++ */ + nfs_export * +-export_lookup(char *hname, char *path, int canonical) ++export_lookup(char *hname, char *path, int canonical) + { + nfs_client *clp; + nfs_export *exp; +@@ -251,14 +299,18 @@ export_lookup(char *hname, char *path, int canonical) + } + + static int +-export_check(nfs_export *exp, struct hostent *hp, char *path) ++export_check(const nfs_export *exp, const struct addrinfo *ai, const char *path) + { + if (strcmp(path, exp->m_export.e_path)) + return 0; + +- return client_check(exp->m_client, hp); ++ return client_check(exp->m_client, ai); + } + ++/** ++ * export_freeall - deallocate all nfs_export records ++ * ++ */ + void + export_freeall(void) + { +@@ -269,22 +321,13 @@ export_freeall(void) + for (exp = exportlist[i].p_head; exp; exp = nxt) { + nxt = exp->m_next; + client_release(exp->m_client); +- if (exp->m_export.e_squids) +- xfree(exp->m_export.e_squids); +- if (exp->m_export.e_sqgids) +- xfree(exp->m_export.e_sqgids); +- if (exp->m_export.e_mountpoint) +- free(exp->m_export.e_mountpoint); +- if (exp->m_export.e_fslocdata) +- xfree(exp->m_export.e_fslocdata); +- xfree(exp->m_export.e_hostname); +- xfree(exp); ++ export_free(exp); ++ } ++ for (j = 0; j < HASH_TABLE_SIZE; j++) { ++ exportlist[i].entries[j].p_first = NULL; ++ exportlist[i].entries[j].p_last = NULL; + } +- for(j = 0; j < HASH_TABLE_SIZE; j++) { +- exportlist[i].entries[j].p_first = NULL; +- exportlist[i].entries[j].p_last = NULL; +- } +- exportlist[i].p_head = NULL; ++ exportlist[i].p_head = NULL; + } + client_freeall(); + } +diff --git a/support/export/hostname.c b/support/export/hostname.c +index 8a23a89..232e040 100644 +--- a/support/export/hostname.c ++++ b/support/export/hostname.c +@@ -1,315 +1,377 @@ + /* +- * support/export/hostname.c ++ * Copyright 2010 Oracle. All rights reserved. + * +- * Functions for hostname. ++ * This file is part of nfs-utils. + * ++ * nfs-utils 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. ++ * ++ * nfs-utils 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 nfs-utils. If not, see . + */ + + #ifdef HAVE_CONFIG_H + #include + #endif + +-/* +-#define TEST +-*/ +- + #include +-#include +-#include +-#include + #include +-#include +-#ifdef TEST +-#define xmalloc malloc +-#else +-#include "xmalloc.h" +-#include "misc.h" +-#endif ++#include ++#include ++#include ++ ++#include "sockaddr.h" ++#include "exportfs.h" + +-#define ALIGNMENT sizeof (char *) ++#ifndef HAVE_DECL_AI_ADDRCONFIG ++#define AI_ADDRCONFIG 0 ++#endif + +-static int +-align (int len, int al) ++#ifdef HAVE_GETNAMEINFO ++static socklen_t ++sockaddr_size(const struct sockaddr *sap) + { +- int i; +- i = len % al; +- if (i) +- len += al - i; +- return len; ++ if (sap->sa_family != AF_INET) ++ return 0; ++ return (socklen_t)sizeof(struct sockaddr_in); + } ++#endif /* HAVE_GETNAMEINFO */ ++ ++/** ++ * host_ntop - generate presentation address given a sockaddr ++ * @sap: pointer to socket address ++ * @buf: working storage ++ * @buflen: size of @buf in bytes ++ * ++ * Returns a pointer to a @buf. ++ */ ++#ifdef HAVE_GETNAMEINFO ++char * ++host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen) ++{ ++ socklen_t salen = sockaddr_size(sap); ++ int error; ++ ++ memset(buf, 0, buflen); ++ ++ if (salen == 0) { ++ (void)strncpy(buf, "bad family", buflen - 1); ++ return buf; ++ } ++ ++ error = getnameinfo(sap, salen, buf, (socklen_t)buflen, ++ NULL, 0, NI_NUMERICHOST); ++ if (error != 0) { ++ buf[0] = '\0'; ++ (void)strncpy(buf, "bad address", buflen - 1); ++ } + +-struct hostent * +-get_hostent (const char *addr, int len, int type) ++ return buf; ++} ++#else /* !HAVE_GETNAMEINFO */ ++char * ++host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen) + { +- struct hostent *cp; +- int len_ent; +- const char *name; +- int len_name; +- int num_aliases = 1; +- int len_aliases = sizeof (char *); +- int num_addr_list = 1; +- int len_addr_list = sizeof (char *); +- int pos; +- struct in_addr *ipv4; +- +- switch (type) +- { +- case AF_INET: +- ipv4 = (struct in_addr *) addr; +- name = inet_ntoa (*ipv4); +- break; +- +- default: +- return NULL; +- } +- +- len_ent = align (sizeof (*cp), ALIGNMENT); +- len_name = align (strlen (name) + 1, ALIGNMENT); +- +- num_addr_list++; +- len_addr_list += align (len, ALIGNMENT) + sizeof (char *); +- +- cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases +- + len_addr_list); +- +- cp->h_addrtype = type; +- cp->h_length = len; +- pos = len_ent; +- cp->h_name = (char *) &(((char *) cp) [pos]); +- strcpy (cp->h_name, name); +- +- pos += len_name; +- cp->h_aliases = (char **) &(((char *) cp) [pos]); +- pos += num_aliases * sizeof (char *); +- cp->h_aliases [0] = NULL; +- +- pos = len_ent + len_name + len_aliases; +- cp->h_addr_list = (char **) &(((char *) cp) [pos]); +- pos += num_addr_list * sizeof (char *); +- cp->h_addr_list [0] = (char *) &(((char *) cp) [pos]); +- memcpy (cp->h_addr_list [0], addr, cp->h_length); +- pos += align (cp->h_length, ALIGNMENT); +- cp->h_addr_list [1] = NULL; +- +- return cp; ++ const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap; ++ ++ memset(buf, 0, buflen); ++ ++ if (sin->sin_family != AF_INET) ++ (void)strncpy(buf, "bad family", buflen - 1); ++ return buf; ++ } ++ ++ if (inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, buflen) != NULL) ++ return buf; ++ ++ buf[0] = '\0'; ++ (void)strncpy(buf, "bad address", buflen - 1); ++ return buf; + } ++#endif /* !HAVE_GETNAMEINFO */ + +-struct hostent * +-hostent_dup (struct hostent *hp) ++/** ++ * host_pton - return addrinfo for a given presentation address ++ * @paddr: pointer to a '\0'-terminated ASCII string containing an ++ * IP presentation address ++ * ++ * Returns address info structure, or NULL if an error occurs. Caller ++ * must free the returned structure with freeaddrinfo(3). ++ */ ++__attribute_malloc__ ++struct addrinfo * ++host_pton(const char *paddr) + { +- int len_ent = align (sizeof (*hp), ALIGNMENT); +- int len_name = align (strlen (hp->h_name) + 1, ALIGNMENT); +- int num_aliases = 1; +- int len_aliases = sizeof (char *); +- int num_addr_list = 1; +- int len_addr_list = sizeof (char *); +- int pos, i; +- char **sp; +- struct hostent *cp; +- +- for (sp = hp->h_aliases; sp && *sp; sp++) +- { +- num_aliases++; +- len_aliases += align (strlen (*sp) + 1, ALIGNMENT) +- + sizeof (char *); +- } +- +- for (sp = hp->h_addr_list; *sp; sp++) +- { +- num_addr_list++; +- len_addr_list += align (hp->h_length, ALIGNMENT) +- + sizeof (char *); +- } +- +- cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases +- + len_addr_list); +- +- *cp = *hp; +- pos = len_ent; +- cp->h_name = (char *) &(((char *) cp) [pos]); +- strcpy (cp->h_name, hp->h_name); +- +- pos += len_name; +- cp->h_aliases = (char **) &(((char *) cp) [pos]); +- pos += num_aliases * sizeof (char *); +- for (sp = hp->h_aliases, i = 0; i < num_aliases; i++, sp++) +- if (sp && *sp) +- { +- cp->h_aliases [i] = (char *) &(((char *) cp) [pos]); +- strcpy (cp->h_aliases [i], *sp); +- pos += align (strlen (*sp) + 1, ALIGNMENT); +- } +- else +- cp->h_aliases [i] = NULL; +- +- pos = len_ent + len_name + len_aliases; +- cp->h_addr_list = (char **) &(((char *) cp) [pos]); +- pos += num_addr_list * sizeof (char *); +- for (sp = hp->h_addr_list, i = 0; i < num_addr_list; i++, sp++) +- if (*sp) +- { +- cp->h_addr_list [i] = (char *) &(((char *) cp) [pos]); +- memcpy (cp->h_addr_list [i], *sp, hp->h_length); +- pos += align (hp->h_length, ALIGNMENT); +- } +- else +- cp->h_addr_list [i] = *sp; +- +- return cp; ++ struct addrinfo *ai = NULL; ++ struct addrinfo hint = { ++ /* don't return duplicates */ ++ .ai_protocol = (int)IPPROTO_UDP, ++ .ai_flags = AI_NUMERICHOST, ++ .ai_family = AF_UNSPEC, ++ }; ++ struct sockaddr_in sin; ++ int error; ++ ++ /* ++ * Although getaddrinfo(3) is easier to use and supports ++ * IPv6, it recognizes incomplete addresses like "10.4" ++ * as valid AF_INET addresses. It also accepts presentation ++ * addresses that end with a blank. ++ * ++ * inet_pton(3) is much stricter. Use it to be certain we ++ * have a real AF_INET presentation address, before invoking ++ * getaddrinfo(3) to generate the full addrinfo list. ++ */ ++ if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0) ++ return NULL; ++ ++ error = getaddrinfo(paddr, NULL, &hint, &ai); ++ switch (error) { ++ case 0: ++ return ai; ++ case EAI_NONAME: ++ if (paddr == NULL) ++ xlog(D_GENERAL, "%s: passed a NULL presentation address", ++ __func__); ++ break; ++ case EAI_SYSTEM: ++ xlog(D_GENERAL, "%s: failed to convert %s: (%d) %m", ++ __func__, paddr, errno); ++ break; ++ default: ++ xlog(D_GENERAL, "%s: failed to convert %s: %s", ++ __func__, paddr, gai_strerror(error)); ++ break; ++ } ++ ++ return NULL; + } + +-static int +-is_hostname(const char *sp) ++/** ++ * host_addrinfo - return addrinfo for a given hostname ++ * @hostname: pointer to a '\0'-terminated ASCII string containing a hostname ++ * ++ * Returns address info structure with ai_canonname filled in, or NULL ++ * if no information is available for @hostname. Caller must free the ++ * returned structure with freeaddrinfo(3). ++ */ ++__attribute_malloc__ ++struct addrinfo * ++host_addrinfo(const char *hostname) + { +- if (*sp == '\0' || *sp == '@') +- return 0; +- +- for (; *sp; sp++) +- { +- if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/') +- return 0; +- if (*sp == '\\' && sp[1]) +- sp++; +- } +- +- return 1; ++ struct addrinfo *ai = NULL; ++ struct addrinfo hint = { ++ .ai_family = AF_INET, ++ /* don't return duplicates */ ++ .ai_protocol = (int)IPPROTO_UDP, ++ .ai_flags = AI_ADDRCONFIG | AI_CANONNAME, ++ }; ++ int error; ++ ++ error = getaddrinfo(hostname, NULL, &hint, &ai); ++ switch (error) { ++ case 0: ++ return ai; ++ case EAI_SYSTEM: ++ xlog(D_GENERAL, "%s: failed to resolve %s: (%d) %m", ++ __func__, hostname, errno); ++ break; ++ default: ++ xlog(D_GENERAL, "%s: failed to resolve %s: %s", ++ __func__, hostname, gai_strerror(error)); ++ break; ++ } ++ ++ return NULL; + } + +-int +-matchhostname (const char *h1, const char *h2) ++/** ++ * host_canonname - return canonical hostname bound to an address ++ * @sap: pointer to socket address to look up ++ * ++ * Discover the canonical hostname associated with the given socket ++ * address. The host's reverse mapping is verified in the process. ++ * ++ * Returns a '\0'-terminated ASCII string containing a hostname, or ++ * NULL if no hostname can be found for @sap. Caller must free ++ * the string. ++ */ ++#ifdef HAVE_GETNAMEINFO ++__attribute_malloc__ ++char * ++host_canonname(const struct sockaddr *sap) + { +- struct hostent *hp1, *hp2; +- int status; +- +- if (strcasecmp (h1, h2) == 0) +- return 1; +- +- if (!is_hostname (h1) || !is_hostname (h2)) +- return 0; +- +- hp1 = gethostbyname (h1); +- if (hp1 == NULL) +- return 0; +- +- hp1 = hostent_dup (hp1); +- +- hp2 = gethostbyname (h2); +- if (hp2) +- { +- if (strcasecmp (hp1->h_name, hp2->h_name) == 0) +- status = 1; +- else +- { +- char **ap1, **ap2; +- +- status = 0; +- for (ap1 = hp1->h_addr_list; *ap1 && status == 0; ap1++) +- for (ap2 = hp2->h_addr_list; *ap2; ap2++) +- if (memcmp (*ap1, *ap2, sizeof (struct in_addr)) == 0) +- { +- status = 1; +- break; +- } ++ socklen_t salen = sockaddr_size(sap); ++ char buf[NI_MAXHOST]; ++ int error; ++ ++ if (salen == 0) { ++ xlog(D_GENERAL, "%s: unsupported address family %d", ++ __func__, sap->sa_family); ++ return NULL; ++ } ++ ++ memset(buf, 0, sizeof(buf)); ++ error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), ++ NULL, 0, NI_NAMEREQD); ++ switch (error) { ++ case 0: ++ break; ++ case EAI_SYSTEM: ++ xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m", ++ __func__, errno); ++ return NULL; ++ default: ++ (void)getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), ++ NULL, 0, NI_NUMERICHOST); ++ xlog(D_GENERAL, "%s: failed to resolve %s: %s", ++ __func__, buf, gai_strerror(error)); ++ return NULL; + } +- } +- else +- status = 0; + +- free (hp1); +- return status; ++ return strdup(buf); + } ++#else /* !HAVE_GETNAMEINFO */ ++__attribute_malloc__ ++char * ++host_canonname(const struct sockaddr *sap) ++{ ++ const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap; ++ const struct in_addr *addr = &sin->sin_addr; ++ struct hostent *hp; ++ ++ if (sap->sa_family != AF_INET) ++ return NULL; ++ ++ hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET); ++ if (hp == NULL) ++ return NULL; + ++ return strdup(hp->h_name); ++} ++#endif /* !HAVE_GETNAMEINFO */ + +-/* Map IP to hostname, and then map back to addr to make sure it is a +- * reliable hostname ++/** ++ * host_reliable_addrinfo - return addrinfo for a given address ++ * @sap: pointer to socket address to look up ++ * ++ * Reverse and forward lookups are performed to ensure the address has ++ * proper forward and reverse mappings. ++ * ++ * Returns address info structure with ai_canonname filled in, or NULL ++ * if no information is available for @sap. Caller must free the returned ++ * structure with freeaddrinfo(3). + */ +-struct hostent * +-get_reliable_hostbyaddr(const char *addr, int len, int type) ++__attribute_malloc__ ++struct addrinfo * ++host_reliable_addrinfo(const struct sockaddr *sap) + { +- struct hostent *hp = NULL; +- +- struct hostent *reverse; +- struct hostent *forward; +- char **sp; ++ struct addrinfo *ai; ++ char *hostname; + +- reverse = gethostbyaddr (addr, len, type); +- if (!reverse) ++ hostname = host_canonname(sap); ++ if (hostname == NULL) + return NULL; + +- /* must make sure the hostent is authorative. */ ++ ai = host_addrinfo(hostname); + +- reverse = hostent_dup (reverse); +- forward = gethostbyname (reverse->h_name); ++ free(hostname); ++ return ai; ++} + +- if (forward) { +- /* now make sure the "addr" is in the list */ +- for (sp = forward->h_addr_list ; *sp ; sp++) { +- if (memcmp (*sp, addr, forward->h_length) == 0) +- break; +- } ++/** ++ * host_numeric_addrinfo - return addrinfo without doing DNS queries ++ * @sap: pointer to socket address ++ * ++ * Returns address info structure, or NULL if an error occurred. ++ * Caller must free the returned structure with freeaddrinfo(3). ++ */ ++#ifdef HAVE_GETNAMEINFO ++__attribute_malloc__ ++struct addrinfo * ++host_numeric_addrinfo(const struct sockaddr *sap) ++{ ++ socklen_t salen = sockaddr_size(sap); ++ char buf[INET_ADDRSTRLEN]; ++ struct addrinfo *ai; ++ int error; ++ ++ if (salen == 0) { ++ xlog(D_GENERAL, "%s: unsupported address family %d", ++ __func__, sap->sa_family); ++ return NULL; ++ } + +- if (*sp) { +- /* it's valid */ +- hp = hostent_dup (forward); +- } +- else { +- /* it was a FAKE */ +- xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't match reverse", +- reverse->h_name, inet_ntoa(*(struct in_addr*)addr)); +- } ++ memset(buf, 0, sizeof(buf)); ++ error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), ++ NULL, 0, NI_NUMERICHOST); ++ switch (error) { ++ case 0: ++ break; ++ case EAI_SYSTEM: ++ xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m", ++ __func__, errno); ++ return NULL; ++ default: ++ xlog(D_GENERAL, "%s: getnameinfo(3) failed: %s", ++ __func__, gai_strerror(error)); ++ return NULL; + } +- else { +- /* never heard of it. misconfigured DNS? */ +- xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't exist", +- reverse->h_name, inet_ntoa(*(struct in_addr*)addr)); ++ ++ ai = host_pton(buf); ++ ++ /* ++ * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname ++ */ ++ if (ai != NULL) { ++ free(ai->ai_canonname); /* just in case */ ++ ai->ai_canonname = strdup(buf); ++ if (ai->ai_canonname == NULL) { ++ freeaddrinfo(ai); ++ ai = NULL; ++ } + } + +- free (reverse); +- return hp; ++ return ai; + } ++#else /* !HAVE_GETNAMEINFO */ ++__attribute_malloc__ ++struct addrinfo * ++host_numeric_addrinfo(const struct sockaddr *sap) ++{ ++ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; ++ const struct in_addr *addr = &sin->sin_addr; ++ char buf[INET_ADDRSTRLEN]; ++ struct addrinfo *ai; + ++ if (sap->sa_family != AF_INET) ++ return NULL; + +-#ifdef TEST +-void +-print_host (struct hostent *hp) +-{ +- char **sp; +- +- if (hp) +- { +- printf ("official hostname: %s\n", hp->h_name); +- printf ("aliases:\n"); +- for (sp = hp->h_aliases; *sp; sp++) +- printf (" %s\n", *sp); +- printf ("IP addresses:\n"); +- for (sp = hp->h_addr_list; *sp; sp++) +- printf (" %s\n", inet_ntoa (*(struct in_addr *) *sp)); +- } +- else +- printf ("Not host information\n"); +-} ++ memset(buf, 0, sizeof(buf)); ++ if (inet_ntop(AF_INET, (char *)addr, buf, ++ (socklen_t)sizeof(buf)) == NULL) ++ return NULL; + +-int +-main (int argc, char **argv) +-{ +- struct hostent *hp = gethostbyname (argv [1]); +- struct hostent *cp; +- struct in_addr addr; +- +- print_host (hp); +- +- if (hp) +- { +- cp = hostent_dup (hp); +- print_host (cp); +- free (cp); +- } +- printf ("127.0.0.1 == %s: %d\n", argv [1], +- matchhostname ("127.0.0.1", argv [1])); +- addr.s_addr = inet_addr(argv [2]); +- printf ("%s\n", inet_ntoa (addr)); +- cp = get_hostent ((const char *)&addr, sizeof(addr), AF_INET); +- print_host (cp); +- return 0; ++ ai = host_pton(buf); ++ ++ /* ++ * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname ++ */ ++ if (ai != NULL) { ++ ai->ai_canonname = strdup(buf); ++ if (ai->ai_canonname == NULL) { ++ freeaddrinfo(ai); ++ ai = NULL; ++ } ++ } ++ ++ return ai; + } +-#endif ++#endif /* !HAVE_GETNAMEINFO */ +diff --git a/support/export/nfsctl.c b/support/export/nfsctl.c +index e2877b9..3b9876a 100644 +--- a/support/export/nfsctl.c ++++ b/support/export/nfsctl.c +@@ -66,7 +66,7 @@ str_tolower(char *s) + static int + cltsetup(struct nfsctl_client *cltarg, nfs_client *clp) + { +- int i; ++ int i, j; + + if (clp->m_type != MCL_FQDN) { + xlog(L_ERROR, "internal: can't export non-FQDN host"); +@@ -76,10 +76,19 @@ cltsetup(struct nfsctl_client *cltarg, nfs_client *clp) + strncpy(cltarg->cl_ident, clp->m_hostname, + sizeof (cltarg->cl_ident) - 1); + str_tolower(cltarg->cl_ident); +- cltarg->cl_naddr = clp->m_naddr; +- for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) +- cltarg->cl_addrlist[i] = clp->m_addrlist[i]; + ++ j = 0; ++ for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) { ++ const struct sockaddr_in *sin = get_addrlist_in(clp, i); ++ if (sin->sin_family == AF_INET) ++ cltarg->cl_addrlist[j++] = sin->sin_addr; ++ } ++ if (j == 0) { ++ xlog(L_ERROR, "internal: no supported addresses in nfs_client"); ++ return 0; ++ } ++ ++ cltarg->cl_naddr = j; + return 1; + } + +diff --git a/support/export/rmtab.c b/support/export/rmtab.c +index b49e1aa..31c0f50 100644 +--- a/support/export/rmtab.c ++++ b/support/export/rmtab.c +@@ -19,39 +19,54 @@ + #include "xio.h" + #include "xlog.h" + ++/* ++ * See if the entry already exists. If not, ++ * this was an instantiated wild card, and we ++ * must add it. ++ */ ++static void ++rmtab_read_wildcard(struct rmtabent *rep) ++{ ++ nfs_export *exp, *exp2; ++ struct addrinfo *ai; ++ ++ ai = host_addrinfo(rep->r_client); ++ if (ai == NULL) ++ return; ++ ++ exp = export_allowed(ai, rep->r_path); ++ freeaddrinfo(ai); ++ if (exp == NULL) ++ return; ++ ++ exp2 = export_lookup(rep->r_client, exp->m_export.e_path, 0); ++ if (exp2 == NULL) { ++ struct exportent ee; ++ ++ memset(&ee, 0, sizeof(ee)); ++ dupexportent(&ee, &exp->m_export); ++ ++ ee.e_hostname = rep->r_client; ++ exp2 = export_create(&ee, 0); ++ exp2->m_changed = exp->m_changed; ++ } ++ exp2->m_mayexport = 1; ++} ++ + int + rmtab_read(void) + { + struct rmtabent *rep; +- nfs_export *exp = NULL; + + setrmtabent("r"); + while ((rep = getrmtabent(1, NULL)) != NULL) { +- struct hostent *hp = NULL; + int htype; +- ++ + htype = client_gettype(rep->r_client); +- if ((htype == MCL_FQDN || htype == MCL_SUBNETWORK) +- && (hp = gethostbyname (rep->r_client)) +- && (hp = hostent_dup (hp), +- exp = export_allowed (hp, rep->r_path))) { +- /* see if the entry already exists, otherwise this was an instantiated +- * wild card, and we must add it +- */ +- nfs_export *exp2 = export_lookup(rep->r_client, +- exp->m_export.e_path, 0); +- if (!exp2) { +- struct exportent ee; +- dupexportent(&ee, &exp->m_export); +- ee.e_hostname = rep->r_client; +- exp2 = export_create(&ee, 0); +- exp2->m_changed = exp->m_changed; +- } +- free (hp); +- exp2->m_mayexport = 1; +- } else if (hp) /* export_allowed failed */ +- free(hp); ++ if (htype == MCL_FQDN || htype == MCL_SUBNETWORK) ++ rmtab_read_wildcard(rep); + } ++ + if (errno == EINVAL) { + /* Something goes wrong. We need to fix the rmtab + file. */ +diff --git a/support/include/exportfs.h b/support/include/exportfs.h +index 470b2ec..3cf1ee8 100644 +--- a/support/include/exportfs.h ++++ b/support/include/exportfs.h +@@ -10,6 +10,8 @@ + #define EXPORTFS_H + + #include ++ ++#include "sockaddr.h" + #include "nfslib.h" + + enum { +@@ -35,11 +37,56 @@ typedef struct mclient { + char * m_hostname; + int m_type; + int m_naddr; +- struct in_addr m_addrlist[NFSCLNT_ADDRMAX]; ++ union nfs_sockaddr m_addrlist[NFSCLNT_ADDRMAX]; + int m_exported; /* exported to nfsd */ + int m_count; + } nfs_client; + ++static inline const struct sockaddr * ++get_addrlist(const nfs_client *clp, const int i) ++{ ++ return &clp->m_addrlist[i].sa; ++} ++ ++static inline const struct sockaddr_in * ++get_addrlist_in(const nfs_client *clp, const int i) ++{ ++ return &clp->m_addrlist[i].s4; ++} ++ ++static inline const struct sockaddr_in6 * ++get_addrlist_in6(const nfs_client *clp, const int i) ++{ ++ return &clp->m_addrlist[i].s6; ++} ++ ++static inline void ++set_addrlist_in(nfs_client *clp, const int i, const struct sockaddr_in *sin) ++{ ++ memcpy(&clp->m_addrlist[i].s4, sin, sizeof(*sin)); ++} ++ ++static inline void ++set_addrlist_in6(nfs_client *clp, const int i, const struct sockaddr_in6 *sin6) ++{ ++ memcpy(&clp->m_addrlist[i].s6, sin6, sizeof(*sin6)); ++} ++ ++static inline void ++set_addrlist(nfs_client *clp, const int i, const struct sockaddr *sap) ++{ ++ switch (sap->sa_family) { ++ case AF_INET: ++ memcpy(&clp->m_addrlist[i].s4, sap, sizeof(struct sockaddr_in)); ++ break; ++#ifdef IPV6_SUPPORTED ++ case AF_INET6: ++ memcpy(&clp->m_addrlist[i].s6, sap, sizeof(struct sockaddr_in6)); ++ break; ++#endif ++ } ++} ++ + typedef struct mexport { + struct mexport * m_next; + struct mclient * m_client; +@@ -69,26 +116,26 @@ extern exp_hash_table exportlist[MCL_MAXTYPES]; + extern nfs_client * clientlist[MCL_MAXTYPES]; + + nfs_client * client_lookup(char *hname, int canonical); +-nfs_client * client_find(struct hostent *); +-void client_add(nfs_client *); +-nfs_client * client_dup(nfs_client *, struct hostent *); ++nfs_client * client_dup(const nfs_client *clp, ++ const struct addrinfo *ai); + int client_gettype(char *hname); +-int client_check(nfs_client *, struct hostent *); +-int client_match(nfs_client *, char *hname); ++int client_check(const nfs_client *clp, ++ const struct addrinfo *ai); + void client_release(nfs_client *); + void client_freeall(void); +-char * client_compose(struct hostent *he); +-struct hostent * client_resolve(struct in_addr addr); +-int client_member(char *client, char *name); ++char * client_compose(const struct addrinfo *ai); ++struct addrinfo * client_resolve(const struct sockaddr *sap); ++int client_member(const char *client, ++ const char *name); + +-int export_read(char *fname); +-void export_add(nfs_export *); ++void export_read(char *fname); + void export_reset(nfs_export *); + nfs_export * export_lookup(char *hname, char *path, int caconical); +-nfs_export * export_find(struct hostent *, char *path); +-nfs_export * export_allowed(struct hostent *, char *path); ++nfs_export * export_find(const struct addrinfo *ai, ++ const char *path); ++nfs_export * export_allowed(const struct addrinfo *ai, ++ const char *path); + nfs_export * export_create(struct exportent *, int canonical); +-nfs_export * export_dup(nfs_export *, struct hostent *); + void export_freeall(void); + int export_export(nfs_export *); + int export_unexport(nfs_export *); +@@ -101,6 +148,19 @@ void xtab_append(nfs_export *); + + int secinfo_addflavor(struct flav_info *, struct exportent *); + ++char * host_ntop(const struct sockaddr *sap, ++ char *buf, const size_t buflen); ++__attribute_malloc__ ++struct addrinfo * host_pton(const char *paddr); ++__attribute_malloc__ ++struct addrinfo * host_addrinfo(const char *hostname); ++__attribute_malloc__ ++char * host_canonname(const struct sockaddr *sap); ++__attribute_malloc__ ++struct addrinfo * host_reliable_addrinfo(const struct sockaddr *sap); ++__attribute_malloc__ ++struct addrinfo * host_numeric_addrinfo(const struct sockaddr *sap); ++ + int rmtab_read(void); + + struct nfskey * key_lookup(char *hname); +diff --git a/support/include/misc.h b/support/include/misc.h +index 9a1b25d..bc5ba23 100644 +--- a/support/include/misc.h ++++ b/support/include/misc.h +@@ -15,13 +15,6 @@ + int randomkey(unsigned char *keyout, int len); + int weakrandomkey(unsigned char *keyout, int len); + +-int matchhostname(const char *h1, const char *h2); +- +-struct hostent; +-struct hostent *hostent_dup(struct hostent *hp); +-struct hostent *get_hostent (const char *addr, int len, int type); +-struct hostent *get_reliable_hostbyaddr(const char *addr, int len, int type); +- + extern int is_mountpoint(char *path); + + #endif /* MISC_H */ +diff --git a/support/include/nfslib.h b/support/include/nfslib.h +index 537a31e..e44cf8f 100644 +--- a/support/include/nfslib.h ++++ b/support/include/nfslib.h +@@ -152,6 +152,8 @@ void qword_addhex(char **bpp, int *lp, char *buf, int blen); + void qword_addint(char **bpp, int *lp, int n); + void qword_adduint(char **bpp, int *lp, unsigned int n); + void qword_addeol(char **bpp, int *lp); ++int qword_get_uint(char **bpp, unsigned int *anint); ++void qword_printuint(FILE *f, unsigned int num); + + void closeall(int min); + +diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h +index 4db35ab..6ebefca 100644 +--- a/support/include/nfsrpc.h ++++ b/support/include/nfsrpc.h +@@ -160,4 +160,7 @@ extern int nfs_rpc_ping(const struct sockaddr *sap, + const unsigned short protocol, + const struct timeval *timeout); + ++/* create AUTH_SYS handle with no supplemental groups */ ++extern AUTH * nfs_authsys_create(void); ++ + #endif /* !__NFS_UTILS_NFSRPC_H */ +diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c +index bdf5d84..55fa45d 100644 +--- a/support/nfs/cacheio.c ++++ b/support/nfs/cacheio.c +@@ -148,6 +148,11 @@ void qword_printint(FILE *f, int num) + fprintf(f, "%d ", num); + } + ++void qword_printuint(FILE *f, unsigned int num) ++{ ++ fprintf(f, "%u ", num); ++} ++ + int qword_eol(FILE *f) + { + int err; +@@ -236,6 +241,20 @@ int qword_get_int(char **bpp, int *anint) + return 0; + } + ++int qword_get_uint(char **bpp, unsigned int *anint) ++{ ++ char buf[50]; ++ char *ep; ++ unsigned int rv; ++ int len = qword_get(bpp, buf, 50); ++ if (len < 0) return -1; ++ if (len ==0) return -1; ++ rv = strtoul(buf, &ep, 0); ++ if (*ep) return -1; ++ *anint = rv; ++ return 0; ++} ++ + #define READLINE_BUFFER_INCREMENT 2048 + + int readline(int fd, char **buf, int *lenp) +diff --git a/support/nfs/rmtab.c b/support/nfs/rmtab.c +index a28abf3..ca789a3 100644 +--- a/support/nfs/rmtab.c ++++ b/support/nfs/rmtab.c +@@ -19,6 +19,18 @@ + #include + #include "nfslib.h" + ++/* ++ * Colons in incoming IPv6 presentation addresses have to ++ * replaced with another character, since rmtab already ++ * uses colons to delineate fields. ++ * ++ * Use a printable character, but one that would never be ++ * found in a presentation address or domain name ++ */ ++#define IPV6_COLON ';' ++ ++#define LINELEN (2048) ++ + static FILE *rmfp = NULL; + + int +@@ -56,7 +68,8 @@ struct rmtabent * + fgetrmtabent(FILE *fp, int log, long *pos) + { + static struct rmtabent re; +- char buf[2048], *count, *host, *path; ++ char *count, *host, *path, *c; ++ static char buf[LINELEN]; + + errno = 0; + if (!fp) +@@ -84,10 +97,16 @@ fgetrmtabent(FILE *fp, int log, long *pos) + else + re.r_count = 1; + } while (0); ++ + strncpy(re.r_client, host, sizeof (re.r_client) - 1); + re.r_client[sizeof (re.r_client) - 1] = '\0'; ++ for (c = re.r_client; *c != '\0'; c++) ++ if (*c == IPV6_COLON) ++ *c = ':'; ++ + strncpy(re.r_path, path, sizeof (re.r_path) - 1); + re.r_path[sizeof (re.r_path) - 1] = '\0'; ++ + return &re; + } + +@@ -100,10 +119,27 @@ putrmtabent(struct rmtabent *rep, long *pos) + void + fputrmtabent(FILE *fp, struct rmtabent *rep, long *pos) + { ++ static char buf[LINELEN]; ++ char *c; ++ + if (!fp || (pos && fseek (fp, *pos, SEEK_SET) != 0)) + return; +- fprintf(fp, "%s:%s:0x%.8x\n", rep->r_client, rep->r_path, +- rep->r_count); ++ ++ /* ++ * To avoid confusing the token parser in fgetrmtabent(), ++ * convert colons in incoming IPv6 presentation addresses ++ * to semicolons. ++ */ ++ if (strlen(rep->r_client) > sizeof(buf)) { ++ xlog(L_ERROR, "client name too large"); ++ return; ++ } ++ strncpy(buf, rep->r_client, sizeof(buf)); ++ for (c = buf; *c != '\0'; c++) ++ if (*c == ':') ++ *c = IPV6_COLON; ++ ++ (void)fprintf(fp, "%s:%s:0x%.8x\n", buf, rep->r_path, rep->r_count); + } + + void +diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c +index 0e20824..c14efe8 100644 +--- a/support/nfs/rpc_socket.c ++++ b/support/nfs/rpc_socket.c +@@ -557,3 +557,24 @@ rpcprog_t nfs_getrpcbyname(const rpcprog_t program, const char *table[]) + + return program; + } ++ ++/* ++ * AUTH_SYS doesn't allow more than 16 gids in the supplemental group list. ++ * If there are more than that, trying to determine which ones to include ++ * in the list is problematic. This function creates an auth handle that ++ * only has the primary gid in the supplemental gids list. It's intended to ++ * be used for protocols where credentials really don't matter much (the MNT ++ * protocol, for instance). ++ */ ++AUTH * ++nfs_authsys_create(void) ++{ ++ char machname[MAXHOSTNAMELEN + 1]; ++ uid_t uid = geteuid(); ++ gid_t gid = getegid(); ++ ++ if (gethostname(machname, sizeof(machname)) == -1) ++ return NULL; ++ ++ return authunix_create(machname, uid, gid, 1, &gid); ++} +diff --git a/support/nsm/file.c b/support/nsm/file.c +index d469219..f4baeb9 100644 +--- a/support/nsm/file.c ++++ b/support/nsm/file.c +@@ -67,7 +67,9 @@ + #endif + + #include ++#ifdef HAVE_SYS_CAPABILITY_H + #include ++#endif + #include + #include + +@@ -347,6 +349,7 @@ nsm_is_default_parentdir(void) + static _Bool + nsm_clear_capabilities(void) + { ++#ifdef HAVE_SYS_CAPABILITY_H + cap_t caps; + + caps = cap_from_text("cap_net_bind_service=ep"); +@@ -362,6 +365,7 @@ nsm_clear_capabilities(void) + } + + (void)cap_free(caps); ++#endif + return true; + } + +diff --git a/tests/t0001-statd-basic-mon-unmon.sh b/tests/t0001-statd-basic-mon-unmon.sh +old mode 100644 +new mode 100755 +diff --git a/tools/Makefile.am b/tools/Makefile.am +index db15346..f2ce282 100644 +--- a/tools/Makefile.am ++++ b/tools/Makefile.am +@@ -6,6 +6,6 @@ if CONFIG_RPCGEN + OPTDIRS += rpcgen + endif + +-SUBDIRS = locktest rpcdebug nlmtest $(OPTDIRS) ++SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff --git a/tools/mountstats/Makefile.am b/tools/mountstats/Makefile.am +new file mode 100644 +index 0000000..ca617a2 +--- /dev/null ++++ b/tools/mountstats/Makefile.am +@@ -0,0 +1,13 @@ ++## Process this file with automake to produce Makefile.in ++PYTHON_FILES = mountstats.py ++ ++man8_MANS = mountstats.man ++ ++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) ++ ++all-local: $(PYTHON_FILES) ++ ++install-data-hook: ++ $(INSTALL) --mode 755 mountstats.py $(DESTDIR)$(sbindir)/mountstats ++ ++MAINTAINERCLEANFILES=Makefile.in +diff --git a/tools/mountstats/mountstats.man b/tools/mountstats/mountstats.man +new file mode 100644 +index 0000000..0de31b7 +--- /dev/null ++++ b/tools/mountstats/mountstats.man +@@ -0,0 +1,32 @@ ++.\" ++.\" mountstats(8) ++.\" ++.TH mountstats 8 "15 Apr 2010" ++.SH NAME ++mountstats \- Displays NFS client per-mount statistics ++.SH SYNOPSIS ++.BI "mountstats [" "] " " [ " "]" ++.SH DESCRIPTION ++The ++.B mountstats ++command displays NFS client statisitics on each given ++.I ++.SH OPTIONS ++.TP ++.B " \-\-nfs ++display only the NFS statistics ++.TP ++.B " \-\-rpc ++display only the RPC statistics ++.TP ++.B " \-\-version ++display the version of this command ++.SH FILES ++.TP ++.B /proc/self/mountstats ++.SH SEE ALSO ++.BR iostat (8), ++.BR nfsiostat (8), ++.BR nfsstat(8) ++.SH AUTHOR ++Chuck Lever +diff --git a/tools/nfs-iostat/Makefile.am b/tools/nfs-iostat/Makefile.am +new file mode 100644 +index 0000000..30f4054 +--- /dev/null ++++ b/tools/nfs-iostat/Makefile.am +@@ -0,0 +1,13 @@ ++## Process this file with automake to produce Makefile.in ++PYTHON_FILES = nfs-iostat.py ++ ++man8_MANS = nfsiostat.man ++ ++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) ++ ++all-local: $(PYTHON_FILES) ++ ++install-data-hook: ++ $(INSTALL) --mode 755 nfs-iostat.py $(DESTDIR)$(sbindir)/nfsiostat ++ ++MAINTAINERCLEANFILES=Makefile.in +diff --git a/tools/nfs-iostat/nfsiostat.man b/tools/nfs-iostat/nfsiostat.man +new file mode 100644 +index 0000000..99e04fb +--- /dev/null ++++ b/tools/nfs-iostat/nfsiostat.man +@@ -0,0 +1,71 @@ ++.\" ++.\" nfsiostat(8) ++.\" ++.TH nfsiostat 8 "15 Apr 2010" ++.SH NAME ++nfsiostat \- Emulate iostat for NFS mount points using /proc/self/mountstats ++.SH SYNOPSIS ++.BI "nfsiostat [[" "] [" "]] [" "][" "] ++.SH DESCRIPTION ++The ++.B nfsiostat ++command displays NFS client per-mount statisitics. ++.TP ++ ++specifies the amount of time in seconds between each report. ++The first report contains statistics for the time since each file ++system was mounted. Each subsequent report contains statistics collected ++during the interval since the previous report. ++.TP ++ ++If the ++.I ++parameter is ++specified, the value of ++.I ++determines the number of reports generated at ++. ++seconds apart. if the interval parameter is ++specified without the ++.I ++parameter, the command generates reports continuously. ++.TP ++ ++Define below ++.TP ++ ++If one or more ++.I ++names are specified, statistics for only these mount points will ++be displayed. Otherwise, all NFS mount points on the client are listed. ++.SH OPTIONS ++.TP ++.B \-a " or " \-\-attr ++displays statistics related to the attribute cache ++.TP ++.B \-d " or " \-\-dir ++displays statistics related to directory operations ++.TP ++.B \-h " or " \-\-help ++shows help message and exit ++.TP ++.B \-l LIST or " \-\-list=LIST ++only print stats for first LIST mount points ++.TP ++.B \-p " or " \-\-page ++displays statistics related to the page cache ++.TP ++.B \-s " or " \-\-sort ++Sort NFS mount points by ops/second ++.TP ++.B \-\-version ++show program's version number and exit ++.SH FILES ++.TP ++.B /proc/self/mountstats ++.SH SEE ALSO ++.BR iostat (8), ++.BR mountstats (8), ++.BR nfsstat(8) ++.SH AUTHOR ++Chuck Lever +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index 331e57e..edc1625 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -36,6 +37,7 @@ static void dump(int verbose); + static void error(nfs_export *exp, int err); + static void usage(void); + static void validate_export(nfs_export *exp); ++static int matchhostname(const char *hostname1, const char *hostname2); + + int + main(int argc, char **argv) +@@ -232,7 +234,7 @@ exportfs(char *arg, char *options, int verbose) + { + struct exportent *eep; + nfs_export *exp; +- struct hostent *hp = NULL; ++ struct addrinfo *ai = NULL; + char *path; + char *hname = arg; + int htype; +@@ -245,32 +247,21 @@ exportfs(char *arg, char *options, int verbose) + return; + } + +- if ((htype = client_gettype(hname)) == MCL_FQDN && +- (hp = gethostbyname(hname)) != NULL) { +- struct hostent *hp2 = hostent_dup (hp); +- hp = gethostbyaddr(hp2->h_addr, hp2->h_length, +- hp2->h_addrtype); +- if (hp) { +- free(hp2); +- hp = hostent_dup(hp); +- } else +- hp = hp2; +- exp = export_find(hp, path); +- hname = hp->h_name; +- } else { ++ if ((htype = client_gettype(hname)) == MCL_FQDN) { ++ ai = host_addrinfo(hname); ++ if (ai != NULL) { ++ exp = export_find(ai, path); ++ hname = ai->ai_canonname; ++ } ++ } else + exp = export_lookup(hname, path, 0); +- } + + if (!exp) { + if (!(eep = mkexportent(hname, path, options)) || +- !(exp = export_create(eep, 0))) { +- if (hp) free (hp); +- return; +- } +- } else if (!updateexportent(&exp->m_export, options)) { +- if (hp) free (hp); +- return; +- } ++ !(exp = export_create(eep, 0))) ++ goto out; ++ } else if (!updateexportent(&exp->m_export, options)) ++ goto out; + + if (verbose) + printf("exporting %s:%s\n", exp->m_client->m_hostname, +@@ -280,14 +271,16 @@ exportfs(char *arg, char *options, int verbose) + exp->m_changed = 1; + exp->m_warned = 0; + validate_export(exp); +- if (hp) free (hp); ++ ++out: ++ freeaddrinfo(ai); + } + + static void + unexportfs(char *arg, int verbose) + { + nfs_export *exp; +- struct hostent *hp = NULL; ++ struct addrinfo *ai = NULL; + char *path; + char *hname = arg; + int htype; +@@ -302,10 +295,9 @@ unexportfs(char *arg, int verbose) + } + + if ((htype = client_gettype(hname)) == MCL_FQDN) { +- if ((hp = gethostbyname(hname)) != 0) { +- hp = hostent_dup (hp); +- hname = (char *) hp->h_name; +- } ++ ai = host_addrinfo(hname); ++ if (ai) ++ hname = ai->ai_canonname; + } + + for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) { +@@ -341,7 +333,7 @@ unexportfs(char *arg, int verbose) + exp->m_mayexport = 0; + } + +- if (hp) free (hp); ++ freeaddrinfo(ai); + } + + static int can_test(void) +@@ -431,6 +423,82 @@ validate_export(nfs_export *exp) + } + } + ++static _Bool ++is_hostname(const char *sp) ++{ ++ if (*sp == '\0' || *sp == '@') ++ return false; ++ ++ for (; *sp != '\0'; sp++) { ++ if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/') ++ return false; ++ if (*sp == '\\' && sp[1] != '\0') ++ sp++; ++ } ++ ++ return true; ++} ++ ++static _Bool ++compare_sockaddrs4(const struct sockaddr *sa1, const struct sockaddr *sa2) ++{ ++ const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; ++ const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; ++ return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; ++} ++ ++static _Bool ++compare_sockaddrs(const struct sockaddr *sa1, const struct sockaddr *sa2) ++{ ++ if (sa1->sa_family == sa2->sa_family) ++ switch (sa1->sa_family) { ++ case AF_INET: ++ return compare_sockaddrs4(sa1, sa2); ++ } ++ ++ return false; ++} ++ ++static int ++matchhostname(const char *hostname1, const char *hostname2) ++{ ++ struct addrinfo *results1 = NULL, *results2 = NULL; ++ struct addrinfo *ai1, *ai2; ++ int result = 0; ++ ++ if (strcasecmp(hostname1, hostname2) == 0) ++ return 1; ++ ++ /* ++ * Don't pass export wildcards or netgroup names to DNS ++ */ ++ if (!is_hostname(hostname1) || !is_hostname(hostname2)) ++ return 0; ++ ++ results1 = host_addrinfo(hostname1); ++ if (results1 == NULL) ++ goto out; ++ results2 = host_addrinfo(hostname2); ++ if (results2 == NULL) ++ goto out; ++ ++ if (strcasecmp(results1->ai_canonname, results2->ai_canonname) == 0) { ++ result = 1; ++ goto out; ++ } ++ ++ for (ai1 = results1; ai1 != NULL; ai1 = ai1->ai_next) ++ for (ai2 = results2; ai2 != NULL; ai2 = ai2->ai_next) ++ if (compare_sockaddrs(ai1->ai_addr, ai2->ai_addr)) { ++ result = 1; ++ break; ++ } ++ ++out: ++ freeaddrinfo(results1); ++ freeaddrinfo(results2); ++ return result; ++} + + static char + dumpopt(char c, char *fmt, ...) +diff --git a/utils/gssd/context.h b/utils/gssd/context.h +index be47f9c..c9cb0bd 100644 +--- a/utils/gssd/context.h ++++ b/utils/gssd/context.h +@@ -1,5 +1,5 @@ + /* +- Copyright (c) 2004 The Regents of the University of Michigan. ++ Copyright (c) 2004,2008 The Regents of the University of Michigan. + All rights reserved. + + Redistribution and use in source and binary forms, with or without +@@ -36,6 +36,10 @@ + /* Hopefully big enough to hold any serialized context */ + #define MAX_CTX_LEN 4096 + ++/* New context format flag values */ ++#define KRB5_CTX_FLAG_INITIATOR 0x00000001 ++#define KRB5_CTX_FLAG_CFX 0x00000002 ++#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + + int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf, + gss_OID mech, int32_t *endtime); +diff --git a/utils/gssd/context_lucid.c b/utils/gssd/context_lucid.c +index 4a682ae..b87bf76 100644 +--- a/utils/gssd/context_lucid.c ++++ b/utils/gssd/context_lucid.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include + +@@ -119,15 +120,13 @@ prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx, + * Note that the rfc1964 version only supports DES enctypes. + */ + if (lctx->rfc1964_kd.ctx_key.type != 4) { +- printerr(1, "prepare_krb5_rfc1964_buffer: " +- "overriding heimdal keytype (%d => %d)\n", +- lctx->rfc1964_kd.ctx_key.type, 4); ++ printerr(2, "%s: overriding heimdal keytype (%d => %d)\n", ++ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, 4); + lctx->rfc1964_kd.ctx_key.type = 4; + } + #endif +- printerr(2, "prepare_krb5_rfc1964_buffer: serializing keys with " +- "enctype %d and length %d\n", +- lctx->rfc1964_kd.ctx_key.type, ++ printerr(2, "%s: serializing keys with enctype %d and length %d\n", ++ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, + lctx->rfc1964_kd.ctx_key.length); + + /* derive the encryption key and copy it into buffer */ +@@ -158,11 +157,100 @@ out_err: + return -1; + } + ++/* Flags for version 2 context flags */ ++#define KRB5_CTX_FLAG_INITIATOR 0x00000001 ++#define KRB5_CTX_FLAG_CFX 0x00000002 ++#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 ++ ++/* ++ * Prepare a new-style buffer, as defined in rfc4121 (a.k.a. cfx), ++ * to send to the kernel for newer encryption types -- or for DES3. ++ * ++ * The new format is: ++ * ++ * u32 flags; ++ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 ++ * #define KRB5_CTX_FLAG_CFX 0x00000002 ++ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 ++ * s32 endtime; ++ * u64 seq_send; ++ * u32 enctype; ( encrption type of key ) ++ * raw key; ( raw key bytes (kernel will derive)) ++ * ++ */ + static int +-prepare_krb5_rfc_cfx_buffer(gss_krb5_lucid_context_v1_t *lctx, ++prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx, + gss_buffer_desc *buf, int32_t *endtime) + { +- printerr(0, "ERROR: prepare_krb5_rfc_cfx_buffer: not implemented\n"); ++ char *p, *end; ++ uint32_t v2_flags = 0; ++ uint32_t enctype; ++ uint32_t keysize; ++ ++ if (!(buf->value = calloc(1, MAX_CTX_LEN))) ++ goto out_err; ++ p = buf->value; ++ end = buf->value + MAX_CTX_LEN; ++ ++ /* Version 2 */ ++ if (lctx->initiate) ++ v2_flags |= KRB5_CTX_FLAG_INITIATOR; ++ if (lctx->protocol != 0) ++ v2_flags |= KRB5_CTX_FLAG_CFX; ++ if (lctx->protocol != 0 && lctx->cfx_kd.have_acceptor_subkey == 1) ++ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; ++ ++ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; ++ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err; ++ if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err; ++ ++ /* Protocol 0 here implies DES3 or RC4 */ ++ printerr(2, "%s: protocol %d\n", __FUNCTION__, lctx->protocol); ++ if (lctx->protocol == 0) { ++ enctype = lctx->rfc1964_kd.ctx_key.type; ++ keysize = lctx->rfc1964_kd.ctx_key.length; ++ } else { ++ if (lctx->cfx_kd.have_acceptor_subkey) { ++ enctype = lctx->cfx_kd.acceptor_subkey.type; ++ keysize = lctx->cfx_kd.acceptor_subkey.length; ++ } else { ++ enctype = lctx->cfx_kd.ctx_key.type; ++ keysize = lctx->cfx_kd.ctx_key.length; ++ } ++ } ++ printerr(2, "%s: serializing key with enctype %d and size %d\n", ++ __FUNCTION__, enctype, keysize); ++ ++ if (WRITE_BYTES(&p, end, enctype)) goto out_err; ++ ++ if (lctx->protocol == 0) { ++ if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data, ++ lctx->rfc1964_kd.ctx_key.length)) ++ goto out_err; ++ } else { ++ if (lctx->cfx_kd.have_acceptor_subkey) { ++ if (write_bytes(&p, end, ++ lctx->cfx_kd.acceptor_subkey.data, ++ lctx->cfx_kd.acceptor_subkey.length)) ++ goto out_err; ++ } else { ++ if (write_bytes(&p, end, lctx->cfx_kd.ctx_key.data, ++ lctx->cfx_kd.ctx_key.length)) ++ goto out_err; ++ } ++ } ++ ++ buf->length = p - (char *)buf->value; ++ return 0; ++ ++out_err: ++ printerr(0, "ERROR: %s: failed serializing krb5 context for kernel\n", ++ __FUNCTION__); ++ if (buf->value) { ++ free(buf->value); ++ buf->value = NULL; ++ } ++ buf->length = 0; + return -1; + } + +@@ -176,7 +264,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime) + gss_krb5_lucid_context_v1_t *lctx = 0; + int retcode = 0; + +- printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n"); ++ printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__); + maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx, + 1, &return_ctx); + if (maj_stat != GSS_S_COMPLETE) { +@@ -198,11 +286,20 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime) + break; + } + +- /* Now lctx points to a lucid context that we can send down to kernel */ +- if (lctx->protocol == 0) ++ /* ++ * Now lctx points to a lucid context that we can send down to kernel ++ * ++ * Note: we send down different information to the kernel depending ++ * on the protocol version and the enctyption type. ++ * For protocol version 0 with all enctypes besides DES3, we use ++ * the original format. For protocol version != 0 or DES3, we ++ * send down the new style information. ++ */ ++ ++ if (lctx->protocol == 0 && lctx->rfc1964_kd.ctx_key.type <= 4) + retcode = prepare_krb5_rfc1964_buffer(lctx, buf, endtime); + else +- retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf, endtime); ++ retcode = prepare_krb5_rfc4121_buffer(lctx, buf, endtime); + + maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx); + if (maj_stat != GSS_S_COMPLETE) { +@@ -212,8 +309,8 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime) + } + + if (retcode) { +- printerr(1, "serialize_krb5_ctx: prepare_krb5_*_buffer " +- "failed (retcode = %d)\n", retcode); ++ printerr(1, "%s: prepare_krb5_*_buffer failed (retcode = %d)\n", ++ __FUNCTION__, retcode); + goto out_err; + } + +@@ -223,4 +320,7 @@ out_err: + printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); + return -1; + } ++ ++ ++ + #endif /* HAVE_LUCID_CONTEXT_SUPPORT */ +diff --git a/utils/gssd/context_mit.c b/utils/gssd/context_mit.c +index 709a903..f9cbb02 100644 +--- a/utils/gssd/context_mit.c ++++ b/utils/gssd/context_mit.c +@@ -1,5 +1,5 @@ + /* +- Copyright (c) 2004 The Regents of the University of Michigan. ++ Copyright (c) 2004-2006 The Regents of the University of Michigan. + All rights reserved. + + Redistribution and use in source and binary forms, with or without +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -52,8 +53,7 @@ + /* XXX argggg, there's gotta be a better way than just duplicating this + * whole struct. Unfortunately, this is in a "private" header file, + * so this is our best choice at this point :-/ +- * +- * XXX Does this match the Heimdal definition? */ ++ */ + + typedef struct _krb5_gss_ctx_id_rec { + unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */ +@@ -156,50 +156,120 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime) + { + krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)ctx)->internal_ctx_id; + char *p, *end; +- static int constant_one = 1; + static int constant_zero = 0; ++ static int constant_one = 1; ++ static int constant_two = 2; + uint32_t word_seq_send; ++ u_int64_t seq_send_64bit; ++ uint32_t v2_flags = 0; + + if (!(buf->value = calloc(1, MAX_CTX_LEN))) + goto out_err; + p = buf->value; + end = buf->value + MAX_CTX_LEN; + +- if (kctx->initiate) { +- if (WRITE_BYTES(&p, end, constant_one)) goto out_err; +- } +- else { +- if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; +- } +- if (kctx->seed_init) { +- if (WRITE_BYTES(&p, end, constant_one)) goto out_err; +- } +- else { +- if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; +- } +- if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) ++ switch (kctx->enc->enctype) { ++ case ENCTYPE_DES_CBC_CRC: ++ case ENCTYPE_DES_CBC_MD4: ++ case ENCTYPE_DES_CBC_MD5: ++ case ENCTYPE_DES_CBC_RAW: ++ /* Old format of context to the kernel */ ++ if (kctx->initiate) { ++ if (WRITE_BYTES(&p, end, constant_one)) goto out_err; ++ } ++ else { ++ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; ++ } ++ if (kctx->seed_init) { ++ if (WRITE_BYTES(&p, end, constant_one)) goto out_err; ++ } ++ else { ++ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; ++ } ++ if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) ++ goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; ++ word_seq_send = kctx->seq_send; ++ if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; ++ if (write_oid(&p, end, kctx->mech_used)) goto out_err; ++ ++ printerr(2, "serialize_krb5_ctx: serializing keys with " ++ "enctype %d and length %d\n", ++ kctx->enc->enctype, kctx->enc->length); ++ ++ if (write_keyblock(&p, end, kctx->enc)) goto out_err; ++ if (write_keyblock(&p, end, kctx->seq)) goto out_err; ++ break; ++ case ENCTYPE_DES3_CBC_RAW: ++ case ENCTYPE_DES3_CBC_SHA1: ++ case ENCTYPE_ARCFOUR_HMAC: ++ case ENCTYPE_ARCFOUR_HMAC_EXP: ++ case ENCTYPE_AES128_CTS_HMAC_SHA1_96: ++ case ENCTYPE_AES256_CTS_HMAC_SHA1_96: ++ /* New format of context to the kernel */ ++ /* u32 flags; ++ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 ++ * #define KRB5_CTX_FLAG_CFX 0x00000002 ++ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 ++ * s32 endtime; ++ * u64 seq_send; ++ * u32 enctype; ++ * rawkey data ++ */ ++ ++ if (kctx->initiate) ++ v2_flags |= KRB5_CTX_FLAG_INITIATOR; ++ if (kctx->proto == 1) ++ v2_flags |= KRB5_CTX_FLAG_CFX; ++ if (kctx->have_acceptor_subkey) ++ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; ++ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; ++ ++ seq_send_64bit = kctx->seq_send; ++ if (WRITE_BYTES(&p, end, seq_send_64bit)) goto out_err; ++ ++ if (kctx->have_acceptor_subkey) { ++ if (WRITE_BYTES(&p, end, kctx->acceptor_subkey->enctype)) ++ goto out_err; ++ printerr(2, "serialize_krb5_ctx: serializing subkey " ++ "with enctype %d and size %d\n", ++ kctx->acceptor_subkey->enctype, ++ kctx->acceptor_subkey->length); ++ ++ if (write_bytes(&p, end, ++ kctx->acceptor_subkey->contents, ++ kctx->acceptor_subkey->length)) ++ goto out_err; ++ } else { ++ if (WRITE_BYTES(&p, end, kctx->enc->enctype)) ++ goto out_err; ++ printerr(2, "serialize_krb5_ctx: serializing key " ++ "with enctype %d and size %d\n", ++ kctx->enc->enctype, kctx->enc->length); ++ ++ if (write_bytes(&p, end, kctx->enc->contents, ++ kctx->enc->length)) ++ goto out_err; ++ } ++ break; ++ default: ++ printerr(0, "ERROR: serialize_krb5_ctx: unsupported encryption " ++ "algorithm %d\n", kctx->enc->enctype); + goto out_err; +- if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; +- if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; +- if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; +- if (endtime) +- *endtime = kctx->endtime; +- word_seq_send = kctx->seq_send; +- if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; +- if (write_oid(&p, end, kctx->mech_used)) goto out_err; +- +- printerr(2, "serialize_krb5_ctx: serializing keys with " +- "enctype %d and length %d\n", +- kctx->enc->enctype, kctx->enc->length); +- +- if (write_keyblock(&p, end, kctx->enc)) goto out_err; +- if (write_keyblock(&p, end, kctx->seq)) goto out_err; ++ } + + buf->length = p - (char *)buf->value; + return 0; ++ + out_err: + printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); +- if (buf->value) free(buf->value); ++ if (buf->value) { ++ free(buf->value); ++ } ++ buf->value = NULL; + buf->length = 0; + return -1; + } +diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c +index be4fb11..a55418b 100644 +--- a/utils/gssd/gssd_proc.c ++++ b/utils/gssd/gssd_proc.c +@@ -600,6 +600,67 @@ update_client_list(void) + return retval; + } + ++/* Encryption types supported by the kernel rpcsec_gss code */ ++int num_krb5_enctypes = 0; ++krb5_enctype *krb5_enctypes = NULL; ++ ++/* ++ * Parse the supported encryption type information ++ */ ++static int ++parse_enctypes(char *enctypes) ++{ ++ int n = 0; ++ char *curr, *comma; ++ int i; ++ static char *cached_types; ++ ++ if (cached_types && strcmp(cached_types, enctypes) == 0) ++ return 0; ++ free(cached_types); ++ ++ if (krb5_enctypes != NULL) { ++ free(krb5_enctypes); ++ krb5_enctypes = NULL; ++ num_krb5_enctypes = 0; ++ } ++ ++ /* count the number of commas */ ++ for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) { ++ comma = strchr(curr, ','); ++ if (comma != NULL) ++ n++; ++ else ++ break; ++ } ++ /* If no more commas and we're not at the end, there's one more value */ ++ if (*curr != '\0') ++ n++; ++ ++ /* Empty string, return an error */ ++ if (n == 0) ++ return ENOENT; ++ ++ /* Allocate space for enctypes array */ ++ if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) { ++ return ENOMEM; ++ } ++ ++ /* Now parse each value into the array */ ++ for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) { ++ krb5_enctypes[i++] = atoi(curr); ++ comma = strchr(curr, ','); ++ if (comma == NULL) ++ break; ++ } ++ ++ num_krb5_enctypes = n; ++ if ((cached_types = malloc(strlen(enctypes)+1))) ++ strcpy(cached_types, enctypes); ++ ++ return 0; ++} ++ + static int + do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, + gss_buffer_desc *context_token) +@@ -1133,6 +1194,7 @@ handle_gssd_upcall(struct clnt_info *clp) + char *mech = NULL; + char *target = NULL; + char *service = NULL; ++ char *enctypes = NULL; + + printerr(1, "handling gssd upcall (%s)\n", clp->dirname); + +@@ -1176,6 +1238,23 @@ handle_gssd_upcall(struct clnt_info *clp) + goto out; + } + ++ /* read supported encryption types if supplied */ ++ if ((p = strstr(lbuf, "enctypes=")) != NULL) { ++ enctypes = malloc(lbuflen); ++ if (!enctypes) ++ goto out; ++ if (sscanf(p, "enctypes=%s", enctypes) != 1) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed to parse target name " ++ "in upcall string '%s'\n", lbuf); ++ goto out; ++ } ++ if (parse_enctypes(enctypes) != 0) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "parsing encryption types failed: errno %d\n", errno); ++ } ++ } ++ + /* read target name */ + if ((p = strstr(lbuf, "target=")) != NULL) { + target = malloc(lbuflen); +@@ -1222,6 +1301,7 @@ handle_gssd_upcall(struct clnt_info *clp) + out: + free(lbuf); + free(mech); ++ free(enctypes); + free(target); + free(service); + return; +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index 1295f57..dccbeb6 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -292,61 +292,6 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) + return err; + } + +- +-#ifdef HAVE_SET_ALLOWABLE_ENCTYPES +-/* +- * this routine obtains a credentials handle via gss_acquire_cred() +- * then calls gss_krb5_set_allowable_enctypes() to limit the encryption +- * types negotiated. +- * +- * XXX Should call some function to determine the enctypes supported +- * by the kernel. (Only need to do that once!) +- * +- * Returns: +- * 0 => all went well +- * -1 => there was an error +- */ +- +-int +-limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) +-{ +- u_int maj_stat, min_stat; +- gss_cred_id_t credh; +- gss_OID_set_desc desired_mechs; +- krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, +- ENCTYPE_DES_CBC_MD5, +- ENCTYPE_DES_CBC_MD4 }; +- int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); +- +- /* We only care about getting a krb5 cred */ +- desired_mechs.count = 1; +- desired_mechs.elements = &krb5oid; +- +- maj_stat = gss_acquire_cred(&min_stat, NULL, 0, +- &desired_mechs, GSS_C_INITIATE, +- &credh, NULL, NULL); +- +- if (maj_stat != GSS_S_COMPLETE) { +- if (get_verbosity() > 0) +- pgsserr("gss_acquire_cred", +- maj_stat, min_stat, &krb5oid); +- return -1; +- } +- +- maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid, +- num_enctypes, &enctypes); +- if (maj_stat != GSS_S_COMPLETE) { +- pgsserr("gss_set_allowable_enctypes", +- maj_stat, min_stat, &krb5oid); +- gss_release_cred(&min_stat, &credh); +- return -1; +- } +- sec->cred = credh; +- +- return 0; +-} +-#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ +- + /* + * Obtain credentials via a key in the keytab given + * a keytab handle and a gssd_k5_kt_princ structure. +@@ -1304,3 +1249,68 @@ gssd_k5_get_default_realm(char **def_realm) + + krb5_free_context(context); + } ++ ++#ifdef HAVE_SET_ALLOWABLE_ENCTYPES ++/* ++ * this routine obtains a credentials handle via gss_acquire_cred() ++ * then calls gss_krb5_set_allowable_enctypes() to limit the encryption ++ * types negotiated. ++ * ++ * XXX Should call some function to determine the enctypes supported ++ * by the kernel. (Only need to do that once!) ++ * ++ * Returns: ++ * 0 => all went well ++ * -1 => there was an error ++ */ ++ ++int ++limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) ++{ ++ u_int maj_stat, min_stat; ++ gss_cred_id_t credh; ++ gss_OID_set_desc desired_mechs; ++ krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, ++ ENCTYPE_DES_CBC_MD5, ++ ENCTYPE_DES_CBC_MD4 }; ++ int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); ++ extern int num_krb5_enctypes; ++ extern krb5_enctype *krb5_enctypes; ++ ++ /* We only care about getting a krb5 cred */ ++ desired_mechs.count = 1; ++ desired_mechs.elements = &krb5oid; ++ ++ maj_stat = gss_acquire_cred(&min_stat, NULL, 0, ++ &desired_mechs, GSS_C_INITIATE, ++ &credh, NULL, NULL); ++ ++ if (maj_stat != GSS_S_COMPLETE) { ++ if (get_verbosity() > 0) ++ pgsserr("gss_acquire_cred", ++ maj_stat, min_stat, &krb5oid); ++ return -1; ++ } ++ ++ /* ++ * If we failed for any reason to produce global ++ * list of supported enctypes, use local default here. ++ */ ++ if (krb5_enctypes == NULL) ++ maj_stat = gss_set_allowable_enctypes(&min_stat, credh, ++ &krb5oid, num_enctypes, enctypes); ++ else ++ maj_stat = gss_set_allowable_enctypes(&min_stat, credh, ++ &krb5oid, num_krb5_enctypes, krb5_enctypes); ++ ++ if (maj_stat != GSS_S_COMPLETE) { ++ pgsserr("gss_set_allowable_enctypes", ++ maj_stat, min_stat, &krb5oid); ++ gss_release_cred(&min_stat, &credh); ++ return -1; ++ } ++ sec->cred = credh; ++ ++ return 0; ++} ++#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ +diff --git a/utils/mount/network.c b/utils/mount/network.c +index 8dc183a..ffb18ab 100644 +--- a/utils/mount/network.c ++++ b/utils/mount/network.c +@@ -857,7 +857,14 @@ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen, + return 0; + } + +- client->cl_auth = authunix_create_default(); ++ client->cl_auth = nfs_authsys_create(); ++ if (client->cl_auth == NULL) { ++ if (verbose) ++ nfs_error(_("%s: Failed to create RPC auth handle"), ++ progname); ++ CLNT_DESTROY(client); ++ return 0; ++ } + + res = CLNT_CALL(client, MOUNTPROC_UMNT, + (xdrproc_t)xdr_dirpath, (caddr_t)argp, +@@ -957,8 +964,10 @@ CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock) + } + if (clnt) { + /* try to mount hostname:dirname */ +- clnt->cl_auth = authunix_create_default(); +- return clnt; ++ clnt->cl_auth = nfs_authsys_create(); ++ if (clnt->cl_auth) ++ return clnt; ++ CLNT_DESTROY(clnt); + } + return NULL; + } +@@ -1203,6 +1212,8 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program) + return 1; + } + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'nfsprog=' option"), ++ progname); + return 0; + } + +@@ -1242,9 +1253,12 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) + } + return 0; + case PO_NOT_FOUND: +- nfs_error(_("%s: option parsing error\n"), ++ nfs_error(_("%s: parsing error on 'vers=' option\n"), + progname); ++ return 0; + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'vers=' option"), ++ progname); + return 0; + } + case 4: /* nfsvers */ +@@ -1256,9 +1270,12 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) + } + return 0; + case PO_NOT_FOUND: +- nfs_error(_("%s: option parsing error\n"), ++ nfs_error(_("%s: parsing error on 'nfsvers=' option\n"), + progname); ++ return 0; + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'nfsvers=' option"), ++ progname); + return 0; + } + } +@@ -1294,6 +1311,8 @@ nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol) + if (option != NULL) { + if (!nfs_get_proto(option, &family, protocol)) { + errno = EPROTONOSUPPORT; ++ nfs_error(_("%s: Failed to find '%s' protocol"), ++ progname, option); + return 0; + } + return 1; +@@ -1327,6 +1346,8 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port) + return 1; + } + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'port=' option"), ++ progname); + return 0; + } + +@@ -1380,8 +1401,13 @@ int nfs_nfs_proto_family(struct mount_options *options, + case 2: /* proto */ + option = po_get(options, "proto"); + if (option != NULL && +- !nfs_get_proto(option, &tmp_family, &protocol)) +- goto out_err; ++ !nfs_get_proto(option, &tmp_family, &protocol)) { ++ ++ nfs_error(_("%s: Failed to find '%s' protocol"), ++ progname, option); ++ errno = EPROTONOSUPPORT; ++ return 0; ++ } + } + + if (!nfs_verify_family(tmp_family)) +@@ -1414,6 +1440,8 @@ nfs_mount_program(struct mount_options *options, unsigned long *program) + return 1; + } + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'mountprog=' option"), ++ progname); + return 0; + } + +@@ -1443,6 +1471,8 @@ nfs_mount_version(struct mount_options *options, unsigned long *version) + return 1; + } + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'mountvers=' option"), ++ progname); + return 0; + } + +@@ -1469,6 +1499,8 @@ nfs_mount_protocol(struct mount_options *options, unsigned long *protocol) + if (option != NULL) { + if (!nfs_get_proto(option, &family, protocol)) { + errno = EPROTONOSUPPORT; ++ nfs_error(_("%s: Failed to find '%s' protocol"), ++ progname, option); + return 0; + } + return 1; +@@ -1501,6 +1533,8 @@ nfs_mount_port(struct mount_options *options, unsigned long *port) + return 1; + } + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'mountport=' option"), ++ progname); + return 0; + } + +@@ -1526,8 +1560,12 @@ int nfs_mount_proto_family(struct mount_options *options, + + option = po_get(options, "mountproto"); + if (option != NULL) { +- if (!nfs_get_proto(option, &tmp_family, &protocol)) ++ if (!nfs_get_proto(option, &tmp_family, &protocol)) { ++ nfs_error(_("%s: Failed to find '%s' protocol"), ++ progname, option); ++ errno = EPROTONOSUPPORT; + goto out_err; ++ } + if (!nfs_verify_family(tmp_family)) + goto out_err; + *family = tmp_family; +diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c +index 9d798a2..1514340 100644 +--- a/utils/mount/nfsumount.c ++++ b/utils/mount/nfsumount.c +@@ -179,10 +179,8 @@ static int nfs_umount_do_umnt(struct mount_options *options, + struct pmap nfs_pmap, mnt_pmap; + sa_family_t family; + +- if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) { +- nfs_error(_("%s: bad mount options"), progname); ++ if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) + return EX_FAIL; +- } + + /* Skip UMNT call for vers=4 mounts */ + if (nfs_pmap.pm_vers == 4) +diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c +index 9b8c38f..0241400 100644 +--- a/utils/mount/stropts.c ++++ b/utils/mount/stropts.c +@@ -538,7 +538,10 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options) + + if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap, + mnt_saddr, &mnt_pmap)) { +- errno = EINVAL; ++ if (rpc_createerr.cf_stat == RPC_UNKNOWNPROTO) ++ errno = EPROTONOSUPPORT; ++ else ++ errno = EINVAL; + return 0; + } + +@@ -586,18 +589,21 @@ static int nfs_do_mount_v3v2(struct nfsmount_info *mi, + errno = ENOMEM; + return result; + } +- ++ errno = 0; + if (!nfs_append_addr_option(sap, salen, options)) { +- errno = EINVAL; ++ if (errno == 0) ++ errno = EINVAL; + goto out_fail; + } + + if (!nfs_fix_mounthost_option(options, mi->hostname)) { +- errno = EINVAL; ++ if (errno == 0) ++ errno = EINVAL; + goto out_fail; + } + if (!mi->fake && !nfs_verify_lock_option(options)) { +- errno = EINVAL; ++ if (errno == 0) ++ errno = EINVAL; + goto out_fail; + } + +@@ -799,6 +805,7 @@ static int nfs_is_permanent_error(int error) + case ESTALE: + case ETIMEDOUT: + case ECONNREFUSED: ++ case EHOSTUNREACH: + return 0; /* temporary */ + default: + return 1; /* permanent */ +diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c +index 13eba70..04487e5 100644 +--- a/utils/mountd/auth.c ++++ b/utils/mountd/auth.c +@@ -110,13 +110,15 @@ auth_reload() + return counter; + } + +-static char *get_client_hostname(struct sockaddr_in *caller, struct hostent *hp, enum auth_error *error) ++static char * ++get_client_hostname(struct sockaddr_in *caller, struct addrinfo *ai, ++ enum auth_error *error) + { + char *n; + + if (use_ipaddr) + return strdup(inet_ntoa(caller->sin_addr)); +- n = client_compose(hp); ++ n = client_compose(ai); + *error = unknown_host; + if (!n) + return NULL; +@@ -128,8 +130,8 @@ static char *get_client_hostname(struct sockaddr_in *caller, struct hostent *hp, + + /* return static nfs_export with details filled in */ + static nfs_export * +-auth_authenticate_newcache(char *what, struct sockaddr_in *caller, +- char *path, struct hostent *hp, ++auth_authenticate_newcache(struct sockaddr_in *caller, ++ char *path, struct addrinfo *ai, + enum auth_error *error) + { + nfs_export *exp; +@@ -137,12 +139,12 @@ auth_authenticate_newcache(char *what, struct sockaddr_in *caller, + + free(my_client.m_hostname); + +- my_client.m_hostname = get_client_hostname(caller, hp, error); ++ my_client.m_hostname = get_client_hostname(caller, ai, error); + if (my_client.m_hostname == NULL) + return NULL; + + my_client.m_naddr = 1; +- my_client.m_addrlist[0] = caller->sin_addr; ++ set_addrlist_in(&my_client, 0, caller); + my_exp.m_client = &my_client; + + exp = NULL; +@@ -152,7 +154,7 @@ auth_authenticate_newcache(char *what, struct sockaddr_in *caller, + continue; + if (!use_ipaddr && !client_member(my_client.m_hostname, exp->m_client->m_hostname)) + continue; +- if (use_ipaddr && !client_check(exp->m_client, hp)) ++ if (use_ipaddr && !client_check(exp->m_client, ai)) + continue; + break; + } +@@ -166,18 +168,19 @@ auth_authenticate_newcache(char *what, struct sockaddr_in *caller, + } + + static nfs_export * +-auth_authenticate_internal(char *what, struct sockaddr_in *caller, +- char *path, struct hostent *hp, ++auth_authenticate_internal(struct sockaddr_in *caller, ++ char *path, struct addrinfo *ai, + enum auth_error *error) + { + nfs_export *exp; + + if (new_cache) { +- exp = auth_authenticate_newcache(what, caller, path, hp, error); ++ exp = auth_authenticate_newcache(caller, path, ai, error); + if (!exp) + return NULL; + } else { +- if (!(exp = export_find(hp, path))) { ++ exp = export_find(ai, path); ++ if (exp == NULL) { + *error = no_entry; + return NULL; + } +@@ -202,7 +205,7 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path) + nfs_export *exp = NULL; + char epath[MAXPATHLEN+1]; + char *p = NULL; +- struct hostent *hp = NULL; ++ struct addrinfo *ai = NULL; + struct in_addr addr = caller->sin_addr; + enum auth_error error = bad_path; + +@@ -216,14 +219,14 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path) + epath[sizeof (epath) - 1] = '\0'; + auth_fixpath(epath); /* strip duplicate '/' etc */ + +- hp = client_resolve(caller->sin_addr); +- if (!hp) ++ ai = client_resolve((struct sockaddr *)caller); ++ if (ai == NULL) + return exp; + + /* Try the longest matching exported pathname. */ + while (1) { +- exp = auth_authenticate_internal(what, caller, epath, +- hp, &error); ++ exp = auth_authenticate_internal(caller, epath, ++ ai, &error); + if (exp || (error != not_exported && error != no_entry)) + break; + /* We have to treat the root, "/", specially. */ +@@ -246,31 +249,30 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path) + + case no_entry: + xlog(L_WARNING, "refused %s request from %s for %s (%s): no export entry", +- what, hp->h_name, path, epath); ++ what, ai->ai_canonname, path, epath); + break; + + case not_exported: + xlog(L_WARNING, "refused %s request from %s for %s (%s): not exported", +- what, hp->h_name, path, epath); ++ what, ai->ai_canonname, path, epath); + break; + + case illegal_port: + xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %d", +- what, hp->h_name, path, epath, ntohs(caller->sin_port)); ++ what, ai->ai_canonname, path, epath, ntohs(caller->sin_port)); + break; + + case success: + xlog(L_NOTICE, "authenticated %s request from %s:%d for %s (%s)", +- what, hp->h_name, ntohs(caller->sin_port), path, epath); ++ what, ai->ai_canonname, ntohs(caller->sin_port), path, epath); + break; + default: + xlog(L_NOTICE, "%s request from %s:%d for %s (%s) gave %d", +- what, hp->h_name, ntohs(caller->sin_port), path, epath, error); ++ what, ai->ai_canonname, ntohs(caller->sin_port), ++ path, epath, error); + } + +- if (hp) +- free (hp); +- ++ freeaddrinfo(ai); + return exp; + } + +diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c +index d63e10a..9e1b164 100644 +--- a/utils/mountd/cache.c ++++ b/utils/mountd/cache.c +@@ -77,8 +77,8 @@ void auth_unix_ip(FILE *f) + char class[20]; + char ipaddr[20]; + char *client = NULL; +- struct in_addr addr; +- struct hostent *he = NULL; ++ struct addrinfo *tmp = NULL; ++ struct addrinfo *ai = NULL; + if (readline(fileno(f), &lbuf, &lbuflen) != 1) + return; + +@@ -93,17 +93,20 @@ void auth_unix_ip(FILE *f) + if (qword_get(&cp, ipaddr, 20) <= 0) + return; + +- if (inet_aton(ipaddr, &addr)==0) ++ tmp = host_pton(ipaddr); ++ if (tmp == NULL) + return; + + auth_reload(); + + /* addr is a valid, interesting address, find the domain name... */ + if (!use_ipaddr) { +- he = client_resolve(addr); +- client = client_compose(he); ++ ai = client_resolve(tmp->ai_addr); ++ client = client_compose(ai); ++ freeaddrinfo(ai); + } +- ++ freeaddrinfo(tmp); ++ + qword_print(f, "nfsd"); + qword_print(f, ipaddr); + qword_printint(f, time(0)+30*60); +@@ -114,8 +117,7 @@ void auth_unix_ip(FILE *f) + qword_eol(f); + xlog(D_CALL, "auth_unix_ip: client %p '%s'", client, client?client: "DEFAULT"); + +- if (client) free(client); +- free(he); ++ free(client); + } + + void auth_unix_gid(FILE *f) +@@ -125,7 +127,7 @@ void auth_unix_gid(FILE *f) + * reply is + * uid expiry count list of group ids + */ +- int uid; ++ uid_t uid; + struct passwd *pw; + gid_t glist[100], *groups = glist; + int ngroups = 100; +@@ -136,7 +138,7 @@ void auth_unix_gid(FILE *f) + return; + + cp = lbuf; +- if (qword_get_int(&cp, &uid) != 0) ++ if (qword_get_uint(&cp, &uid) != 0) + return; + + pw = getpwuid(uid); +@@ -153,14 +155,14 @@ void auth_unix_gid(FILE *f) + groups, &ngroups); + } + } +- qword_printint(f, uid); +- qword_printint(f, time(0)+30*60); ++ qword_printuint(f, uid); ++ qword_printuint(f, time(0)+30*60); + if (rv >= 0) { +- qword_printint(f, ngroups); ++ qword_printuint(f, ngroups); + for (i=0; im_export.e_flags & NFSEXP_CROSSMOUNT) { + static nfs_export *prev = NULL; +@@ -461,22 +507,29 @@ void nfsd_fh(FILE *f) + continue; + check_uuid: + if (exp->m_export.e_uuid) +- get_uuid(NULL, exp->m_export.e_uuid, ++ get_uuid(exp->m_export.e_uuid, + uuidlen, u); +- else if (get_uuid(path, NULL, uuidlen, u) == 0) +- continue; ++ else ++ for (type = 0; ++ uuid_by_path(path, type, uuidlen, u); ++ type++) ++ if (memcmp(u, fhuuid, uuidlen) != 0) ++ break; + + if (memcmp(u, fhuuid, uuidlen) != 0) + continue; + break; + } + if (use_ipaddr) { +- if (he == NULL) { +- if (!inet_aton(dom, &addr)) ++ if (ai == NULL) { ++ struct addrinfo *tmp; ++ tmp = host_pton(dom); ++ if (tmp == NULL) + goto out; +- he = client_resolve(addr); ++ ai = client_resolve(tmp->ai_addr); ++ freeaddrinfo(tmp); + } +- if (!client_check(exp->m_client, he)) ++ if (!client_check(exp->m_client, ai)) + continue; + } + /* It's a match !! */ +@@ -534,8 +587,7 @@ void nfsd_fh(FILE *f) + out: + if (found_path) + free(found_path); +- if (he) +- free(he); ++ freeaddrinfo(ai); + free(dom); + xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL); + return; +@@ -600,13 +652,13 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex + write_secinfo(f, exp, flag_mask); + if (exp->e_uuid == NULL || different_fs) { + char u[16]; +- if (get_uuid(path, NULL, 16, u)) { ++ if (uuid_by_path(path, 0, 16, u)) { + qword_print(f, "uuid"); + qword_printhex(f, u, 16); + } + } else { + char u[16]; +- get_uuid(NULL, exp->e_uuid, 16, u); ++ get_uuid(exp->e_uuid, 16, u); + qword_print(f, "uuid"); + qword_printhex(f, u, 16); + } +@@ -614,12 +666,12 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex + return qword_eol(f); + } + +-static int is_subdirectory(char *subpath, char *path) ++static int is_subdirectory(char *child, char *parent) + { +- int l = strlen(path); ++ int l = strlen(parent); + +- return strcmp(subpath, path) == 0 +- || (strncmp(subpath, path, l) == 0 && path[l] == '/'); ++ return strcmp(child, parent) == 0 ++ || (strncmp(child, parent, l) == 0 && child[l] == '/'); + } + + static int path_matches(nfs_export *exp, char *path) +@@ -629,19 +681,22 @@ static int path_matches(nfs_export *exp, char *path) + return strcmp(path, exp->m_export.e_path) == 0; + } + +-static int client_matches(nfs_export *exp, char *dom, struct hostent *he) ++static int ++client_matches(nfs_export *exp, char *dom, struct addrinfo *ai) + { + if (use_ipaddr) +- return client_check(exp->m_client, he); ++ return client_check(exp->m_client, ai); + return client_member(dom, exp->m_client->m_hostname); + } + +-static int export_matches(nfs_export *exp, char *dom, char *path, struct hostent *he) ++static int ++export_matches(nfs_export *exp, char *dom, char *path, struct addrinfo *ai) + { +- return path_matches(exp, path) && client_matches(exp, dom, he); ++ return path_matches(exp, path) && client_matches(exp, dom, ai); + } + +-static nfs_export *lookup_export(char *dom, char *path, struct hostent *he) ++static nfs_export * ++lookup_export(char *dom, char *path, struct addrinfo *ai) + { + nfs_export *exp; + nfs_export *found = NULL; +@@ -650,7 +705,7 @@ static nfs_export *lookup_export(char *dom, char *path, struct hostent *he) + + for (i=0 ; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { +- if (!export_matches(exp, dom, path, he)) ++ if (!export_matches(exp, dom, path, ai)) + continue; + if (!found) { + found = exp; +@@ -698,9 +753,7 @@ void nfsd_export(FILE *f) + char *cp; + char *dom, *path; + nfs_export *found = NULL; +- struct in_addr addr; +- struct hostent *he = NULL; +- ++ struct addrinfo *ai = NULL; + + if (readline(fileno(f), &lbuf, &lbuflen) != 1) + return; +@@ -722,12 +775,16 @@ void nfsd_export(FILE *f) + auth_reload(); + + if (use_ipaddr) { +- if (!inet_aton(dom, &addr)) ++ struct addrinfo *tmp; ++ tmp = host_pton(dom); ++ if (tmp == NULL) ++ goto out; ++ ai = client_resolve(tmp->ai_addr); ++ freeaddrinfo(tmp); + goto out; +- he = client_resolve(addr); + } + +- found = lookup_export(dom, path, he); ++ found = lookup_export(dom, path, ai); + + if (found) { + if (dump_to_cache(f, dom, path, &found->m_export) < 0) { +@@ -743,7 +800,7 @@ void nfsd_export(FILE *f) + xlog(D_CALL, "nfsd_export: found %p path %s", found, path ? path : NULL); + if (dom) free(dom); + if (path) free(path); +- if (he) free(he); ++ freeaddrinfo(ai); + } + + +@@ -863,6 +920,7 @@ int cache_export_ent(char *domain, struct exportent *exp, char *path) + + int cache_export(nfs_export *exp, char *path) + { ++ char buf[INET_ADDRSTRLEN]; + int err; + FILE *f; + +@@ -870,8 +928,10 @@ int cache_export(nfs_export *exp, char *path) + if (!f) + return -1; + ++ + qword_print(f, "nfsd"); +- qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0])); ++ qword_print(f, ++ host_ntop(get_addrlist(exp->m_client, 0), buf, sizeof(buf))); + qword_printint(f, time(0)+30*60); + qword_print(f, exp->m_client->m_hostname); + err = qword_eol(f); +diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c +index a0a1f2d..6571454 100644 +--- a/utils/mountd/mountd.c ++++ b/utils/mountd/mountd.c +@@ -80,10 +80,10 @@ static int nfs_version = -1; + static void + unregister_services (void) + { +- if (nfs_version & 0x1) ++ if (nfs_version & (0x1 << 1)) { + pmap_unset (MOUNTPROG, MOUNTVERS); +- if (nfs_version & (0x1 << 1)) + pmap_unset (MOUNTPROG, MOUNTVERS_POSIX); ++ } + if (nfs_version & (0x1 << 2)) + pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3); + } +@@ -536,22 +536,21 @@ static void free_exportlist(exports *elist) + + static void prune_clients(nfs_export *exp, struct exportnode *e) + { +- struct hostent *hp; ++ struct addrinfo *ai = NULL; + struct groupnode *c, **cp; + + cp = &e->ex_groups; + while ((c = *cp) != NULL) { + if (client_gettype(c->gr_name) == MCL_FQDN +- && (hp = gethostbyname(c->gr_name))) { +- hp = hostent_dup(hp); +- if (client_check(exp->m_client, hp)) { ++ && (ai = host_addrinfo(c->gr_name))) { ++ if (client_check(exp->m_client, ai)) { + *cp = c->gr_next; + xfree(c->gr_name); + xfree(c); +- xfree (hp); ++ freeaddrinfo(ai); + continue; + } +- xfree (hp); ++ freeaddrinfo(ai); + } + cp = &(c->gr_next); + } +@@ -712,8 +711,10 @@ main(int argc, char **argv) + usage(argv [0], 1); + } + +- /* No more arguments allowed. */ +- if (optind != argc || !(nfs_version & 0x7)) ++ /* No more arguments allowed. ++ * Require at least one valid version (2, 3, or 4) ++ */ ++ if (optind != argc || !(nfs_version & 0xE)) + usage(argv [0], 1); + + if (chdir(state_dir)) { +@@ -761,12 +762,12 @@ main(int argc, char **argv) + if (new_cache) + cache_open(); + +- if (nfs_version & 0x1) ++ if (nfs_version & (0x1 << 1)) { + rpc_init("mountd", MOUNTPROG, MOUNTVERS, + mount_dispatch, port); +- if (nfs_version & (0x1 << 1)) + rpc_init("mountd", MOUNTPROG, MOUNTVERS_POSIX, + mount_dispatch, port); ++ } + if (nfs_version & (0x1 << 2)) + rpc_init("mountd", MOUNTPROG, MOUNTVERS_NFSV3, + mount_dispatch, port); +diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c +index 19b22ee..ba0fcf6 100644 +--- a/utils/mountd/rmtab.c ++++ b/utils/mountd/rmtab.c +@@ -133,8 +133,7 @@ mountlist_del(char *hname, const char *path) + void + mountlist_del_all(struct sockaddr_in *sin) + { +- struct in_addr addr = sin->sin_addr; +- struct hostent *hp; ++ char *hostname; + struct rmtabent *rep; + nfs_export *exp; + FILE *fp; +@@ -142,11 +141,13 @@ mountlist_del_all(struct sockaddr_in *sin) + + if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0) + return; +- if (!(hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET))) { +- xlog(L_ERROR, "can't get hostname of %s", inet_ntoa(addr)); ++ hostname = host_canonname((struct sockaddr *)sin); ++ if (hostname == NULL) { ++ char buf[INET_ADDRSTRLEN]; ++ xlog(L_ERROR, "can't get hostname of %s", ++ host_ntop((struct sockaddr *)sin, buf, sizeof(buf))); + goto out_unlock; + } +- hp = hostent_dup (hp); + + if (!setrmtabent("r")) + goto out_free; +@@ -155,7 +156,7 @@ mountlist_del_all(struct sockaddr_in *sin) + goto out_close; + + while ((rep = getrmtabent(1, NULL)) != NULL) { +- if (strcmp(rep->r_client, hp->h_name) == 0 && ++ if (strcmp(rep->r_client, hostname) == 0 && + (exp = auth_authenticate("umountall", sin, rep->r_path))) + continue; + fputrmtabent(fp, rep, NULL); +@@ -168,7 +169,7 @@ mountlist_del_all(struct sockaddr_in *sin) + out_close: + endrmtabent(); /* close & unlink */ + out_free: +- free (hp); ++ free(hostname); + out_unlock: + xfunlock(lockid); + } +diff --git a/utils/showmount/showmount.c b/utils/showmount/showmount.c +index f567093..394f528 100644 +--- a/utils/showmount/showmount.c ++++ b/utils/showmount/showmount.c +@@ -194,7 +194,13 @@ int main(int argc, char **argv) + } + + mclient = nfs_get_mount_client(hostname, mount_vers_tbl[vers]); +- mclient->cl_auth = authunix_create_default(); ++ mclient->cl_auth = nfs_authsys_create(); ++ if (mclient->cl_auth == NULL) { ++ fprintf(stderr, "%s: unable to create RPC auth handle.\n", ++ program_name); ++ clnt_destroy(mclient); ++ exit(1); ++ } + total_timeout.tv_sec = TOTAL_TIMEOUT; + total_timeout.tv_usec = 0; + +diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c +index 3259a3e..437e37a 100644 +--- a/utils/statd/sm-notify.c ++++ b/utils/statd/sm-notify.c +@@ -54,7 +54,7 @@ struct nsm_host { + uint32_t xid; + }; + +-static char nsm_hostname[256]; ++static char nsm_hostname[SM_MAXSTRLEN + 1]; + static int nsm_state; + static int nsm_family = AF_INET; + static int opt_debug = 0; +@@ -412,12 +412,33 @@ usage: fprintf(stderr, + } + } + +- if (opt_srcaddr) { +- strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1); +- } else +- if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) { +- xlog(L_ERROR, "Failed to obtain name of local host: %m"); +- exit(1); ++ if (opt_srcaddr != NULL) { ++ struct addrinfo *ai = NULL; ++ struct addrinfo hint = { ++ .ai_family = AF_UNSPEC, ++ .ai_flags = AI_NUMERICHOST, ++ }; ++ ++ if (getaddrinfo(opt_srcaddr, NULL, &hint, &ai)) ++ /* not a presentation address - use it */ ++ strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)); ++ else { ++ /* was a presentation address - look it up in ++ * /etc/hosts, so it can be used for my_name */ ++ int error; ++ ++ freeaddrinfo(ai); ++ hint.ai_flags = AI_CANONNAME; ++ error = getaddrinfo(opt_srcaddr, NULL, &hint, &ai); ++ if (error != 0) { ++ xlog(L_ERROR, "Bind address %s is unusable: %s", ++ opt_srcaddr, gai_strerror(error)); ++ exit(1); ++ } ++ strncpy(nsm_hostname, ai->ai_canonname, ++ sizeof(nsm_hostname)); ++ freeaddrinfo(ai); ++ } + } + + (void)nsm_retire_monitored_hosts(); +@@ -535,6 +556,8 @@ notify(const int sock) + static int + notify_host(int sock, struct nsm_host *host) + { ++ const char *my_name = (opt_srcaddr != NULL ? ++ nsm_hostname : host->my_name); + struct sockaddr *sap; + socklen_t salen; + +@@ -580,8 +603,8 @@ notify_host(int sock, struct nsm_host *host) + host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS); + else + host->xid = nsm_xmit_notify(sock, sap, salen, +- SM_PROG, nsm_hostname, nsm_state); +- ++ SM_PROG, my_name, nsm_state); ++ + return 0; + } + +@@ -611,15 +634,28 @@ recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr) + } + + /* +- * Successful NOTIFY call. Server returns void, so nothing +- * we need to do here. ++ * Successful NOTIFY call. Server returns void. ++ * ++ * Try sending another SM_NOTIFY with an unqualified "my_name" ++ * argument. Reuse the port number. If "my_name" is already ++ * unqualified, we're done. + */ + static void + recv_notify_reply(struct nsm_host *host) + { +- xlog(D_GENERAL, "Host %s notified successfully", host->name); ++ char *dot = strchr(host->my_name, '.'); + +- smn_forget_host(host); ++ if (dot != NULL) { ++ *dot = '\0'; ++ host->send_next = time(NULL); ++ host->xid = 0; ++ if (host->timeout >= NSM_MAX_TIMEOUT / 4) ++ host->timeout = NSM_MAX_TIMEOUT / 4; ++ insert_host(host); ++ } else { ++ xlog(D_GENERAL, "Host %s notified successfully", host->name); ++ smn_forget_host(host); ++ } + } + + /* +diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man +index 163713e..7a1cbfa 100644 +--- a/utils/statd/sm-notify.man ++++ b/utils/statd/sm-notify.man +@@ -97,11 +97,9 @@ It uses the + string as the destination. + To identify which host has rebooted, the + .B sm-notify +-command normally sends the results of +-.BR gethostname (3) +-as the ++command normally sends + .I my_name +-string. ++string recorded when that remote was monitored. + The remote + .B rpc.statd + matches incoming SM_NOTIFY requests using this string, +@@ -202,15 +200,22 @@ argument to use when sending SM_NOTIFY requests. + If this option is not specified, + .B sm-notify + uses a wildcard address as the transport bind address, +-and uses the results of +-.BR gethostname (3) +-as the ++and uses the ++.I my_name ++recorded when the remote was monitored as the + .I mon_name +-argument. ++argument when sending SM_NOTIFY requests. + .IP + The + .I ipaddr + form can be expressed as either an IPv4 or an IPv6 presentation address. ++If the ++.I ipaddr ++form is used, the ++.B sm-notify ++command converts this address to a hostname for use as the ++.I mon_name ++argument when sending SM_NOTIFY requests. + .IP + This option can be useful in multi-homed configurations where + the remote requires notification from a specific network address. +@@ -252,13 +257,6 @@ consistent + The hostname the client uses to mount the server should match the server's + .I mon_name + in SM_NOTIFY requests it sends +-.IP +-The use of network addresses as a +-.I mon_name +-or a +-.I my_name +-string should be avoided when +-interoperating with non-Linux NFS implementations. + .PP + Unmounting an NFS file system does not necessarily stop + either the NFS client or server from monitoring each other. +diff --git a/utils/statd/statd.man b/utils/statd/statd.man +index ffc5e95..ca00e24 100644 +--- a/utils/statd/statd.man ++++ b/utils/statd/statd.man +@@ -100,11 +100,9 @@ It uses the + string as the destination. + To identify which host has rebooted, the + .B sm-notify +-command normally sends the results of +-.BR gethostname (3) +-as the ++command sends the + .I my_name +-string. ++string recorded when that remote was monitored. + The remote + .B rpc.statd + matches incoming SM_NOTIFY requests using this string, +@@ -292,7 +290,6 @@ man pages. + .SH ADDITIONAL NOTES + Lock recovery after a reboot is critical to maintaining data integrity + and preventing unnecessary application hangs. +-.PP + To help + .B rpc.statd + match SM_NOTIFY requests to NLM requests, a number of best practices +@@ -309,13 +306,6 @@ consistent + The hostname the client uses to mount the server should match the server's + .I mon_name + in SM_NOTIFY requests it sends +-.IP +-The use of network addresses as a +-.I mon_name +-or a +-.I my_name +-string should be avoided when +-interoperating with non-Linux NFS implementations. + .PP + Unmounting an NFS file system does not necessarily stop + either the NFS client or server from monitoring each other. diff --git a/nfs-utils.spec b/nfs-utils.spec index 6b1e642..4dd16d7 100644 --- a/nfs-utils.spec +++ b/nfs-utils.spec @@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser Name: nfs-utils URL: http://sourceforge.net/projects/nfs Version: 1.2.2 -Release: 4%{?dist} +Release: 5%{?dist} Epoch: 1 # group all 32bit related archs @@ -18,7 +18,7 @@ Source13: rpcgssd.init Source14: rpcsvcgssd.init Source15: nfs.sysconfig -Patch001: nfs-utils-1-2-3-rc3.patch +Patch001: nfs-utils-1-2-3-rc4.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.2-statdpath.patch @@ -251,6 +251,9 @@ fi %attr(4755,root,root) /sbin/umount.nfs4 %changelog +* Tue Jun 22 2010 Steve Dickson 1.2.2-5 +- Update to upstream RC release: nfs-utils-1-2-3-rc4 + * Thu May 6 2010 Steve Dickson 1.2.2-4 - Update to upstream RC release: nfs-utils-1-2-3-rc3