Send connman mailing list submissions to
        [email protected]

To subscribe or unsubscribe via the World Wide Web, visit
        https://lists.01.org/mailman/listinfo/connman
or, via email, send a message with subject or body 'help' to
        [email protected]

You can reach the person managing the list at
        [email protected]

When replying, please edit your Subject line so it is more specific
than "Re: Contents of connman digest..."


Today's Topics:

   1. [PATCH 1/5] dnsproxy: move dns module prototypes to a
      separate file. (Ismo Puustinen)
   2. [PATCH 2/5] dns: add new functions to the API. (Ismo Puustinen)
   3. [PATCH 3/5] service: add mDNS support. (Ismo Puustinen)
   4. [PATCH 4/5] config: support mDNS in service configuration
      files. (Ismo Puustinen)
   5. [PATCH 5/5] connmanctl: add mDNS support. (Ismo Puustinen)


----------------------------------------------------------------------

Message: 1
Date: Thu, 12 Oct 2017 17:43:15 +0300
From: Ismo Puustinen <[email protected]>
To: [email protected]
Subject: [PATCH 1/5] dnsproxy: move dns module prototypes to a
        separate file.
Message-ID: <[email protected]>

---
 include/dnsproxy.h         | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 src/connman.h              |  7 -------
 src/dns-systemd-resolved.c |  1 +
 src/dnsproxy.c             |  1 +
 src/resolver.c             |  1 +
 src/tethering.c            |  1 +
 6 files changed, 50 insertions(+), 7 deletions(-)
 create mode 100644 include/dnsproxy.h

diff --git a/include/dnsproxy.h b/include/dnsproxy.h
new file mode 100644
index 00000000..40971b96
--- /dev/null
+++ b/include/dnsproxy.h
@@ -0,0 +1,46 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2017  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_DNSPROXY_H
+#define __CONNMAN_DNSPROXY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:dnsproxy
+ * @title: dnsproxy premitives
+ * @short_description: Functions for registering dnsproxy modules
+ */
+
+int __connman_dnsproxy_init(void);
+void __connman_dnsproxy_cleanup(void);
+int __connman_dnsproxy_add_listener(int index);
+void __connman_dnsproxy_remove_listener(int index);
+int __connman_dnsproxy_append(int index, const char *domain, const char 
*server);
+int __connman_dnsproxy_remove(int index, const char *domain, const char 
*server);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_DNSPROXY_H */
diff --git a/src/connman.h b/src/connman.h
index da4446a4..fe7e1b0d 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -946,13 +946,6 @@ int __connman_iptables_init(void);
 void __connman_iptables_cleanup(void);
 int __connman_iptables_commit(const char *table_name);
 
-int __connman_dnsproxy_init(void);
-void __connman_dnsproxy_cleanup(void);
-int __connman_dnsproxy_add_listener(int index);
-void __connman_dnsproxy_remove_listener(int index);
-int __connman_dnsproxy_append(int index, const char *domain, const char 
*server);
-int __connman_dnsproxy_remove(int index, const char *domain, const char 
*server);
-
 int __connman_6to4_probe(struct connman_service *service);
 void __connman_6to4_remove(struct connman_ipconfig *ipconfig);
 int __connman_6to4_check(struct connman_ipconfig *ipconfig);
diff --git a/src/dns-systemd-resolved.c b/src/dns-systemd-resolved.c
index 4e9b2ff1..0ab24e8d 100644
--- a/src/dns-systemd-resolved.c
+++ b/src/dns-systemd-resolved.c
@@ -32,6 +32,7 @@
 #include <connman/dbus.h>
 
 #include "connman.h"
+#include "dnsproxy.h"
 
 #define SYSTEMD_RESOLVED_SERVICE "org.freedesktop.resolve1"
 #define SYSTEMD_RESOLVED_PATH "/org/freedesktop/resolve1"
