Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37863084
en ru br
Репозитории ALT
S:2.18-alt1
5.1: 1.22-alt3
4.1: 0.9.25-alt1.1
4.0: 0.9.25-alt1.1
www.altlinux.org/Changes

Группа :: Система/Библиотеки
Пакет: pam_mount

 Главная   Изменения   Спек   Патчи   Sources   Загрузить   Gear   Bugs and FR  Repocop 

Патч: pam_mount-1.22-alt3.patch
Скачать


 config/pam_mount.conf.xml |    2 +-
 configure.ac              |   31 +++
 src/Makefile.am           |    8 +-
 src/mount.c               |   27 ++-
 src/pam_mount.h           |    8 +
 src/pm_dnssd.c            |  590 +++++++++++++++++++++++++++++++++++++++++++++
 src/pm_dnssd.h            |   36 +++
 src/rdconf1.c             |  171 +++++++++++++-
 8 files changed, 867 insertions(+), 6 deletions(-)
diff --git a/config/pam_mount.conf.xml b/config/pam_mount.conf.xml
index ee4ae63..4b0ee89 100644
--- a/config/pam_mount.conf.xml
+++ b/config/pam_mount.conf.xml
@@ -20,7 +20,7 @@
 <!-- Note that commenting out mntoptions will give you the defaults.
      You will need to explicitly initialize it with the empty string
      to reset the defaults to nothing. -->
-<mntoptions allow="nosuid,nodev,loop,encryption,fsck,nonempty,allow_root,allow_other" />
+<mntoptions allow="nosuid,nodev,loop,encryption,fsck,nonempty,allow_root,allow_other,sec" />
 <!--
 <mntoptions deny="suid,dev" />
 <mntoptions allow="*" />
diff --git a/configure.ac b/configure.ac
index 7ebda70..5232bd8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -61,6 +61,37 @@ PKG_CHECK_MODULES([libcrypto], [libcrypto >= 0.9.8],
 	[AC_DEFINE_UNQUOTED([HAVE_LIBCRYPTO], [1],
 	[OpenSSL libcrypto available])])
 
+#### Avahi support (optional) ####
+
+AC_ARG_ENABLE([avahi],
+    AS_HELP_STRING([--disable-avahi],[Disable optional Avahi support]),
+        [
+            case "${enableval}" in
+                yes) avahi=yes ;;
+                no) avahi=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-avahi) ;;
+            esac
+        ],
+        [avahi=auto])
+
+if test "x${avahi}" != xno ; then
+    PKG_CHECK_MODULES(AVAHI, [ avahi-client >= 0.6.0 ],
+        HAVE_AVAHI=1,
+        [
+                HAVE_AVAHI=0
+                if test "x$avahi" = xyes ; then
+                        AC_MSG_ERROR([*** Avahi support not found])
+                fi
+        ])
+else
+    HAVE_AVAHI=0
+fi
+
+AC_SUBST(AVAHI_CFLAGS)
+AC_SUBST(AVAHI_LIBS)
+AC_SUBST(HAVE_AVAHI)
+AM_CONDITIONAL([HAVE_AVAHI], [test "x$HAVE_AVAHI" = x1])
+
 AC_CHECK_HEADER([security/pam_modules.h], [have_pamheader="yes"])
 # Mac OS X 10.3 puts PAM headers in /usr/include/pam.
 AC_CHECK_HEADER([pam/pam_modules.h], [have_pamheader="yes"])
diff --git a/src/Makefile.am b/src/Makefile.am
index 1d5e030..f3eaaeb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,6 +23,12 @@ pam_mount_la_LIBADD	= -lpam ${libHX_LIBS} ${libcrypto_LIBS} \
 			  ${libxml_LIBS}
 pam_mount_la_LDFLAGS	= -module -avoid-version
 
+if HAVE_AVAHI
+pam_mount_la_SOURCES	+= pm_dnssd.c
+pam_mount_la_CFLAGS	+= -DHAVE_AVAHI $(AVAHI_CFLAGS)
+pam_mount_la_LIBADD	+= $(AVAHI_LIBS)
+endif
+
 autoloop_SOURCES	= autoloop.c ${loop_sources} log.c misc.c spawn.c
 autoloop_LDADD		= ${libHX_LIBS} ${libcrypto_LIBS}
 
@@ -47,7 +53,7 @@ pmt_ofl_LDADD		= ${libHX_LIBS}
 pmvarrun_SOURCES = pmvarrun.c log.c
 pmvarrun_LDADD   = ${libHX_LIBS}
 
