X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=support%2Fexport%2Fclient.c;h=780c74d1b8b22b7bd39b462be7a9f456770aceb7;hp=9a0fc9dabd1f58068a46cfea13d41838e618a830;hb=2824097496f6b154befbf3b3d15dacf237b07f83;hpb=0509d3428f523776ddd9d6e9fa318587d3ec7d84 diff --git a/support/export/client.c b/support/export/client.c index 9a0fc9d..780c74d 100644 --- a/support/export/client.c +++ b/support/export/client.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "misc.h" #include "nfslib.h" @@ -58,25 +59,58 @@ client_free(nfs_client *clp) } static int -init_netmask(nfs_client *clp, const char *slash) +init_netmask(nfs_client *clp, const char *slash, const sa_family_t family) { struct sockaddr_in sin = { .sin_family = AF_INET, }; + unsigned long prefixlen; + uint32_t shift; - 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 + /* No slash present; assume netmask is all ones */ + if (slash == NULL) { + switch (family) { + case AF_INET: + prefixlen = 32; + break; + default: + goto out_badfamily; + } + } else { + char *endptr; + + /* A spelled out netmask address, perhaps? */ + if (strchr(slash + 1, '.') != NULL) { + if (inet_pton(AF_INET, slash + 1, + &sin.sin_addr.s_addr) == 0) + goto out_badmask; + set_addrlist_in(clp, 1, &sin); + return 1; + } + + /* A prefixlen was given */ + prefixlen = strtoul(slash + 1, &endptr, 10); + if (*endptr != '\0' && prefixlen != ULONG_MAX && errno != ERANGE) goto out_badprefix; } - set_addrlist_in(clp, 1, &sin); - return 1; + switch (family) { + case AF_INET: + if (prefixlen > 32) + goto out_badprefix; + shift = 32 - (uint32_t)prefixlen; + sin.sin_addr.s_addr = htonl((uint32_t)~0 << shift); + set_addrlist_in(clp, 1, &sin); + return 1; + } + +out_badfamily: + xlog(L_ERROR, "Unsupported address family for %s", clp->m_hostname); + return 0; + +out_badmask: + xlog(L_ERROR, "Invalid netmask `%s' for %s", slash + 1, clp->m_hostname); + return 0; out_badprefix: xlog(L_ERROR, "Invalid prefix `%s' for %s", slash + 1, clp->m_hostname); @@ -86,22 +120,28 @@ out_badprefix: static int init_subnetwork(nfs_client *clp) { - struct sockaddr_in sin = { - .sin_family = AF_INET, - }; - static char slash32[] = "/32"; - char *cp; + struct addrinfo *ai; + sa_family_t family; + char *slash; + + slash = strchr(clp->m_hostname, '/'); + if (slash != NULL) { + *slash = '\0'; + ai = host_pton(clp->m_hostname); + *slash = '/'; + } else + ai = host_pton(clp->m_hostname); + if (ai == NULL) { + xlog(L_ERROR, "Invalid IP address %s", clp->m_hostname); + return false; + } - cp = strchr(clp->m_hostname, '/'); - if (cp == NULL) - cp = slash32; + set_addrlist(clp, 0, ai->ai_addr); + family = ai->ai_addr->sa_family; - *cp = '\0'; - sin.sin_addr.s_addr = inet_addr(clp->m_hostname); - set_addrlist_in(clp, 0, &sin); - *cp = '/'; + freeaddrinfo(ai); - return init_netmask(clp, cp); + return init_netmask(clp, slash, family); } static int @@ -134,9 +174,13 @@ client_add(nfs_client *clp) *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 +/** + * 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) @@ -215,6 +259,10 @@ client_dup(const nfs_client *clp, const struct addrinfo *ai) return new; } +/** + * client_release - drop a reference to an nfs_client record + * + */ void client_release(nfs_client *clp) { @@ -223,6 +271,10 @@ client_release(nfs_client *clp) clp->m_count--; } +/** + * client_freeall - deallocate all nfs_client records + * + */ void client_freeall(void) { @@ -478,43 +530,65 @@ static int check_netgroup(const nfs_client *clp, const struct addrinfo *ai) { const char *netgroup = clp->m_hostname + 1; - const char *hname = ai->ai_canonname; struct addrinfo *tmp = NULL; struct hostent *hp; + char *dot, *hname; int i, match; - char *dot; + + match = 0; + + hname = strdup(ai->ai_canonname); + if (hname == NULL) { + xlog(D_GENERAL, "%s: no memory for strdup", __func__); + goto out; + } /* First, try to match the hostname without * splitting off the domain */ - if (innetgr(netgroup, hname, NULL, NULL)) - return 1; + if (innetgr(netgroup, hname, NULL, NULL)) { + match = 1; + goto out; + } /* 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 (innetgr(netgroup, hp->h_aliases[i], NULL, NULL)) { + match = 1; + goto out; + } } - /* If hname is ip address convert to FQDN */ + /* If hname happens to be an IP address, convert it + * to a the canonical DNS name bound to this address. */ tmp = host_pton(hname); if (tmp != NULL) { + char *cname = host_canonname(tmp->ai_addr); freeaddrinfo(tmp); - if (innetgr(netgroup, hname, NULL, NULL)) - return 1; + + /* The resulting FQDN may be in our netgroup. */ + if (cname != NULL) { + free(hname); + hname = cname; + if (innetgr(netgroup, hname, NULL, NULL)) { + match = 1; + goto out; + } + } } /* Okay, strip off the domain (if we have one) */ dot = strchr(hname, '.'); if (dot == NULL) - return 0; + goto out; *dot = '\0'; match = innetgr(netgroup, hname, NULL, NULL); - *dot = '.'; +out: + free(hname); return match; } #else /* !HAVE_INNETGR */ @@ -558,10 +632,18 @@ client_check(const nfs_client *clp, const struct addrinfo *ai) 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) { - char *sp; + struct addrinfo *ai; + char *sp; if (ident[0] == '\0' || strcmp(ident, "*")==0) return MCL_ANONYMOUS; @@ -581,12 +663,16 @@ client_gettype(char *ident) if (*sp == '\\' && sp[1]) sp++; } - /* check for N.N.N.N */ - sp = ident; - if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN; - sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN; - sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN; - sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '\0') return MCL_FQDN; - /* we lie here a bit. but technically N.N.N.N == N.N.N.N/32 :) */ - return MCL_SUBNETWORK; + + /* + * Treat unadorned IP addresses as MCL_SUBNETWORK. + * Everything else is MCL_FQDN. + */ + ai = host_pton(ident); + if (ai != NULL) { + freeaddrinfo(ai); + return MCL_SUBNETWORK; + } + + return MCL_FQDN; }