diff --git a/src/dnsproxy.c b/src/dnsproxy.c
index 8b2827a5..efc582fa 100644
--- a/src/dnsproxy.c
+++ b/src/dnsproxy.c
@@ -40,6 +40,7 @@
 #include <glib.h>
 
 #include "connman.h"
+#include "dnsproxy.h"
 
 #define debug(fmt...) do { } while (0)
 
diff --git a/src/resolver.c b/src/resolver.c
index 75ea5ba6..e8157e8c 100644
--- a/src/resolver.c
+++ b/src/resolver.c
@@ -34,6 +34,7 @@
 #include <netdb.h>
 
 #include "connman.h"
+#include "dnsproxy.h"
 
 #define RESOLV_CONF_STATEDIR STATEDIR"/resolv.conf"
 #define RESOLV_CONF_ETC "/etc/resolv.conf"
diff --git a/src/tethering.c b/src/tethering.c
index c929ba71..28620567 100644
--- a/src/tethering.c
+++ b/src/tethering.c
@@ -39,6 +39,7 @@
 #include <linux/if_bridge.h>
 
 #include "connman.h"
+#include "dnsproxy.h"
 
 #include <gdhcp/gdhcp.h>
 
-- 
2.13.6



------------------------------

Message: 2
Date: Thu, 12 Oct 2017 17:43:16 +0300
From: Ismo Puustinen <[email protected]>
To: [email protected]
Subject: [PATCH 2/5] dns: add new functions to the API.
Message-ID: <[email protected]>

Add two new functions to dnsproxy API.

  enum dnsproxy_capabilities __connman_dnsproxy_get_capabilities()

    Returns flags telling the capabilities which the DNS backend has.
    For example, if DNS backend supports MDNS, DNSPROXY_CAP_MDNS bit is
    on in the return value.

  int __connman_dnsproxy_set_mdns(int index, bool enabled);

    Turn mDNS support for the interface with specified interface index
    on or off.
---
 include/dnsproxy.h         |  8 ++++++++
 src/dns-systemd-resolved.c | 41 +++++++++++++++++++++++++++++++++++++++++
 src/dnsproxy.c             | 10 ++++++++++
 3 files changed, 59 insertions(+)