-EXTRA_DIST = misc.h mount.h pam_mount.h readconfig.h spawn.h
+EXTRA_DIST = misc.h mount.h pam_mount.h readconfig.h spawn.h pm_dnssd.h
 
 umount.crypt${EXEEXT}: mount.crypt${EXEEXT}
 	-${LN_S} $^ $@;
diff --git a/src/mount.c b/src/mount.c
index 183201a..ac6d2ea 100644
--- a/src/mount.c
+++ b/src/mount.c
@@ -207,13 +207,24 @@ static bool mkmountpoint_real(struct vol *const volume, const char *const d)
 	 * it, so there is no need to use S_IRWXUGO or S_IRWXU | S_IXUGO here.
 	 *
 	 * Workaround for CIFS on root_squashed NFS: +S_IXUGO
+     *
+     * To much debug for aufs.
 	 */
 	if (mkdir(d, S_IRWXU | S_IXUGO) < 0) {
+        l0g("Can't create %s directory. Error: [%d], %s.\n", d, errno, strerror(errno));
 		ret = false;
 		goto out;
-	}
+	} else {
+        l0g("Directory `%s' created.\n", d);
+    }
+
 	if (chown(d, passwd_ent->pw_uid, passwd_ent->pw_gid) < 0) {
-		l0g("could not chown %s to %s\n", d, volume->user);
+		l0g("could not chown %s to %s (UID: %d, GID: %d) Error: [%d], %s.\n",
+                d, volume->user, passwd_ent->pw_uid, passwd_ent->pw_gid, errno, strerror(errno));
+        if (rmdir(d) == 0)
+            l0g("`%s' removed, because can't change owner.\n",d);
+        else
+            l0g("Can't remove `%s'. Error: [%d], %s\n", d, errno, strerror(errno));
 		ret = false;
 		goto out;
 	}
@@ -247,10 +258,20 @@ static bool mkmountpoint_pick(struct vol *volume, const char *d)
 
 	w4rn("creating mount point %s\n", d);
 	if (seteuid(pe->pw_uid) == 0)
+    {
+		l0g("Successful seteuid to %d\n", pe->pw_uid);
 		if (mkmountpoint_real(volume, d))
 			return true;
+    } else {
+		l0g("Can't seteuid to %d\n", pe->pw_uid);
+    }
+
+
+	if (seteuid(0) == 0)
+		l0g("Successful seteuid to 0\n");
+    else
+        l0g("Can't seteuid to 0\n");
 
-	seteuid(0);
 	ret = mkmountpoint_real(volume, d);
 	if (!ret)
 		l0g("tried to create %s but failed\n", d);
