On Fri, 20 Aug 2010 00:26:14 +0400 Igor Druzhinin <jaxbrigs@xxxxxxxxx> wrote: > It is a userspace part of a new infrastructure for stashing passwords > in kernel keyring per user basis. The patch adds the "cifscreds" > utility for management keys with credentials. The resolve_host > routine from mount.cifs is carried out in separate file and > appropriate corrections are made. Assembling of the utility from > the distribution is possible with --enable-cifscreds=yes option of > configure script. > > Signed-off-by: Igor Druzhinin <jaxbrigs@xxxxxxxxx> > --- > Makefile.am | 7 +- > cifscreds.c | 582 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > configure.ac | 15 ++- > mount.cifs.c | 105 ++--------- > resolve_host.c | 106 ++++++++++ > resolve_host.h | 34 ++++ > 6 files changed, 754 insertions(+), 95 deletions(-) > create mode 100644 cifscreds.c > create mode 100644 resolve_host.c > create mode 100644 resolve_host.h > > diff --git a/Makefile.am b/Makefile.am > index c53c9ec..38a16fe 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -3,7 +3,7 @@ ACLOCAL_AMFLAGS = -I aclocal > > root_sbindir = "/sbin" > root_sbin_PROGRAMS = mount.cifs > -mount_cifs_SOURCES = mount.cifs.c mtab.c util.c > +mount_cifs_SOURCES = mount.cifs.c mtab.c resolve_host.c util.c > mount_cifs_LDADD = $(LIBCAP) $(CAPNG_LDADD) > > man_MANS = mount.cifs.8 > @@ -15,3 +15,8 @@ cifs_upcall_LDADD = -ltalloc -lkeyutils $(KRB5_LDADD) > man_MANS += cifs.upcall.8 > endif > > +if CONFIG_CIFSCREDS > +bin_PROGRAMS = cifscreds > +cifscreds_SOURCES = cifscreds.c resolve_host.c util.c > +cifscreds_LDADD = -lkeyutils > +endif > diff --git a/cifscreds.c b/cifscreds.c > new file mode 100644 > index 0000000..f21a47f > --- /dev/null > +++ b/cifscreds.c > @@ -0,0 +1,582 @@ > +/* > + * Credentials stashing utility for Linux CIFS VFS (virtual filesystem) client > + * Copyright (C) 2010 Jeff Layton (jlayton@xxxxxxxxx) > + * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@xxxxxxxxx) > + * > + * This program 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 3 of the License, or > + * (at your option) any later version. > + * > + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#ifdef HAVE_CONFIG_H > +#include "config.h" > +#endif /* HAVE_CONFIG_H */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <unistd.h> > +#include <string.h> > +#include <ctype.h> > +#include <keyutils.h> > +#include "mount.h" > +#include "resolve_host.h" > + > +#define THIS_PROGRAM_NAME "cifscreds" > + > +/* max length of appropriate command */ > +#define MAX_COMMAND_SIZE 32 > + > +/* max length of username, password and domain name */ > +#define MAX_USERNAME_SIZE 32 > +#define MOUNT_PASSWD_SIZE 128 > +#define MAX_DOMAIN_SIZE 64 > + > +/* allowed and disallowed characters for user and domain name */ > +#define USER_DISALLOWED_CHARS "\\/\"[]:|<>+=;,?*@" > +#define DOMAIN_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz" \ > + "ABCDEFGHIJKLMNOPQRSTUVWXYZ-." > + > +/* destination keyring */ > +#define DEST_KEYRING KEY_SPEC_USER_KEYRING > + > +struct command { > + int (*action)(int argc, char *argv[]); > + const char name[MAX_COMMAND_SIZE]; > + const char *format; > +}; > + > +static int cifscreds_add(int argc, char *argv[]); > +static int cifscreds_clear(int argc, char *argv[]); > +static int cifscreds_clearall(int argc, char *argv[]); > +static int cifscreds_update(int argc, char *argv[]); > + > +const char *thisprogram; > + > +struct command commands[] = { > + { cifscreds_add, "add", "<host> <user> [domain]" }, > + { cifscreds_clear, "clear", "<host> <user> [domain]" }, > + { cifscreds_clearall, "clearall", "" }, > + { cifscreds_update, "update", "<host> <user> [domain]" }, > + { NULL, "", NULL } > +}; > + > +/* display usage information */ > +static void usage(void) > +{ > + struct command *cmd; > + > + fprintf(stderr, "Usage:\n"); > + for (cmd = commands; cmd->action; cmd++) > + fprintf(stderr, "\t%s %s %s\n", thisprogram, > + cmd->name, cmd->format); > + fprintf(stderr, "\n"); > + > + exit(EXIT_FAILURE); > +} > + > +/* create key's description string from given credentials */ > +static char * > +create_description(const char *addr, const char *user, > + const char *domain, char *desc) > +{ > + char *str_end; > + int str_len; > + > + sprintf(desc, "%s:%s:%s:", THIS_PROGRAM_NAME, addr, user); > + > + if (domain != NULL) { > + str_end = desc + strnlen(desc, INET6_ADDRSTRLEN + \ > + + MAX_USERNAME_SIZE + \ > + + sizeof(THIS_PROGRAM_NAME) + 3); > + str_len = strnlen(domain, MAX_DOMAIN_SIZE); > + while (str_len--) { > + *str_end = tolower(*domain++); > + str_end++; > + } > + *str_end = '\0'; > + } > + > + return desc; > +} > + > +/* search a specific key in keyring */ > +static key_serial_t > +key_search(const char *addr, const char *user, const char *domain) > +{ > + char desc[INET6_ADDRSTRLEN + MAX_USERNAME_SIZE + MAX_DOMAIN_SIZE + \ > + + sizeof(THIS_PROGRAM_NAME) + 3]; > + key_serial_t key, *pk; > + void *keylist; > + char *buffer; > + int count, dpos, n, ret; > + > + create_description(addr, user, domain, desc); > + > + /* read the key payload data */ > + count = keyctl_read_alloc(DEST_KEYRING, &keylist); > + if (count < 0) > + return 0; > + > + count /= sizeof(key_serial_t); > + > + if (count == 0) { > + ret = 0; > + goto key_search_out; > + } > + > + /* list the keys in the keyring */ > + pk = keylist; > + do { > + key = *pk++; > + > + ret = keyctl_describe_alloc(key, &buffer); > + if (ret < 0) > + continue; > + > + n = sscanf(buffer, "%*[^;];%*d;%*d;%*x;%n", &dpos); > + if (n) { > + free(buffer); > + continue; > + } > + > + if (!strcmp(buffer + dpos, desc)) { > + ret = key; > + free(buffer); > + goto key_search_out; > + } > + free(buffer); > + > + } while (--count); > + > + ret = 0; > + > +key_search_out: > + free(keylist); > + return ret; > +} > + > +/* search all program's keys in keyring */ > +static key_serial_t key_search_all(void) > +{ > + key_serial_t key, *pk; > + void *keylist; > + char *buffer; > + int count, dpos, n, ret; > + > + /* read the key payload data */ > + count = keyctl_read_alloc(DEST_KEYRING, &keylist); > + if (count < 0) > + return 0; > + > + count /= sizeof(key_serial_t); > + > + if (count == 0) { > + ret = 0; > + goto key_search_all_out; > + } > + > + /* list the keys in the keyring */ > + pk = keylist; > + do { > + key = *pk++; > + > + ret = keyctl_describe_alloc(key, &buffer); > + if (ret < 0) > + continue; > + > + n = sscanf(buffer, "%*[^;];%*d;%*d;%*x;%n", &dpos); > + if (n) { > + free(buffer); > + continue; > + } > + > + if (strstr(buffer + dpos, THIS_PROGRAM_NAME ":") == > + buffer + dpos > + ) { > + ret = key; > + free(buffer); > + goto key_search_all_out; > + } > + free(buffer); > + > + } while (--count); > + > + ret = 0; > + > +key_search_all_out: > + free(keylist); > + return ret; > +} > + > +/* add or update a specific key to keyring */ > +static key_serial_t > +key_add(const char *addr, const char *user, > + const char *domain, const char *pass) > +{ > + char desc[INET6_ADDRSTRLEN + MAX_USERNAME_SIZE + MAX_DOMAIN_SIZE + \ > + + sizeof(THIS_PROGRAM_NAME) + 3]; > + > + create_description(addr, user, domain, desc); > + > + return add_key("user", desc, pass, strnlen(pass, MOUNT_PASSWD_SIZE) + 1, > + DEST_KEYRING); > +} > + > +/* add command handler */ > +static int cifscreds_add(int argc, char *argv[]) > +{ > + char addrstr[MAX_ADDR_LIST_LEN]; > + char *currentaddress, *nextaddress; > + char *pass; > + int ret; > + > + if (argc != 4 && argc != 5) > + usage(); > + > + ret = resolve_host(argv[2], addrstr); > + switch (ret) { > + case EX_USAGE: > + fprintf(stderr, "error: Could not resolve address " > + "for %s\n", argv[2]); > + return EXIT_FAILURE; > + > + case EX_SYSERR: > + fprintf(stderr, "error: Problem parsing address list\n"); > + return EXIT_FAILURE; > + } > + > + if (strpbrk(argv[3], USER_DISALLOWED_CHARS)) { > + fprintf(stderr, "error: Incorrect username\n"); > + return EXIT_FAILURE; > + } > + > + if (argc == 5) { > + if (strspn(argv[4], DOMAIN_ALLOWED_CHARS) != > + strnlen(argv[4], MAX_DOMAIN_SIZE) > + ) { > + fprintf(stderr, "error: Incorrect domain name\n"); > + return EXIT_FAILURE; > + } > + } > + > + /* search for same credentials stashed for current host */ > + currentaddress = addrstr; > + nextaddress = strchr(currentaddress, ','); > + if (nextaddress) > + *nextaddress++ = '\0'; > + > + while (currentaddress) { > + if (key_search(currentaddress, argv[3], > + argc == 5 ? argv[4] : NULL) > 0 > + ) { > + printf("You already have stashed credentials " > + "for %s (%s)\n", currentaddress, argv[2]); > + printf("If you want to update them use:\n"); > + printf("\t%s update\n", thisprogram); > + > + return EXIT_FAILURE; > + } > + > + currentaddress = nextaddress; > + if (currentaddress) { > + *(currentaddress - 1) = ','; > + nextaddress = strchr(currentaddress, ','); > + if (nextaddress) > + *nextaddress++ = '\0'; > + } > + } > + > + /* > + * if there isn't same credentials stashed add them to keyring > + * and set permisson mask > + */ > + pass = getpass("Password: "); > + > + currentaddress = addrstr; > + nextaddress = strchr(currentaddress, ','); > + if (nextaddress) > + *nextaddress++ = '\0'; > + > + while (currentaddress) { > + key_serial_t key = key_add(currentaddress, argv[3], > + argc == 5 ? argv[4] : NULL, pass); > + if (key <= 0) { > + fprintf(stderr, "error: Add credential key for %s\n", > + currentaddress); > + } else { > + if (keyctl(KEYCTL_SETPERM, key, KEY_POS_VIEW | \ > + KEY_POS_WRITE | KEY_USR_VIEW | \ > + KEY_USR_WRITE) < 0 > + ) { > + fprintf(stderr, "error: Setting permissons " > + "on key, attempt to delete...\n"); > + > + if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) { > + fprintf(stderr, "error: Deleting key from " > + "keyring for %s (%s)\n", > + currentaddress, argv[2]); > + } > + } > + } > + > + currentaddress = nextaddress; > + if (currentaddress) { > + nextaddress = strchr(currentaddress, ','); > + if (nextaddress) > + *nextaddress++ = '\0'; > + } > + } > + > + return EXIT_SUCCESS; > +} > + > +/* clear command handler */ > +static int cifscreds_clear(int argc, char *argv[]) > +{ > + char addrstr[MAX_ADDR_LIST_LEN]; > + char *currentaddress, *nextaddress; > + int ret, count = 0, errors = 0; > + > + if (argc != 4 && argc != 5) > + usage(); > + > + ret = resolve_host(argv[2], addrstr); > + switch (ret) { > + case EX_USAGE: > + fprintf(stderr, "error: Could not resolve address " > + "for %s\n", argv[2]); > + return EXIT_FAILURE; > + > + case EX_SYSERR: > + fprintf(stderr, "error: Problem parsing address list\n"); > + return EXIT_FAILURE; > + } > + > + if (strpbrk(argv[3], USER_DISALLOWED_CHARS)) { > + fprintf(stderr, "error: Incorrect username\n"); > + return EXIT_FAILURE; > + } > + > + if (argc == 5) { > + if (strspn(argv[4], DOMAIN_ALLOWED_CHARS) != > + strnlen(argv[4], MAX_DOMAIN_SIZE) > + ) { > + fprintf(stderr, "error: Incorrect domain name\n"); > + return EXIT_FAILURE; > + } > + } > + > + /* > + * search for same credentials stashed for current host > + * and unlink them from session keyring > + */ > + currentaddress = addrstr; > + nextaddress = strchr(currentaddress, ','); > + if (nextaddress) > + *nextaddress++ = '\0'; > + > + while (currentaddress) { > + key_serial_t key = key_search(currentaddress, argv[3], > + argc == 5 ? argv[4] : NULL); > + if (key > 0) { > + if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) { > + fprintf(stderr, "error: Removing key from " > + "keyring for %s (%s)\n", > + currentaddress, argv[2]); > + errors++; > + } else { > + count++; > + } > + } > + > + currentaddress = nextaddress; > + if (currentaddress) { > + nextaddress = strchr(currentaddress, ','); > + if (nextaddress) > + *nextaddress++ = '\0'; > + } > + } > + > + if (!count && !errors) { > + printf("You have no same stashed credentials " > + " for %s\n", argv[2]); > + printf("If you want to add them use:\n"); > + printf("\t%s add\n", thisprogram); > + > + return EXIT_FAILURE; > + } > + > + return EXIT_SUCCESS; > +} > + > +/* clearall command handler */ > +static int cifscreds_clearall(int argc, char *argv[]) > +{ > + key_serial_t key; > + int count = 0, errors = 0; > + > + if (argc != 2) > + usage(); > + > + /* > + * search for all program's credentials stashed in session keyring > + * and then unlink them > + */ > + do { > + key = key_search_all(); > + if (key > 0) { > + if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) { > + fprintf(stderr, "error: Deleting key " > + "from keyring"); > + errors++; > + } else { > + count++; > + } > + } > + } while (key > 0); > + > + if (!count && !errors) { > + printf("You have no stashed " THIS_PROGRAM_NAME > + " credentials\n"); > + printf("If you want to add them use:\n"); > + printf("\t%s add\n", thisprogram); > + > + return EXIT_FAILURE; > + } > + > + return EXIT_SUCCESS; > +} > + > +/* update command handler */ > +static int cifscreds_update(int argc, char *argv[]) > +{ > + char addrstr[MAX_ADDR_LIST_LEN]; > + char *currentaddress, *nextaddress, *pass; > + char *addrs[16]; > + int ret, id, count = 0; > + > + if (argc != 4 && argc != 5) > + usage(); > + > + ret = resolve_host(argv[2], addrstr); > + switch (ret) { > + case EX_USAGE: > + fprintf(stderr, "error: Could not resolve address " > + "for %s\n", argv[2]); > + return EXIT_FAILURE; > + > + case EX_SYSERR: > + fprintf(stderr, "error: Problem parsing address list\n"); > + return EXIT_FAILURE; > + } > + > + if (strpbrk(argv[3], USER_DISALLOWED_CHARS)) { > + fprintf(stderr, "error: Incorrect username\n"); > + return EXIT_FAILURE; > + } > + > + if (argc == 5) { > + if (strspn(argv[4], DOMAIN_ALLOWED_CHARS) != > + strnlen(argv[4], MAX_DOMAIN_SIZE) > + ) { > + fprintf(stderr, "error: Incorrect domain name\n"); > + return EXIT_FAILURE; > + } > + } > + > + /* search for necessary credentials stashed in session keyring */ > + currentaddress = addrstr; > + nextaddress = strchr(currentaddress, ','); > + if (nextaddress) > + *nextaddress++ = '\0'; > + > + while (currentaddress) { > + if (key_search(currentaddress, argv[3], > + argc == 5 ? argv[4] : NULL) > 0 > + ) { > + addrs[count] = currentaddress; > + count++; > + } > + > + currentaddress = nextaddress; > + if (currentaddress) { > + nextaddress = strchr(currentaddress, ','); > + if (nextaddress) > + *nextaddress++ = '\0'; > + } > + } > + > + if (!count) { > + printf("You have no same stashed credentials " > + "for %s\n", argv[2]); > + printf("If you want to add them use:\n"); > + printf("\t%s add\n", thisprogram); > + > + return EXIT_FAILURE; > + } > + > + /* update payload of found keys */ > + pass = getpass("Password: "); > + > + for (id = 0; id < count; id++) { > + key_serial_t key = key_add(addrs[id], argv[3], > + argc == 5 ? argv[4] : NULL, pass); > + if (key <= 0) > + fprintf(stderr, "error: Update credential key " > + "for %s\n", addrs[id]); > + } > + > + return EXIT_SUCCESS; > +} > + > +int main(int argc, char **argv) > +{ > + struct command *cmd, *best; > + int n; > + > + thisprogram = (char *)basename(argv[0]); > + if (thisprogram == NULL) > + thisprogram = THIS_PROGRAM_NAME; > + > + if (argc == 1) > + usage(); > + > + /* find the best fit command */ > + best = NULL; > + n = strnlen(argv[1], MAX_COMMAND_SIZE); > + > + for (cmd = commands; cmd->action; cmd++) { > + if (memcmp(cmd->name, argv[1], n) != 0) > + continue; > + > + if (cmd->name[n] == 0) { > + /* exact match */ > + best = cmd; > + break; > + } > + > + /* partial match */ > + if (best) { > + fprintf(stderr, "Ambiguous command\n"); > + exit(EXIT_FAILURE); > + } > + > + best = cmd; > + } > + > + if (!best) { > + fprintf(stderr, "Unknown command\n"); > + exit(EXIT_FAILURE); > + } > + > + exit(best->action(argc, argv)); > +} > diff --git a/configure.ac b/configure.ac > index 266380a..c7d420d 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -16,12 +16,18 @@ AC_ARG_ENABLE(cifsupcall, > enable_cifsupcall=$enableval, > enable_cifsupcall="maybe") > > +AC_ARG_ENABLE(cifscreds, > + [AC_HELP_STRING([--enable-cifscreds], > + [Create cifscreds utility @<:@default=no@:>@])], > + enable_cifscreds=$enableval, > + enable_cifscreds="no") > + > # Checks for programs. > AC_PROG_CC > AC_GNU_SOURCE > > # Checks for header files. > -AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h limits.h mntent.h netdb.h stddef.h stdint.h stdlib.h string.h strings.h sys/mount.h sys/param.h sys/socket.h sys/time.h syslog.h unistd.h], , [AC_MSG_ERROR([necessary header(s) not found])]) > +AC_CHECK_HEADERS([arpa/inet.h ctype.h fcntl.h inttypes.h limits.h mntent.h netdb.h stddef.h stdint.h stdlib.h string.h strings.h sys/mount.h sys/param.h sys/socket.h sys/time.h syslog.h unistd.h], , [AC_MSG_ERROR([necessary header(s) not found])]) > > if test $enable_cifsupcall != "no"; then > AC_CHECK_HEADERS([krb5.h krb5/krb5.h]) > @@ -82,6 +88,10 @@ if test $enable_cifsupcall != "no"; then > AC_SUBST(KRB5_LDADD) > fi > > +if test $enable_cifscreds = "yes"; then > + AC_CHECK_HEADERS([keyutils.h], , [AC_MSG_ERROR([keyutils.h not found, consider installing keyutils-libs-devel.])]) > +fi > + > # Checks for typedefs, structures, and compiler characteristics. > AC_HEADER_STDBOOL > AC_TYPE_UID_T > @@ -98,7 +108,7 @@ AC_FUNC_REALLOC > AC_FUNC_STRNLEN > > # check for required functions > -AC_CHECK_FUNCS([alarm atexit endpwent getmntent getpass gettimeofday inet_ntop memset realpath setenv strchr strdup strerror strncasecmp strndup strpbrk strrchr strstr strtol strtoul uname], , [AC_MSG_ERROR([necessary functions(s) not found])]) > +AC_CHECK_FUNCS([alarm atexit endpwent getmntent getpass gettimeofday inet_ntop memset realpath setenv strchr strcmp strdup strerror strncasecmp strndup strpbrk strrchr strstr strtol strtoul tolower uname], , [AC_MSG_ERROR([necessary functions(s) not found])]) > > # ugly, but I'm not sure how to check for functions in a library that's not in $LIBS > cu_saved_libs=$LIBS > @@ -117,6 +127,7 @@ fi > LIBS=$cu_saved_libs > > AM_CONDITIONAL(CONFIG_CIFSUPCALL, [test "$enable_cifsupcall" != "no"]) > +AM_CONDITIONAL(CONFIG_CIFSCREDS, [test "$enable_cifscreds" = "yes"]) > > LIBCAP_NG_PATH > > diff --git a/mount.cifs.c b/mount.cifs.c > index 3623e76..ed27bba 100644 > --- a/mount.cifs.c > +++ b/mount.cifs.c > @@ -56,6 +56,7 @@ > #endif /* HAVE_LIBCAP_NG */ > #include "mount.h" > #include "util.h" > +#include "resolve_host.h" > > #ifndef MS_MOVE > #define MS_MOVE 8192 > @@ -87,12 +88,6 @@ > /* max length of username (somewhat made up here) */ > #define MAX_USERNAME_SIZE 32 > > -/* currently maximum length of IPv6 address string */ > -#define MAX_ADDRESS_LEN INET6_ADDRSTRLEN > - > -/* limit list of addresses to 16 max-size addrs */ > -#define MAX_ADDR_LIST_LEN ((MAX_ADDRESS_LEN + 1) * 16) > - > #ifndef SAFE_FREE > #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x = NULL; } } while (0) > #endif > @@ -1207,90 +1202,6 @@ nocopy: > return 0; > } > > -/* > - * resolve "host" portion of parsed info to comma-separated list of > - * address(es) > - */ > -static int resolve_host(struct parsed_mount_info *parsed_info) > -{ > - int rc; > - /* 10 for max width of decimal scopeid */ > - char tmpbuf[NI_MAXHOST + 1 + 10 + 1]; > - const char *ipaddr; > - size_t len; > - struct addrinfo *addrlist, *addr; > - struct sockaddr_in *sin; > - struct sockaddr_in6 *sin6; > - > - rc = getaddrinfo(parsed_info->host, NULL, NULL, &addrlist); > - if (rc != 0) { > - fprintf(stderr, "mount error: could not resolve address for " > - "%s: %s\n", parsed_info->host, > - rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc)); > - /* FIXME: return better error based on rc? */ > - return EX_USAGE; > - } > - > - addr = addrlist; > - while (addr) { > - /* skip non-TCP entries */ > - if (addr->ai_socktype != SOCK_STREAM || > - addr->ai_protocol != IPPROTO_TCP) { > - addr = addr->ai_next; > - continue; > - } > - > - switch (addr->ai_addr->sa_family) { > - case AF_INET6: > - sin6 = (struct sockaddr_in6 *)addr->ai_addr; > - ipaddr = inet_ntop(AF_INET6, &sin6->sin6_addr, tmpbuf, > - sizeof(tmpbuf)); > - if (!ipaddr) { > - rc = EX_SYSERR; > - fprintf(stderr, > - "mount error: problem parsing address " > - "list: %s\n", strerror(errno)); > - goto resolve_host_out; > - } > - > - if (sin6->sin6_scope_id) { > - len = strnlen(tmpbuf, sizeof(tmpbuf)); > - ipaddr = tmpbuf + len; > - snprintf(tmpbuf, sizeof(tmpbuf) - len, "%%%u", > - sin6->sin6_scope_id); > - } > - break; > - case AF_INET: > - sin = (struct sockaddr_in *)addr->ai_addr; > - ipaddr = inet_ntop(AF_INET, &sin->sin_addr, tmpbuf, > - sizeof(tmpbuf)); > - if (!ipaddr) { > - rc = EX_SYSERR; > - fprintf(stderr, > - "mount error: problem parsing address " > - "list: %s\n", strerror(errno)); > - goto resolve_host_out; > - } > - > - break; > - default: > - addr = addr->ai_next; > - continue; > - } > - > - if (parsed_info->addrlist[0] != '\0') > - strlcat(parsed_info->addrlist, ",", > - sizeof(parsed_info->addrlist)); > - strlcat(parsed_info->addrlist, tmpbuf, > - sizeof(parsed_info->addrlist)); > - addr = addr->ai_next; > - } > - > -resolve_host_out: > - freeaddrinfo(addrlist); > - return rc; > -} > - > static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info) > { > int length = strnlen(unc_name, MAX_UNC_LEN); > @@ -1645,10 +1556,20 @@ assemble_mountinfo(struct parsed_mount_info *parsed_info, > if (rc) > goto assemble_exit; > > - rc = resolve_host(parsed_info); > - if (rc) > + rc = resolve_host(parsed_info->host, parsed_info->addrlist); > + switch (rc) { > + case EX_USAGE: > + fprintf(stderr, "mount error: could not resolve address for " > + "%s: %s\n", parsed_info->host, > + rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc)); > goto assemble_exit; > > + case EX_SYSERR: > + fprintf(stderr, "mount error: problem parsing address " > + "list: %s\n", strerror(errno)); > + goto assemble_exit; > + } > + > if (!parsed_info->got_user) { > /* > * Note that the password will not be retrieved from the > diff --git a/resolve_host.c b/resolve_host.c > new file mode 100644 > index 0000000..02b8096 > --- /dev/null > +++ b/resolve_host.c > @@ -0,0 +1,106 @@ > +/* > + * resolving DNS hostname routine > + * > + * Copyright (C) 2010 Jeff Layton (jlayton@xxxxxxxxx) > + * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@xxxxxxxxx) > + * > + * This program 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 3 of the License, or > + * (at your option) any later version. > + * > + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#ifdef HAVE_CONFIG_H > +#include "config.h" > +#endif /* HAVE_CONFIG_H */ > + > +#include <stdio.h> > +#include <string.h> > +#include <sys/socket.h> > +#include <arpa/inet.h> > +#include <netdb.h> > + > +#include "mount.h" > +#include "util.h" > +#include "resolve_host.h" > + > +/* > + * resolve hostname to comma-separated list of address(es) > + */ > +int resolve_host(const char *host, char *addrstr) > +{ > + int rc; > + /* 10 for max width of decimal scopeid */ > + char tmpbuf[NI_MAXHOST + 1 + 10 + 1]; > + const char *ipaddr; > + size_t len; > + struct addrinfo *addrlist, *addr; > + struct sockaddr_in *sin; > + struct sockaddr_in6 *sin6; > + > + rc = getaddrinfo(host, NULL, NULL, &addrlist); > + if (rc != 0) > + return EX_USAGE; > + > + addr = addrlist; > + while (addr) { > + /* skip non-TCP entries */ > + if (addr->ai_socktype != SOCK_STREAM || > + addr->ai_protocol != IPPROTO_TCP) { > + addr = addr->ai_next; > + continue; > + } > + > + switch (addr->ai_addr->sa_family) { > + case AF_INET6: > + sin6 = (struct sockaddr_in6 *)addr->ai_addr; > + ipaddr = inet_ntop(AF_INET6, &sin6->sin6_addr, tmpbuf, > + sizeof(tmpbuf)); > + if (!ipaddr) { > + rc = EX_SYSERR; > + goto resolve_host_out; > + } > + > + if (sin6->sin6_scope_id) { > + len = strnlen(tmpbuf, sizeof(tmpbuf)); > + ipaddr = tmpbuf + len; > + snprintf(tmpbuf, sizeof(tmpbuf) - len, "%%%u", > + sin6->sin6_scope_id); > + } > + break; > + case AF_INET: > + sin = (struct sockaddr_in *)addr->ai_addr; > + ipaddr = inet_ntop(AF_INET, &sin->sin_addr, tmpbuf, > + sizeof(tmpbuf)); > + if (!ipaddr) { > + rc = EX_SYSERR; > + goto resolve_host_out; > + } > + > + break; > + default: > + addr = addr->ai_next; > + continue; > + } > + > + if (addr == addrlist) > + *addrstr = '\0'; > + else > + strlcat(addrstr, ",", MAX_ADDR_LIST_LEN); > + > + strlcat(addrstr, tmpbuf, MAX_ADDR_LIST_LEN); > + addr = addr->ai_next; > + } > + > +resolve_host_out: > + freeaddrinfo(addrlist); > + return rc; > +} > diff --git a/resolve_host.h b/resolve_host.h > new file mode 100644 > index 0000000..b949245 > --- /dev/null > +++ b/resolve_host.h > @@ -0,0 +1,34 @@ > +/* > + * resolving DNS hostname routine > + * > + * Copyright (C) 2010 Jeff Layton (jlayton@xxxxxxxxx) > + * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@xxxxxxxxx) > + * > + * This program 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 3 of the License, or > + * (at your option) any later version. > + * > + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef _RESOLVE_HOST_H_ > +#define _RESOLVE_HOST_H_ > + > +#include <arpa/inet.h> > + > +/* currently maximum length of IPv6 address string */ > +#define MAX_ADDRESS_LEN INET6_ADDRSTRLEN > + > +/* limit list of addresses to 16 max-size addrs */ > +#define MAX_ADDR_LIST_LEN ((MAX_ADDRESS_LEN + 1) * 16) > + > +extern int resolve_host(const char *host, char *addrstr); > + > +#endif /* _RESOLVE_HOST_H_ */ This looks like good work and I think it's getting close to merge ready. Could you break this into more than one patch however? The move of resolve_host into a separate file should be a separate patch from the one that adds the new program. Thanks, -- Jeff Layton <jlayton@xxxxxxxxx> -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html