diff --git a/include/dnsproxy.h b/include/dnsproxy.h
index 40971b96..87d7e824 100644
--- a/include/dnsproxy.h
+++ b/include/dnsproxy.h
@@ -26,6 +26,12 @@
 extern "C" {
 #endif
 
+enum dnsproxy_capabilities {
+        DNSPROXY_CAP_SERVER_LO  = (1<<0), /* bind DNS server to loopback if */
+        DNSPROXY_CAP_SERVER_EXT = (1<<1), /* bind DNS server to external ifs */
+        DNSPROXY_CAP_MDNS       = (1<<2), /* support mDNS */
+};
+
 /**
  * SECTION:dnsproxy
  * @title: dnsproxy premitives
@@ -38,6 +44,8 @@ int __connman_dnsproxy_add_listener(int index);
 void __connman_dnsproxy_remove_listener(int index);
 int __connman_dnsproxy_append(int index, const char *domain, const char 
*server);
 int __connman_dnsproxy_remove(int index, const char *domain, const char 
*server);
+enum dnsproxy_capabilities __connman_dnsproxy_get_capabilities();
+int __connman_dnsproxy_set_mdns(int index, bool enabled);
 
 #ifdef __cplusplus
 }
diff --git a/src/dns-systemd-resolved.c b/src/dns-systemd-resolved.c
index 0ab24e8d..16ec3b73 100644
--- a/src/dns-systemd-resolved.c
+++ b/src/dns-systemd-resolved.c
@@ -37,6 +37,11 @@
 #define SYSTEMD_RESOLVED_SERVICE "org.freedesktop.resolve1"
 #define SYSTEMD_RESOLVED_PATH "/org/freedesktop/resolve1"
 
+struct mdns_data {
+       int index;
+       bool enabled;
+};
+
 static GHashTable *interface_hash;
 static DBusConnection *connection;
 static GDBusClient *client;
@@ -400,6 +405,41 @@ static int setup_resolved(void)
        return 0;
 }
 
+enum dnsproxy_capabilities __connman_dnsproxy_get_capabilities()
+{
+       return DNSPROXY_CAP_SERVER_LO | DNSPROXY_CAP_MDNS;
+}
+
+static void setlinkmulticastdns_append(DBusMessageIter *iter, void *user_data) 
{
+       struct mdns_data *data = user_data;
+       char *val = "no";
+
+       if (data->enabled)
+               val = "yes";
+
+       DBG("SetLinkMulticastDNS: %d/%s", data->index, val);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &data->index);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &val);
+}
+
+int __connman_dnsproxy_set_mdns(int index, bool enabled)
+{
+       struct mdns_data data = { .index = index, .enabled = enabled };
+
+       if (!resolved_proxy)
+               return -ENOENT;
+
+       if (index < 0)
+               return -EINVAL;
+
+       if (!g_dbus_proxy_method_call(resolved_proxy, "SetLinkMulticastDNS",
+                       setlinkmulticastdns_append, NULL, &data, NULL))
+               return -EINVAL;
+
+       return 0;
+}
+
 int __connman_dnsproxy_init(void)
 {
        int ret;
@@ -454,3 +494,4 @@ void __connman_dnsproxy_cleanup(void)
                connection = NULL;
        }
 }
+
diff --git a/src/dnsproxy.c b/src/dnsproxy.c
index efc582fa..529020c0 100644
--- a/src/dnsproxy.c
+++ b/src/dnsproxy.c
@@ -3948,6 +3948,16 @@ destroy:
        return err;
 }
 
+enum dnsproxy_capabilities __connman_dnsproxy_get_capabilities()
+{
+       return DNSPROXY_CAP_SERVER_LO | DNSPROXY_CAP_SERVER_EXT;
+}
+
+int __connman_dnsproxy_set_mdns(int index, bool enabled)
+{
+       return -ENOTSUP;
+}
+
 void __connman_dnsproxy_cleanup(void)
 {
        DBG("");
-- 
2.13.6



------------------------------

Message: 3
Date: Thu, 12 Oct 2017 17:43:17 +0300
From: Ismo Puustinen <[email protected]>
To: [email protected]
Subject: [PATCH 3/5] service: add mDNS support.
Message-ID: <[email protected]>

Add two boolean D-Bus properties for services: mDNS [readonly] and
mDNS.Configuration [readwrite]. The mDNS support for a service is
enabled or disabled by setting true or false to mDNS.Configuration.

Not all DNS backends support mDNS. Currently only systemd-resolved does.
If a DNS backend doesn't support mDNS, the properties are not exposed
over D-Bus.
---
 doc/service-api.txt |  13 +++++++
 src/connman.h       |   2 ++
 src/service.c       | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+)

diff --git a/doc/service-api.txt b/doc/service-api.txt
index 6cdb0bb5..ac8e99ec 100644
--- a/doc/service-api.txt
+++ b/doc/service-api.txt
@@ -500,3 +500,16 @@ Properties string State [readonly]
                                Possible values are "half" and "full".
 
                                This information is not available.
+
+               bool mDNS [readonly]
+
+                       Whether or not mDNS support is enabled. Note that mDNS
+                       requires a DNS backend which supports it -- otherwise
+                       the property will not be present. Currently the only
+                       DNS backend which supports mDNS is systemd-resolved.
+
+               bool mDNS.Configuration [readwrite]
+
+                       Same values as mDNS property. The mDNS represents the
+                       actual system configuration while this allows user
+                       configuration.
diff --git a/src/connman.h b/src/connman.h
index fe7e1b0d..30cc6276 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -708,6 +708,8 @@ int __connman_service_set_ignore(struct connman_service 
*service,
                                                bool ignore);
 void __connman_service_set_search_domains(struct connman_service *service,
                                        char **domains);
+int __connman_service_set_mdns(struct connman_service *service,
+                                       bool enabled);
 
 void __connman_service_set_string(struct connman_service *service,
                                        const char *key, const char *value);
diff --git a/src/service.c b/src/service.c
index 02cd51f2..7def0c2b 100644
--- a/src/service.c
+++ b/src/service.c
@@ -35,6 +35,7 @@
 #include <connman/setting.h>
 #include <connman/agent.h>
 
+#include "dnsproxy.h"
 #include "connman.h"
 
 #define CONNECT_TIMEOUT                120
@@ -94,6 +95,8 @@ struct connman_service {
        char **nameservers_auto;
        int nameservers_timeout;
        char **domains;
+       bool mdns;
+       bool mdns_config;
        char *hostname;
        char *domainname;
        char **timeservers;
@@ -594,6 +597,9 @@ static int service_load(struct connman_service *service)
                service->pac = str;
        }
 
+       service->mdns_config = g_key_file_get_boolean(keyfile,
+                               service->identifier, "mDNS", NULL);
+
        service->hidden_service = g_key_file_get_boolean(keyfile,
                                        service->identifier, "Hidden", NULL);
 
@@ -774,6 +780,13 @@ static int service_save(struct connman_service *service)
                g_key_file_remove_key(keyfile, service->identifier,
                                                        "Proxy.URL", NULL);
 
+       if (service->mdns_config)
+               g_key_file_set_boolean(keyfile, service->identifier,
+                                                               "mDNS", TRUE);
+       else
+               g_key_file_remove_key(keyfile, service->identifier,
+                                                               "mDNS", NULL);
+
        if (service->hidden_service)
                g_key_file_set_boolean(keyfile, service->identifier, "Hidden",
                                                                        TRUE);
@@ -2075,6 +2088,53 @@ static void proxy_configuration_changed(struct 
connman_service *service)
        proxy_changed(service);
 }
 
+static void mdns_changed(struct connman_service *service)
+{
+       dbus_bool_t mdns = service->mdns;
+
+       if (!allow_property_changed(service))
+               return;
+
+       connman_dbus_property_changed_basic(service->path,
+                       CONNMAN_SERVICE_INTERFACE, "mDNS", DBUS_TYPE_BOOLEAN,
+                       &mdns);
+}
+
+static void mdns_configuration_changed(struct connman_service *service)
+{
+       dbus_bool_t mdns = service->mdns;
+
+       if (!allow_property_changed(service))
+               return;
+
+       connman_dbus_property_changed_basic(service->path,
+                       CONNMAN_SERVICE_INTERFACE, "mDNS.Configuration",
+                       DBUS_TYPE_BOOLEAN, &mdns);
+}
+
+static int set_mdns(struct connman_service *service,
+                       bool enabled)
+{
+       int result;
+       enum dnsproxy_capabilities dns_caps;
+
+       dns_caps = __connman_dnsproxy_get_capabilities();
+       if (!(dns_caps & DNSPROXY_CAP_MDNS))
+               return -ENOTSUP;
+
+       result = __connman_dnsproxy_set_mdns(
+                       __connman_service_get_index(service), enabled);
+
+       if (result == 0) {
+               if (service->mdns != enabled) {
+                       service->mdns = enabled;
+                       mdns_changed(service);
+               }
+       }
+
+       return result;
+}
+
 static void timeservers_configuration_changed(struct connman_service *service)
 {
        if (!allow_property_changed(service))
@@ -2344,6 +2404,7 @@ static void append_properties(DBusMessageIter *dict, 
dbus_bool_t limited,
        dbus_bool_t val;
        const char *str;
        GSList *list;
+       enum dnsproxy_capabilities dns_caps;
 
        str = __connman_service_type2string(service->type);
        if (str)
@@ -2452,6 +2513,20 @@ static void append_properties(DBusMessageIter *dict, 
dbus_bool_t limited,
        connman_dbus_dict_append_dict(dict, "Proxy.Configuration",
                                                append_proxyconfig, service);
 
+       /* Check if mDNS support is available. Only add mDNS properties if it
+        * is there.
+        */
+       dns_caps = __connman_dnsproxy_get_capabilities();
+       if (dns_caps & DNSPROXY_CAP_MDNS) {
+               val = service->mdns;
+               connman_dbus_dict_append_basic(dict, "mDNS", DBUS_TYPE_BOOLEAN,
+                               &val);
+
+               val = service->mdns_config;
+               connman_dbus_dict_append_basic(dict, "mDNS.Configuration",
+                               DBUS_TYPE_BOOLEAN, &val);
+       }
+
        connman_dbus_dict_append_dict(dict, "Provider",
                                                append_provider, service);
 }