diff --git a/src/pam_mount.h b/src/pam_mount.h
index 79cbaa9..3f95788 100644
--- a/src/pam_mount.h
+++ b/src/pam_mount.h
@@ -102,6 +102,14 @@ struct vol {
 	bool use_fstab;
 	bool uses_ssh;
 	bool noroot;
+#ifdef HAVE_AVAHI
+    /**
+     * dns service discovery can recive several ansvers from network
+     */
+    bool dnssd_lookup;
+    bool dnssd_cacheonly;    /** optional, default 0 (query network) */
+    char *dnssd_key;         /** optional, default NULL, if defined mount only shares(services) with "txt" key="dnssd_key" */
+#endif
 };
 
 /**
diff --git a/src/pm_dnssd.c b/src/pm_dnssd.c
new file mode 100644
index 0000000..d8c0143
--- /dev/null
+++ b/src/pm_dnssd.c
@@ -0,0 +1,590 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/error.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/llist.h>
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+
+#include "pm_dnssd.h"
+
+typedef struct ServiceInfo ServiceInfo;
+
+/** service is "_XXX._XXX" */
+struct ServiceInfo {
+
+    /** Numeric network interface index. Takes OS dependent values and the special constant AVAHI_IF_UNSPEC  */
+    AvahiIfIndex interface;
+
+    /** Protocol family specification, takes the values AVAHI_PROTO_INET, AVAHI_PROTO_INET6, AVAHI_PROTO_UNSPEC */
+    AvahiProtocol protocol;
+
+    /** Unique fields for indentify service */
+    char *name, *type;
+    char domain[AVAHI_DOMAIN_NAME_MAX];
+
+    /** Fill up after resolver callback */
+    char address[AVAHI_ADDRESS_STR_MAX];
+    char *hostname;
+    uint16_t port;
+    AvahiStringList *txt;
+
+    /** resolver for service
+     * before callback - point to resolver
+     * after - point to NULL */
+    AvahiServiceResolver *resolver;
+
+    int *remain_resolve;
+
+    /** The pointers in the linked list's items. Use this in the item structure */
+    /** *name##_next, *name##_prev */
+    AVAHI_LLIST_FIELDS(ServiceInfo, info);
+};
+
+typedef struct BrowserState BrowserState;
+
+struct BrowserState {
+    ServiceInfo **services;
+    const PMRequest *pm_request;
+    int browser_running;
+};
+
+static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
+
+    AvahiSimplePoll *simple_poll = userdata;
+
+    switch (state) {
+        case AVAHI_CLIENT_FAILURE:
+
+            fprintf(stderr, "DNS-SD: Client failure, exiting: %s\n", avahi_strerror(avahi_client_errno(c)));
+
+            avahi_simple_poll_quit(simple_poll);
+
+            break;
+
+        case AVAHI_CLIENT_S_RUNNING:
+        case AVAHI_CLIENT_S_REGISTERING:
+        case AVAHI_CLIENT_S_COLLISION:
+
+            break;
+
+        /**
+         * This state is only entered when AVAHI_CLIENT_NO_FAIL has been passed to avahi_client_new()
+         * and the daemon is not yet available.
+         * */
+        /* case AVAHI_CLIENT_CONNECTING:
+
+            fprintf(stderr, _("DNS-SD: Waiting for daemon ...\n"));
+            break;
+        */
+    }
+}
+
+static ServiceInfo *add_service(ServiceInfo *services,
+        AvahiIfIndex interface,
+        AvahiProtocol protocol,
+        const char *name,
+        const char *type,
+        const char *domain) {
+    ServiceInfo *i;
+
+    i = avahi_new(ServiceInfo, 1);
+    memset(i, 0, sizeof(ServiceInfo));
+
+    i->interface = interface;
+    i->protocol = protocol;
+    i->name = avahi_strdup(name);
+    i->type = avahi_strdup(type);
+    snprintf(i->domain, AVAHI_DOMAIN_NAME_MAX, "%s", domain);
+
+    AVAHI_LLIST_PREPEND(ServiceInfo, info, services, i);
+
+    return i;
+}
+
+static ServiceInfo *find_service(ServiceInfo *services,
+        AvahiIfIndex interface,
+        AvahiProtocol protocol,
+        const char *name,
+        const char *type,
+        const char *domain) {
+
+    ServiceInfo *i;
+
+    for (i = services; i; i = i->info_next)
+        if (i->interface == interface &&
+            i->protocol == protocol &&
+            strcasecmp(i->name, name) == 0 &&
+            avahi_domain_equal(i->type, type) &&
+            avahi_domain_equal(i->domain, domain))
+
+            return i;
+
+    return NULL;
+}
+
+/**
+ * One callback browser for all services types
+ */
+static void service_browser_callback(
+    AvahiServiceBrowser *b,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiBrowserEvent event,
+    const char *name,
+    const char *type,
+    const char *domain,
+    AvahiLookupResultFlags flags,
+    void *userdata) {
+
+    BrowserState *s = userdata;
+    ServiceInfo *found_services;
+
+    assert(b != NULL);
+    assert(s != NULL);
+
+    found_services = *s->services;
+
+    switch (event) {
+        /* The object is new on the network */
+        case AVAHI_BROWSER_NEW: {
+            // Example: service 'umka' of type '_smb._tcp' in domain 'local'
+            // LEAVE FOR DEBUG fprintf(stderr, "DNS-SD: (Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
+
+            if (s->pm_request->ignore_local && (flags & AVAHI_LOOKUP_RESULT_LOCAL))
+                break;
+
+            if (find_service(found_services, interface, protocol, name, type, domain))
+                return;
+
+            *s->services = add_service(found_services, interface, protocol, name, type, domain);
+
+            break;
+        }
+
+        /**
+         * The object has been removed from the network
+         * Suppose time to recive AVAHI_BROWSER_ALL_FOR_NOW will be to short so ignore AVAHI_BROWSER_REMOVE
+         */
+        case AVAHI_BROWSER_REMOVE:
+            fprintf(stderr, "DNS-SD: (Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
+            break;
+
+        /* Browsing failed due to some reason */
+        case AVAHI_BROWSER_FAILURE:
+            fprintf(stderr, "DNS-SD: (Browser) service_browser failed: %s\n",
+                    avahi_strerror (avahi_client_errno (avahi_service_browser_get_client (b))));
+            s->browser_running = 0;
+            break;
+
+        /* One-time event, to notify the user that all entries from the caches have been sent */
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+            // LEAVE FOR DEBUG fprintf(stderr, "DNS-SD: (Browser) CACHE_EXHAUSTED\n");
+            if (s->pm_request->terminate_on_cache_exhausted)
+                s->browser_running = 0;
+            break;
+
+        /* One-time event, to notify the user that more records will probably not show up in the near future,
+             i.e. all cache entries have been read and all static servers been queried */
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+            // LEAVE FOR DEBUG fprintf(stderr, "DNS-SD: (Browser) ALL_FOR_NOW\n");
+            s->browser_running = 0;
+            break;
+    }
+}
+
+static void service_resolver_callback(
+    AvahiServiceResolver *r,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiResolverEvent event,
+    const char *name,
+    const char *type,
+    const char *domain,
+    const char *host_name,
+    const AvahiAddress *a,
+    uint16_t port,
+    AvahiStringList *txt,
+    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+    void *userdata) {
+
+    ServiceInfo *i = userdata;
+
+    assert(r);
+    assert(i);
+
+    switch (event) {
+        case AVAHI_RESOLVER_FOUND:
+
+            // XXX: print_service_line(i->config, '=', interface, protocol, name, type, domain, 0);
+
+            i->hostname = avahi_strdup(host_name);
+            // i->address = avahi_malloc(sizeof(char) * AVAHI_ADDRESS_STR_MAX);
+            avahi_address_snprint(i->address, AVAHI_ADDRESS_STR_MAX, a);
+            i->port = port;
+            i->txt = avahi_string_list_copy(txt);
+
+            break;
+
+        case AVAHI_RESOLVER_FAILURE:
+
+            fprintf(stderr, "DNS-SD: Failed to resolve service '%s' of type '%s' in domain '%s'\n", name, type, domain);
+            break;
+    }
+
+    avahi_service_resolver_free(i->resolver);
+    i->resolver = NULL;
+
+    assert(*i->remain_resolve > 0);
+    *i->remain_resolve = *i->remain_resolve - 1;
+}
+
+/**
+ * Found all services given type
+ */
+static ServiceInfo* pm_dnssd_find_services(const PMRequest *request)
+{
+    AvahiClient *client = NULL;
+    AvahiSimplePoll *simple_poll = NULL;
+    AvahiServiceBrowser *sb = NULL;
+    int error;
+    int remain_resolve = 0;
+    BrowserState bstate;
+    ServiceInfo *services = NULL;
+    ServiceInfo *s = NULL;
+
+    bstate.pm_request = request;
+    bstate.services = &services;
+
+    /* Allocate main loop object */
+    if (!(simple_poll = avahi_simple_poll_new())) {
+        fprintf(stderr, "DNS-SD: Failed to create simple poll object.\n");
+        goto fail;
+    }
+
+    /* Allocate a new client */
+    client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, simple_poll, &error);
+
+    /* Check wether creating the client object succeeded */
+    if (!client) {
+        fprintf(stderr, "DNS-SD: Failed to create client object: %s\n", avahi_strerror(error));
+        goto fail;
+    }
+
+    /**
+     * Create the browser for service (install watcher for desired service).
+     *
+     * It is nothing dangerous to register service browser callback,
+     * before entering to avahi_simple_poll_loop, it just return 1, if
+     * avahi_simple_poll_quit was called.
+     */
+    sb = avahi_service_browser_new(
+            client,
+            AVAHI_IF_UNSPEC,
+            AVAHI_PROTO_UNSPEC,
+            request->stype,  /** A service type such as "_http._tcp" */
+            request->domain, /** A domain to browse in. In most cases you want to pass NULL here for the def */
+            0,               /** Avahi lookup Flags */
+            service_browser_callback,
+            &bstate          /** User data */
+            );
+
+    if (!sb) {
+        fprintf(stderr, "DNS-SD: Failed to create service browser: avahi_service_browser_new() failed: %s\n",
+                avahi_strerror(avahi_client_errno(client)));
+        goto fail;
+    }
+
+    /**
+     * Wait until all service providers will be found.
+     */
+    bstate.browser_running = 1;
+    while (bstate.browser_running)
+        if (avahi_simple_poll_iterate(simple_poll, -1) != 0)
+            break;
+
+    /**
+     * At this moment service_browser_callback finished with one of:
+     * AVAHI_BROWSER_CACHE_EXHAUSTED
+     * AVAHI_BROWSER_ALL_FOR_NOW
+     * there was an error
+     */
+
+    /**
+     * Destroy service browser object
+     */
+    if(sb)
+        avahi_service_browser_free(sb);
+
+    /**
+     * Go thru all founded services
+     * Fetch information about each founded service
+     */
+    for(s=services; s; s = s->info_next) {
+        s->resolver = avahi_service_resolver_new(client,
+                s->interface,
+                s->protocol,
+                s->name,
+                s->type,
+                s->domain,
+                AVAHI_PROTO_UNSPEC,
+                0,
+                service_resolver_callback,
+                s);
+
+        if ( ! s->resolver ) {
+            fprintf(stderr, "DNS-SD: Failed to resolve service '%s' of type '%s' in domain '%s': %s\n",
+                    s->name, s->type, s->domain, avahi_strerror(avahi_client_errno(client)));
+            continue;
+        }
+        s->remain_resolve = &remain_resolve;
+        remain_resolve++;
+    }
+
+    /**
+     * Wait until last service_resolver_callback will be handled
+     */
+    while (remain_resolve > 0)
+        if (avahi_simple_poll_iterate(simple_poll, -1) != 0)
+            break;
+
+fail:
+    if (client)
+        avahi_client_free(client);
+
+    if (simple_poll)
+        avahi_simple_poll_free(simple_poll);
+
+    return services;
+}
+
+static void destroy_service(ServiceInfo *s)
+{
+    assert(s);
+
+    avahi_free(s->name);
+    avahi_free(s->type);
+    avahi_free(s->hostname);
+    if ( s->resolver != NULL )
+    {
+        fprintf(stderr, "DNS-SD: Destroy unresolved service '%s' of type '%s' in domain '%s'\n",
+                    s->name, s->type, s->domain);
+        avahi_service_resolver_free(s->resolver);
+    }
+    avahi_string_list_free(s->txt);
+
+    avahi_free(s);
+}
+
+void dnssd_share_list_free(dnssd_share_t *s) {
+
+    dnssd_share_t * share;
+
+    share = s;
+
+    while (share) {
+        free(s->hostname);
+        free(s->volume);
+        free(s->sname);
+        share = s->next;
+        free(s);
+        s = share;
+    }
+
+    return;
+}
+
+static char *get_hostname(const char *name, const char *address)
+{
+    struct addrinfo hints, *res = NULL;
+    char *fqdn = NULL, *res_addr;
+    int gai_err;
+
+    memset(&hints, 0, sizeof(struct addrinfo));
+    hints.ai_family = AF_INET;
+    hints.ai_flags = AI_CANONNAME;
+
+    gai_err = getaddrinfo(name, NULL, &hints, &res);
+    if(gai_err != 0) {
+        fprintf(stderr, "DNS-SD: Unable to obtain FQDN for %s: %s\n", name, gai_strerror(gai_err));
+        return NULL;
+    }
+    if (res) {
+        res_addr = inet_ntoa(((struct sockaddr_in *)res->ai_addr)->sin_addr);
+        if(res->ai_canonname && res_addr && strcmp (address, res_addr) == 0)
+            fqdn = strdup(res->ai_canonname);
+        freeaddrinfo(res);
+    }
+
+    return fqdn;
+}
+
+/**
+ * If key == NULL, then return all found services given type
+ */
+dnssd_share_t * dnssd_lookup(const PMRequest * pm_request, const char * key) {
+    ServiceInfo *s, *head;
+
+    dnssd_share_t *share = NULL, *tail = NULL;
+    AvahiStringList * txt;
+
+    char *ignore = NULL;
+    char *path = NULL;
+    char *hostname = NULL, *p;
+    char tmp[256]={'\0'};
+
+    int err = 0;
+
+    s = pm_dnssd_find_services(pm_request);
+
+    head = s;
+
+    while(s) {
+        /* Ignore services without required key, example: LOGON_MOUNT */
+        if (key && ! avahi_string_list_find(s->txt, key))
+        {
+            s = s->info_next;
+            continue;
+        }
+
+        /* Find path in TXT section */
+        txt = avahi_string_list_find(s->txt, "path");
+
+        if (txt == NULL) {
+            fprintf(stderr, "DNS-SD: Can't find `path' in TXT section in service '%s'\n", s->name);
+            s = s->info_next;
+            continue;
+        }
+
+        avahi_string_list_get_pair(txt, &ignore, &path, NULL);
+        avahi_free(ignore);
+
+        share = calloc (1, sizeof(dnssd_share_t));
+        if (share == NULL)
+        {
+            err = 1;
+            goto out;
+        }
+        share->next = tail;
+
+        // Copy volume (path)
+        share->volume = calloc(strlen(path)+1, sizeof(char));
+        if (share->volume == NULL)
+        {
+            avahi_free(path);
+            err = 1;
+            goto out;
+        }
+        strcpy(share->volume, path);
+        avahi_free(path);
+
+        // Copy hostname
+        strncpy (tmp, s->hostname, sizeof(tmp));
+        p = strrchr (tmp, '.');
+        if (p)
+        {
+            *p = '\0';
+            if (strcmp ("local", ++p) == 0)
+                hostname = get_hostname(tmp, s->address);
+        }
+
+        if (hostname != NULL)
+            share->hostname = hostname;
+        else
+        {
+            share->hostname = calloc(strlen(s->hostname)+1, sizeof(char));
+            if (share->hostname == NULL)
+            {
+                err = 1;
+                goto out;
+            }
+            strcpy(share->hostname, s->hostname);
+        }
+        // Copy service name
+        share->sname = calloc(strlen(s->name)+1, sizeof(char));
+        if (share->sname == NULL)
+        {
+            err = 1;
+            goto out;
+        }
+        strcpy(share->sname, s->name);
+
+        tail = share;
+
+        s = s->info_next;
+    }
+
+out:
+    // Free memory for all found services
+    s = head;
+    while(head) {
+        AVAHI_LLIST_REMOVE(ServiceInfo, info, head, s);
+        destroy_service(s);
+        s = head;
+    }
+
+    if(err) {
+        dnssd_share_list_free(share);
+        return NULL;
+    }
+
+    return share;
+}
+
+/**
+ * FOR TEST SUIT PURPOSES
+ */
+//#define DNSSD_TEST 1
+#ifdef DNSSD_TEST
+int main(int argc, char **argv)
+{
+    PMRequest pm_request;
+    ServiceInfo *s, *sd;
+    AvahiStringList * sl;
+
+    dnssd_share_t *shares, *head;
+
+    pm_request.terminate_on_cache_exhausted = 0;
+    pm_request.ignore_local = 1;
+    pm_request.domain = NULL;
+    pm_request.stype = "_cifs._tcp";
+
+    sd = s = pm_dnssd_find_services(&pm_request);
+
+    while(s) {
+        fprintf(stderr, "DNS-SD: Found service with type: %s --- name: %s, provided by: %s, in domain: %s, address: %s, port: %d\n",
+                s->type,
+                s->name,
+                s->hostname,
+                s->domain,
+                s->address,
+                s->port);
+        AVAHI_LLIST_REMOVE(ServiceInfo, info, sd, s);
+        destroy_service(s);
+        s = sd;
+    }
+
+    shares = dnssd_lookup(&pm_request, "path");
+    head = shares;
+
+    while (shares) {
+        fprintf(stderr, "DNS-SD: Service name: %s, server: %s, volume: %s\n",
+                shares->sname,
+                shares->hostname,
+                shares->volume);
+
+        shares = shares->next;
+    }
+
+    dnssd_share_list_free(head);
+
+    return 0;
+}
+#endif
diff --git a/src/pm_dnssd.h b/src/pm_dnssd.h
new file mode 100644
index 0000000..74707d6
--- /dev/null
+++ b/src/pm_dnssd.h
@@ -0,0 +1,36 @@
+#ifndef PM_DNSSD_H
+#define PM_DNSSD_H 1
+
+/**
+ * Answer from DNS-SD
+ * one direction list of found services
+ */
+typedef struct dnssd_share_t dnssd_share_t;
+
+struct dnssd_share_t {
+    char *hostname; // server
+    char *volume; // path to share on server
+    char *sname;  // service name (unique in DNS-SD network)
+
+    struct dnssd_share_t *next;
+};
+
+/** Request from pam_mount */
+typedef struct PMRequest PMRequest;
+
+struct PMRequest {
+    int terminate_on_cache_exhausted;
+    int ignore_local;
+    const char *domain; /** In most cases you want to pass NULL here for the default domain */
+    const char *stype;  /** Example: _smb._tcp for CIFS */
+};
+
+/**
+ * Return list of found anonced services in DNS-SD (Avahi, Zeroconf, etc..)
+ * List MUST be free with dnssd_share_list_free()
+ */
+extern dnssd_share_t * dnssd_lookup(const PMRequest *, const char * key);
+
+extern void dnssd_share_list_free(dnssd_share_t *s);
+
+#endif /* PM_DNSSD_H */
diff --git a/src/rdconf1.c b/src/rdconf1.c
index 78d4438..784dfff 100644
--- a/src/rdconf1.c
+++ b/src/rdconf1.c
@@ -28,6 +28,10 @@
 #include <libHX/libxml_helper.h>
 #include "pam_mount.h"
 
