X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fmount%2Fnetwork.c;h=8dc183a16e78983ef8fcf8f79506275b1c635763;hp=ad6c14644e7f31fa64196cf2af86a25cd87caf45;hb=bc7736ea87166e99d6b4f57da84d26d007db8ae4;hpb=5600327322a78a3a803368c0fe4f923cf14a4cf7 diff --git a/utils/mount/network.c b/utils/mount/network.c index ad6c146..8dc183a 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -37,11 +37,13 @@ #include #include #include +#include #include #include #include #include +#include "sockaddr.h" #include "xcommon.h" #include "mount.h" #include "nls.h" @@ -50,15 +52,12 @@ #include "nfsrpc.h" #include "parse_opt.h" #include "network.h" +#include "conffile.h" #define PMAP_TIMEOUT (10) #define CONNECT_TIMEOUT (20) #define MOUNT_TIMEOUT (30) -#if SIZEOF_SOCKLEN_T - 0 == 0 -#define socklen_t unsigned int -#endif - extern int nfs_mount_data_version; extern char *progname; extern int verbose; @@ -90,6 +89,7 @@ static const char *nfs_transport_opttbl[] = { static const char *nfs_version_opttbl[] = { "v2", "v3", + "v4", "vers", "nfsvers", NULL, @@ -170,8 +170,39 @@ static const unsigned long probe_mnt3_first[] = { 0, }; -static int nfs_lookup(const char *hostname, const sa_family_t family, - struct sockaddr *sap, socklen_t *salen) +static const unsigned int *nfs_default_proto(void); +#ifdef MOUNT_CONFIG +static const unsigned int *nfs_default_proto() +{ + extern unsigned long config_default_proto; + /* + * If the default proto has been set and + * its not TCP, start with UDP + */ + if (config_default_proto && config_default_proto != IPPROTO_TCP) + return probe_udp_first; + + return probe_tcp_first; +} +#else +static const unsigned int *nfs_default_proto() +{ + return probe_tcp_first; +} +#endif /* MOUNT_CONFIG */ + +/** + * nfs_lookup - resolve hostname to an IPv4 or IPv6 socket address + * @hostname: pointer to C string containing DNS hostname to resolve + * @family: address family hint + * @sap: pointer to buffer to fill with socket address + * @len: IN: size of buffer to fill; OUT: size of socket address + * + * Returns 1 and places a socket address at @sap if successful; + * otherwise zero. + */ +int nfs_lookup(const char *hostname, const sa_family_t family, + struct sockaddr *sap, socklen_t *salen) { struct addrinfo *gai_results; struct addrinfo gai_hint = { @@ -219,25 +250,6 @@ static int nfs_lookup(const char *hostname, const sa_family_t family, return ret; } -/** - * nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address - * @hostname: pointer to C string containing DNS hostname to resolve - * @sap: pointer to buffer to fill with socket address - * @len: IN: size of buffer to fill; OUT: size of socket address - * - * Returns 1 and places a socket address at @sap if successful; - * otherwise zero. - */ -int nfs_name_to_address(const char *hostname, - struct sockaddr *sap, socklen_t *salen) -{ -#ifdef IPV6_SUPPORTED - return nfs_lookup(hostname, AF_UNSPEC, sap, salen); -#else /* !IPV6_SUPPORTED */ - return nfs_lookup(hostname, AF_INET, sap, salen); -#endif /* !IPV6_SUPPORTED */ -} - /** * nfs_gethostbyname - resolve a hostname to an IPv4 address * @hostname: pointer to a C string containing a DNS hostname @@ -255,22 +267,21 @@ int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin) /** * nfs_string_to_sockaddr - convert string address to sockaddr * @address: pointer to presentation format address to convert - * @addrlen: length of presentation address * @sap: pointer to socket address buffer to fill in * @salen: IN: length of address buffer * OUT: length of converted socket address * * Convert a presentation format address string to a socket address. - * Similar to nfs_name_to_address(), but the DNS query is squelched, - * and won't make any noise if the getaddrinfo() call fails. + * Similar to nfs_lookup(), but the DNS query is squelched, and it + * won't make any noise if the getaddrinfo() call fails. * * Returns 1 and fills in @sap and @salen if successful; otherwise zero. * * See RFC 4038 section 5.1 or RFC 3513 section 2.2 for more details * on presenting IPv6 addresses as text strings. */ -int nfs_string_to_sockaddr(const char *address, const size_t addrlen, - struct sockaddr *sap, socklen_t *salen) +int nfs_string_to_sockaddr(const char *address, struct sockaddr *sap, + socklen_t *salen) { struct addrinfo *gai_results; struct addrinfo gai_hint = { @@ -527,8 +538,8 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen, struct pmap *pmap, const unsigned long *versions, const unsigned int *protos) { - struct sockaddr_storage address; - struct sockaddr *saddr = (struct sockaddr *)&address; + union nfs_sockaddr address; + struct sockaddr *saddr = &address.sa; const unsigned long prog = pmap->pm_prog, *p_vers; const unsigned int prot = (u_int)pmap->pm_prot, *p_prot; const u_short port = (u_short) pmap->pm_port; @@ -589,7 +600,6 @@ out_ok: nfs_clear_rpc_createerr(); return 1; } - /* * Probe a server's NFS service to determine which versions and * transport protocols are supported. @@ -609,10 +619,14 @@ static int nfs_probe_nfsport(const struct sockaddr *sap, const socklen_t salen, if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port) return 1; - if (nfs_mount_data_version >= 4) + if (nfs_mount_data_version >= 4) { + const unsigned int *probe_proto; + + probe_proto = nfs_default_proto(); + return nfs_probe_port(sap, salen, pmap, - probe_nfs3_first, probe_tcp_first); - else + probe_nfs3_first, probe_proto); + } else return nfs_probe_port(sap, salen, pmap, probe_nfs2_only, probe_udp_only); } @@ -783,8 +797,8 @@ int start_statd(void) execl(START_STATD, START_STATD, NULL); exit(1); case -1: /* error */ - nfs_error(_("fork failed: %s"), - strerror(errno)); + nfs_error(_("%s: fork failed: %s"), + progname, strerror(errno)); break; default: /* parent */ waitpid(pid, NULL,0); @@ -815,8 +829,8 @@ int start_statd(void) int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen, const struct pmap *pmap, const dirpath *argp) { - struct sockaddr_storage address; - struct sockaddr *saddr = (struct sockaddr *)&address; + union nfs_sockaddr address; + struct sockaddr *saddr = &address.sa; struct pmap mnt_pmap = *pmap; struct timeval timeout = { .tv_sec = MOUNT_TIMEOUT >> 3, @@ -849,10 +863,14 @@ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen, (xdrproc_t)xdr_dirpath, (caddr_t)argp, (xdrproc_t)xdr_void, NULL, timeout); - if (verbose && res != RPC_SUCCESS) - nfs_error(_("%s: UMNT call failed: %s"), - progname, clnt_sperrno(res)); + if (res != RPC_SUCCESS) { + rpc_createerr.cf_stat = res; + CLNT_GETERR(client, &rpc_createerr.cf_error); + if (verbose) + nfs_error(_("%s: UMNT call failed: %s"), + progname, clnt_sperrno(res)); + } auth_destroy(client->cl_auth); CLNT_DESTROY(client); @@ -1103,7 +1121,7 @@ static int nfs_ca_sockname(const struct sockaddr *sap, const socklen_t salen, * * Returns 1 and fills in @buf if successful; otherwise, zero. */ -static int nfs_ca_gai(const struct sockaddr *sap, const socklen_t salen, +static int nfs_ca_gai(const struct sockaddr *sap, struct sockaddr *buf, socklen_t *buflen) { struct addrinfo *gai_results; @@ -1144,7 +1162,7 @@ int nfs_callback_address(const struct sockaddr *sap, const socklen_t salen, struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf; if (nfs_ca_sockname(sap, salen, buf, buflen) == 0) - if (nfs_ca_gai(sap, salen, buf, buflen) == 0) + if (nfs_ca_gai(sap, buf, buflen) == 0) goto out_failed; /* @@ -1159,7 +1177,8 @@ int nfs_callback_address(const struct sockaddr *sap, const socklen_t salen, out_failed: *buflen = 0; if (verbose) - nfs_error(_("%s: failed to construct callback address")); + nfs_error(_("%s: failed to construct callback address"), + progname); return 0; } @@ -1199,7 +1218,7 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program) * Returns TRUE if @version contains a valid value for this option, * or FALSE if the option was specified with an invalid value. */ -static int +int nfs_nfs_version(struct mount_options *options, unsigned long *version) { long tmp; @@ -1211,10 +1230,13 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) case 1: /* v3 */ *version = 3; return 1; - case 2: /* vers */ + case 2: /* v4 */ + *version = 4; + return 1; + case 3: /* vers */ switch (po_get_numeric(options, "vers", &tmp)) { case PO_FOUND: - if (tmp >= 2 && tmp <= 3) { + if (tmp >= 2 && tmp <= 4) { *version = tmp; return 1; } @@ -1225,10 +1247,10 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) case PO_BAD_VALUE: return 0; } - case 3: /* nfsvers */ + case 4: /* nfsvers */ switch (po_get_numeric(options, "nfsvers", &tmp)) { case PO_FOUND: - if (tmp >= 2 && tmp <= 3) { + if (tmp >= 2 && tmp <= 4) { *version = tmp; return 1; } @@ -1251,11 +1273,13 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) /* * Returns TRUE if @protocol contains a valid value for this option, - * or FALSE if the option was specified with an invalid value. + * or FALSE if the option was specified with an invalid value. On + * error, errno is set. */ -static int +int nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol) { + sa_family_t family; char *option; switch (po_rightmost(options, nfs_transport_opttbl)) { @@ -1267,16 +1291,12 @@ nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol) return 1; case 2: /* proto */ option = po_get(options, "proto"); - if (option) { - if (strcmp(option, "tcp") == 0) { - *protocol = IPPROTO_TCP; - return 1; - } - if (strcmp(option, "udp") == 0) { - *protocol = IPPROTO_UDP; - return 1; + if (option != NULL) { + if (!nfs_get_proto(option, &family, protocol)) { + errno = EPROTONOSUPPORT; + return 0; } - return 0; + return 1; } } @@ -1318,6 +1338,61 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port) return 1; } +#ifdef IPV6_SUPPORTED +sa_family_t config_default_family = AF_UNSPEC; + +static int +nfs_verify_family(sa_family_t family) +{ + return 1; +} +#else /* IPV6_SUPPORTED */ +sa_family_t config_default_family = AF_INET; + +static int +nfs_verify_family(sa_family_t family) +{ + if (family != AF_INET) + return 0; + + return 1; +} +#endif /* IPV6_SUPPORTED */ + +/* + * Returns TRUE and fills in @family if a valid NFS protocol option + * is found, or FALSE if the option was specified with an invalid value + * or if the protocol family isn't supported. On error, errno is set. + */ +int nfs_nfs_proto_family(struct mount_options *options, + sa_family_t *family) +{ + unsigned long protocol; + char *option; + sa_family_t tmp_family = config_default_family; + + switch (po_rightmost(options, nfs_transport_opttbl)) { + case 0: /* udp */ + case 1: /* tcp */ + /* for compatibility; these are always AF_INET */ + *family = AF_INET; + return 1; + case 2: /* proto */ + option = po_get(options, "proto"); + if (option != NULL && + !nfs_get_proto(option, &tmp_family, &protocol)) + goto out_err; + } + + if (!nfs_verify_family(tmp_family)) + goto out_err; + *family = tmp_family; + return 1; +out_err: + errno = EAFNOSUPPORT; + return 0; +} + /* * "mountprog" is supported only by the legacy mount command. The * kernel mount client does not support this option. @@ -1381,24 +1456,22 @@ nfs_mount_version(struct mount_options *options, unsigned long *version) /* * Returns TRUE if @protocol contains a valid value for this option, - * or FALSE if the option was specified with an invalid value. + * or FALSE if the option was specified with an invalid value. On + * error, errno is set. */ static int nfs_mount_protocol(struct mount_options *options, unsigned long *protocol) { + sa_family_t family; char *option; option = po_get(options, "mountproto"); - if (option) { - if (strcmp(option, "tcp") == 0) { - *protocol = IPPROTO_TCP; - return 1; - } - if (strcmp(option, "udp") == 0) { - *protocol = IPPROTO_UDP; - return 1; + if (option != NULL) { + if (!nfs_get_proto(option, &family, protocol)) { + errno = EPROTONOSUPPORT; + return 0; } - return 0; + return 1; } /* @@ -1439,6 +1512,40 @@ nfs_mount_port(struct mount_options *options, unsigned long *port) return 1; } +/* + * Returns TRUE and fills in @family if a valid MNT protocol option + * is found, or FALSE if the option was specified with an invalid value + * or if the protocol family isn't supported. On error, errno is set. + */ +int nfs_mount_proto_family(struct mount_options *options, + sa_family_t *family) +{ + unsigned long protocol; + char *option; + sa_family_t tmp_family = config_default_family; + + option = po_get(options, "mountproto"); + if (option != NULL) { + if (!nfs_get_proto(option, &tmp_family, &protocol)) + goto out_err; + if (!nfs_verify_family(tmp_family)) + goto out_err; + *family = tmp_family; + return 1; + } + + /* + * MNT transport protocol wasn't specified. If the NFS + * transport protocol was specified, derive the family + * from that; otherwise, return the default family for + * NFS. + */ + return nfs_nfs_proto_family(options, family); +out_err: + errno = EAFNOSUPPORT; + return 0; +} + /** * nfs_options2pmap - set up pmap structs based on mount options * @options: pointer to mount options