@@ -3571,6 +3646,26 @@ static DBusMessage *set_property(DBusConnection *conn,
                __connman_notifier_proxy_changed(service);
 
                service_save(service);
+       } else if (g_str_equal(name, "mDNS.Configuration")) {
+               dbus_bool_t val;
+               enum dnsproxy_capabilities dns_caps;
+
+               dns_caps = __connman_dnsproxy_get_capabilities();
+
+               if (service->immutable || !(dns_caps & DNSPROXY_CAP_MDNS))
+                       return __connman_error_not_supported(msg);
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &val);
+               service->mdns_config = val;
+
+               mdns_configuration_changed(service);
+
+               set_mdns(service, service->mdns_config);
+
+               service_save(service);
        } else if (g_str_equal(name, "IPv4.Configuration") ||
                        g_str_equal(name, "IPv6.Configuration")) {
 
@@ -5273,6 +5368,12 @@ void __connman_service_set_search_domains(struct 
connman_service *service,
        searchdomain_add_all(service);
 }
 
+int __connman_service_set_mdns(struct connman_service *service,
+                       bool enabled)
+{
+       return set_mdns(service, enabled);
+}
+
 static void report_error_cb(void *user_context, bool retry,
                                                        void *user_data)
 {
@@ -6001,6 +6102,7 @@ int __connman_service_ipconfig_indicate_state(struct 
connman_service *service,
                                "Default service remains in READY state.");
                if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
                        service_rp_filter(service, true);
+               set_mdns(service, service->mdns_config);
                break;
        case CONNMAN_SERVICE_STATE_ONLINE:
                break;
-- 
2.13.6



------------------------------

Message: 4
Date: Thu, 12 Oct 2017 17:43:18 +0300
From: Ismo Puustinen <[email protected]>
To: [email protected]
Subject: [PATCH 4/5] config: support mDNS in service configuration
        files.
Message-ID: <[email protected]>

Use key "mDNS" with boolean values. True means that mDNS is enabled --
mDNS domains can be resolved and hostname is registered. False means
that all mDNS functionality for this service is disabled. Note that not
all DNS backends support mDNS: currently systemd-resolved is the only
DNS backend that works with mDNS.
---
 doc/config-format.txt | 5 +++++
 src/config.c          | 8 ++++++++
 2 files changed, 13 insertions(+)

diff --git a/doc/config-format.txt b/doc/config-format.txt
index ed3123aa..4e26eddd 100644
--- a/doc/config-format.txt
+++ b/doc/config-format.txt
@@ -63,6 +63,11 @@ Allowed fields:
 - SearchDomains: Comma separated list of DNS search domains
 - Timeservers: Comma separated list of timeservers
 - Domain: Domain name to be used
+- mDNS: Boolean value (true or false). True means that mDNS is enabled --
+  mDNS domains can be resolved and hostname is registered. False means that all
+  mDNS functionality for this service is disabled. Note that not all DNS
+  backends support mDNS: currently systemd-resolved is the only DNS backend 
with
+  mDNS.
 
 If IPv4 address is missing then DHCP is used. If IPv6 address is missing,
 then SLAAC or DHCPv6 is used.
diff --git a/src/config.c b/src/config.c
index a8c3da89..000a4fbf 100644
--- a/src/config.c
+++ b/src/config.c
@@ -72,6 +72,7 @@ struct connman_config_service {
        char *ipv6_gateway;
        char *ipv6_privacy;
        char *mac;
+       bool mdns;
        char **nameservers;
        char **search_domains;
        char **timeservers;
@@ -112,6 +113,7 @@ static bool cleanup = false;
 #define SERVICE_KEY_PASSPHRASE         "Passphrase"
 #define SERVICE_KEY_SECURITY           "Security"
 #define SERVICE_KEY_HIDDEN             "Hidden"
+#define SERVICE_KEY_MDNS               "mDNS"
 
 #define SERVICE_KEY_IPv4               "IPv4"
 #define SERVICE_KEY_IPv6               "IPv6"
@@ -152,6 +154,7 @@ static const char *service_possible_keys[] = {
        SERVICE_KEY_IPv6,
        SERVICE_KEY_IPv6_PRIVACY,
        SERVICE_KEY_MAC,
+       SERVICE_KEY_MDNS,
        SERVICE_KEY_NAMESERVERS,
        SERVICE_KEY_SEARCH_DOMAINS,
        SERVICE_KEY_TIMESERVERS,
@@ -514,6 +517,9 @@ static bool load_service_generic(GKeyFile *keyfile,
                        g_strfreev(strlist);
        }
 
+       service->mdns = __connman_config_get_bool(keyfile, group,
+                                               SERVICE_KEY_MDNS, NULL);
+
        return true;
 
 err:
@@ -1377,6 +1383,8 @@ static int try_provision_service(struct 
connman_config_service *config,
                __connman_service_set_search_domains(service,
                                                config->search_domains);
 
+       __connman_service_set_mdns(service, config->mdns);
+
        if (config->timeservers)
                __connman_service_set_timeservers(service,
                                                config->timeservers);
-- 
2.13.6



------------------------------

Message: 5
Date: Thu, 12 Oct 2017 17:43:19 +0300
From: Ismo Puustinen <[email protected]>
To: [email protected]
Subject: [PATCH 5/5] connmanctl: add mDNS support.
Message-ID: <[email protected]>

Use option 'm' with values 'yes' and 'no'.
---
 client/commands.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/client/commands.c b/client/commands.c
index 583095b1..5761ed14 100644
--- a/client/commands.c
+++ b/client/commands.c
@@ -1202,6 +1202,30 @@ static int cmd_config(char *args[], int num, struct 
connman_option *options)
                                        config_return, g_strdup(service_name),
                                        NULL, NULL);
                        break;
+
+               case 'm':
+                       switch (parse_boolean(*opt_start)) {
+                       case 1:
+                               val = TRUE;
+                               break;
+                       case 0:
+                               val = FALSE;
+                               break;
+                       default:
+                               res = -EINVAL;
+                               break;
+                       }
+                       if (res == 0) {
+                               res = __connmanctl_dbus_set_property(connection,
+                                               path, "net.connman.Service",
+                                               config_return,
+                                               g_strdup(service_name),
+                                               "mDNS.Configuration",
+                                               DBUS_TYPE_BOOLEAN, &val);
+                       }
+                       index++;
+                       break;
+
                default:
                        res = -EINVAL;
                        break;
@@ -2222,6 +2246,7 @@ static struct connman_option config_options[] = {
        {"nameservers", 'n', "<dns1> [<dns2>] [<dns3>]"},
        {"timeservers", 't', "<ntp1> [<ntp2>] [...]"},
        {"domains", 'd', "<domain1> [<domain2>] [...]"},
+       {"mdns", 'm', "yes|no"},
        {"ipv6", 'v', "off|auto [enable|disable|preferred]|\n"
                      "\t\t\tmanual <address> <prefixlength> <gateway>"},
        {"proxy", 'x', "direct|auto <URL>|manual <URL1> [<URL2>] [...]\n"
-- 
2.13.6



------------------------------

Subject: Digest Footer

_______________________________________________
connman mailing list
[email protected]
https://lists.01.org/mailman/listinfo/connman


------------------------------

End of connman Digest, Vol 24, Issue 16
***************************************

Reply via email to