+#ifdef HAVE_AVAHI
+#include "pm_dnssd.h"
+#endif
+
 /* Definitions */
 enum {
 	CONTEXT_GLOBAL = 0,
@@ -1189,6 +1193,139 @@ static int rc_volume_cond(const char *user, xmlNode *node)
 	return true;
 }
 
+static bool copy_options_list(struct HXclist_head *dest_list, const struct HXclist_head *src_list)
+{
+	const struct kvp *src_kvp;
+	struct kvp *dest_kvp;
+
+	HXlist_for_each_entry(src_kvp, src_list, list) {
+		dest_kvp = xmalloc(sizeof(struct kvp));
+		if (dest_kvp == NULL)
+			return false;
+		dest_kvp->key = dest_kvp->value = NULL;
+		HXlist_init(&dest_kvp->list);
+		dest_kvp->key = xstrdup(src_kvp->key);
+		if(dest_kvp->key == NULL)
+			goto err;
+		if(src_kvp->value != NULL) {
+			dest_kvp->value = xstrdup(src_kvp->value);
+			if (dest_kvp->value == NULL)
+				goto err;
+		}
+		HXclist_push(dest_list, &dest_kvp->list);
+	}
+
+	return true;
+err:
+	free(dest_kvp->key);
+	free(dest_kvp->value);
+	free(dest_kvp);
+
+	return false;
+}
+
+#ifdef HAVE_AVAHI
+/**
+ * Expand dns-sd volume to several generic PAM Mount volumes
+ * Requires: mkmountpoint MUST be enabled, or destenation mountpoint:
+ * "mountpoint/service" can't be created.
+ */
+static void dnssd_unzip_volume(struct vol *dnssd_vol, struct config *config)
+{
+	/* Ignore incorrect resolved resources */
+	struct vol *vpt;
+	PMRequest dreq;    // DNS-SD request
+	dnssd_share_t *s, *head;    // List of resolved services
+	unsigned int i;
+
+	if (strcasecmp(dnssd_vol->fstype, "cifs") == 0) {
+		dreq.stype = "_smb._tcp";
+	}
+	else
+	{
+		w4rn("DNS-SD: Unsopported file system: %s\n", dnssd_vol->fstype);
+		return;
+	}
+
+	dreq.terminate_on_cache_exhausted = dnssd_vol->dnssd_cacheonly;
+	dreq.ignore_local = 0;
+	dreq.domain = NULL;
+
+	head = s = dnssd_lookup(&dreq, dnssd_vol->dnssd_key);
+
+	while(s) {
+		vpt = calloc(1, sizeof(struct vol));
+		if (vpt == NULL)
+			return;
+
+		HXlist_init(&vpt->list);
+		HXclist_push(&config->volume_list, &vpt->list);
+
+		vpt->globalconf = config->level == CONTEXT_GLOBAL;
+		vpt->user = config->user;
+		vpt->type = CMD_LCLMOUNT;
+		for (i = 0; default_command[i].type != -1; ++i) {
+			const struct pmt_command *c = &default_command[i];
+			if (c->fs != NULL && strcasecmp(dnssd_vol->fstype, c->fs) == 0) {
+				vpt->type = c->type;
+				break;
+			}
+		}
+		HXclist_init(&vpt->options);
+		if (!copy_options_list(&vpt->options, &dnssd_vol->options))
+			goto err;
+
+		vpt->noroot =  dnssd_vol->noroot;
+		// Copy fstype
+		vpt->fstype = calloc(strlen(dnssd_vol->fstype)+1, sizeof(char));
+		if (vpt->fstype == NULL)
+			goto err;
+		strcpy(vpt->fstype, dnssd_vol->fstype);
+
+		// Copy server (hostname)
+		vpt->server = calloc(strlen(s->hostname)+1, sizeof(char));
+		if (vpt->server == NULL)
+			goto err;
+		strcpy(vpt->server, s->hostname);
+
+		// Copy volume
+		vpt->volume = calloc(strlen(s->volume)+1, sizeof(char));
+		if (vpt->volume== NULL)
+			goto err;
+		if (s->volume[0] == '/')
+			strcpy(vpt->volume, &s->volume[1]);
+		else
+			strcpy(vpt->volume, s->volume);
+
+		// Construct mount point
+		// Service name is unique in DNS-SD domain, so use service name instead "path" (share name)
+		// propagated via DNS-SD.
+		vpt->mountpoint = calloc(strlen(s->sname) + strlen(dnssd_vol->mountpoint) + 2, sizeof(char));
+		if (vpt->mountpoint == NULL)
+			goto err;
+		strcpy(vpt->mountpoint, dnssd_vol->mountpoint);
+		strcat(vpt->mountpoint, "/");
+		strcat(vpt->mountpoint, s->sname);
+
+		l0g("DNS-SD: Added new %s volume. Server: %s, share: %s, mntpoint: %s\n",
+				vpt->fstype,
+				vpt->server,
+				vpt->volume,
+				vpt->mountpoint);
+
+		s = s->next;
+	}
+
+	dnssd_share_list_free(head);
+	return;
+
+err:
+	dnssd_share_list_free(head);
+	volume_free(vpt);
+	return;
+}
+#endif
+
 static const char *rc_volume(xmlNode *node, struct config *config,
     unsigned int command)
 {
@@ -1251,13 +1388,33 @@ static const char *rc_volume(xmlNode *node, struct config *config,
 		vpt->volume = tmp;
 	}
 
+#ifdef HAVE_AVAHI
+	/* DNS Service Discovery (avahi, zeroconf, ...) */
+	if ((tmp = xml_getprop(node, "dnssd_lookup")) != NULL) {
+		vpt->dnssd_lookup = parse_bool_f(tmp);
+		if(vpt->dnssd_lookup) {
+			l0g("DNS-SD: found dns-sd volume in pam_mount config, with %s fs\n", vpt->fstype);
+			if ((tmp = xml_getprop(node, "dnssd_cache_only")) != NULL) {
+				vpt->dnssd_cacheonly = parse_bool_f(tmp);
+			} else {
+				vpt->dnssd_cacheonly = 0;
+			}
+			if ((tmp = xml_getprop(node, "dnssd_key")) != NULL) {
+				free(vpt->dnssd_key);
+				vpt->dnssd_key = tmp;
+			}
+		}
+	}
+#endif
+
 	/* Destination */
 	if ((tmp = xml_getprop(node, "mountpoint")) != NULL) {
 		free(vpt->mountpoint);
 		vpt->mountpoint = tmp;
 	} else {
 		free(vpt->mountpoint);
-		vpt->mountpoint = fstab_value(vpt->volume, FSTAB_MNTPT);
+		if (vpt->volume != NULL)
+			vpt->mountpoint = fstab_value(vpt->volume, FSTAB_MNTPT);
 		if (vpt->mountpoint == NULL) {
 			err = "could not determine mountpoint";
 			goto out;
@@ -1310,6 +1467,17 @@ static const char *rc_volume(xmlNode *node, struct config *config,
 		vpt->fs_key_path = tmp;
 	}
 
+#ifdef HAVE_AVAHI
+	/* DNS Service Discovery: unzip dns-sd volume to several generic volumes */
+	if(vpt->dnssd_lookup) {
+		dnssd_unzip_volume(vpt, config);
+
+		/* Delete pattern dns-sd volume */
+		HXclist_del(&config->volume_list, &vpt->list);
+		volume_free(vpt);
+	}
+#endif
+
 	return NULL;
 
  out:
@@ -1318,6 +1486,7 @@ static const char *rc_volume(xmlNode *node, struct config *config,
 	return err;
 }
 
+
 //-----------------------------------------------------------------------------
 static const struct pmt_command default_command[] = {
 	{CMD_SMBMOUNT,   "smbfs", "smbmount",   {"smbmount", "//%(SERVER)/%(VOLUME)", "%(MNTPT)", "-o", "username=%(USER),uid=%(USERUID),gid=%(USERGID)%(before=\",\" OPTIONS)", NULL}},
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin