For review: version 3 of IPv6 dynamic dns support



Here is the next round of my patch to add support for IPv6 dynamic dns to NetworkManager.

As before, this depends on using dhclient on the client side. As before, IPv4 support remains the same.

While this does work if method dhcp is selected, this is purely by chance. The real usage is when method auto is selected where the dhcp service supplies the IPv6 address and dynamic update of dns with RA (such as from radvd) supplying the default route.

As far as I know, there are not any "Red Hat ism" dependencies ... the patch now creates a conf file for dhclient-6. Some functions were renamed to include "ip4" in the names since there is now a "ip6" version also.

This has been tested with both radvd/named/dhcpd/dhcpd and radvd/dnsmasq providing the server side support. It was developed based on the NetworkManager-0.9.7.0-1.git20120820 rpms.

The patch is an attached file because my mail browser (Thunderbird) seems to screw up inline patches. The final submittal will be as an attached file or from git ... your choice guys.

Comment??  Suggestions??  Whatever??

Gene
diff -urp NetworkManager-0.9.7.0-orig-0/libnm-util/libnm-util.ver NetworkManager-0.9.7.0/libnm-util/libnm-util.ver
--- NetworkManager-0.9.7.0-orig-0/libnm-util/libnm-util.ver	2012-07-17 09:43:14.000000000 -0400
+++ NetworkManager-0.9.7.0/libnm-util/libnm-util.ver	2012-09-28 11:11:27.925410145 -0400
@@ -310,6 +310,7 @@ global:
 	nm_setting_ip6_config_error_get_type;
 	nm_setting_ip6_config_error_quark;
 	nm_setting_ip6_config_get_address;
+	nm_setting_ip6_config_get_dhcp_hostname;
 	nm_setting_ip6_config_get_dns;
 	nm_setting_ip6_config_get_dns_search;
 	nm_setting_ip6_config_get_ignore_auto_dns;
diff -urp NetworkManager-0.9.7.0-orig-0/libnm-util/nm-setting-ip6-config.c NetworkManager-0.9.7.0/libnm-util/nm-setting-ip6-config.c
--- NetworkManager-0.9.7.0-orig-0/libnm-util/nm-setting-ip6-config.c	2012-07-17 09:43:14.000000000 -0400
+++ NetworkManager-0.9.7.0/libnm-util/nm-setting-ip6-config.c	2012-09-28 11:11:27.927410131 -0400
@@ -66,6 +66,7 @@ G_DEFINE_TYPE (NMSettingIP6Config, nm_se
 
 typedef struct {
 	char *method;
+	char *dhcp_hostname;
 	GSList *dns;        /* array of struct in6_addr */
 	GSList *dns_search; /* list of strings */
 	GSList *addresses;  /* array of NMIP6Address */
@@ -81,6 +82,7 @@ typedef struct {
 enum {
 	PROP_0,
 	PROP_METHOD,
+	PROP_DHCP_HOSTNAME,
 	PROP_DNS,
 	PROP_DNS_SEARCH,
 	PROP_ADDRESSES,
@@ -122,6 +124,23 @@ nm_setting_ip6_config_get_method (NMSett
 }
 
 /**
+ * nm_setting_ip6_config_get_dhcp_hostname:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns the value contained in the #NMSettingIP6Config:dhcp-hostname
+ * property.
+ *
+ * Returns: the configured hostname to send to the DHCP server
+ **/
+const char *
+nm_setting_ip6_config_get_dhcp_hostname (NMSettingIP6Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+	return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dhcp_hostname;
+}
+
+/**
  * nm_setting_ip6_config_get_num_dns:
  * @setting: the #NMSettingIP6Config
  *
@@ -693,6 +712,14 @@ verify (NMSetting *setting, GSList *all_
 		return FALSE;
 	}
 
+	if (priv->dhcp_hostname && !strlen (priv->dhcp_hostname)) {
+		g_set_error (error,
+		             NM_SETTING_IP6_CONFIG_ERROR,
+		             NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME);
+		return FALSE;
+	}
+
 	return TRUE;
 }
 
@@ -751,6 +778,10 @@ set_property (GObject *object, guint pro
 	case PROP_IGNORE_AUTO_DNS:
 		priv->ignore_auto_dns = g_value_get_boolean (value);
 		break;
+	case PROP_DHCP_HOSTNAME:
+		g_free (priv->dhcp_hostname);
+		priv->dhcp_hostname = g_value_dup_string (value);
+		break;
 	case PROP_NEVER_DEFAULT:
 		priv->never_default = g_value_get_boolean (value);
 		break;
@@ -794,6 +825,9 @@ get_property (GObject *object, guint pro
 	case PROP_IGNORE_AUTO_DNS:
 		g_value_set_boolean (value, priv->ignore_auto_dns);
 		break;
+	case PROP_DHCP_HOSTNAME:
+		g_value_set_string (value, priv->dhcp_hostname);
+		break;
 	case PROP_NEVER_DEFAULT:
 		g_value_set_boolean (value, priv->never_default);
 		break;
@@ -860,6 +894,20 @@ nm_setting_ip6_config_class_init (NMSett
 						      G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
 
 	/**
+	 * NMSettingIP6Config:dhcp-hostname:
+	 *
+	 * The specified name will be sent to the DHCP server when acquiring a lease.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_DHCP_HOSTNAME,
+		 g_param_spec_string (NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME,
+						   "DHCP Hostname",
+						   "The specified name will be sent to the DHCP server "
+						   "when acquiring a lease.",
+						   NULL,
+						   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
 	 * NMSettingIP6Config:dns:
 	 *
 	 * Array of DNS servers, where each member of the array is a byte array
diff -urp NetworkManager-0.9.7.0-orig-0/libnm-util/nm-setting-ip6-config.h NetworkManager-0.9.7.0/libnm-util/nm-setting-ip6-config.h
--- NetworkManager-0.9.7.0-orig-0/libnm-util/nm-setting-ip6-config.h	2012-07-30 11:33:26.000000000 -0400
+++ NetworkManager-0.9.7.0/libnm-util/nm-setting-ip6-config.h	2012-09-28 11:11:27.929410119 -0400
@@ -69,6 +69,8 @@ GQuark nm_setting_ip6_config_error_quark
 #define NM_SETTING_IP6_CONFIG_NEVER_DEFAULT      "never-default"
 #define NM_SETTING_IP6_CONFIG_MAY_FAIL           "may-fail"
 #define NM_SETTING_IP6_CONFIG_IP6_PRIVACY        "ip6-privacy"
+#define NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME      "dhcp-hostname"
+
 
 /**
  * NM_SETTING_IP6_CONFIG_METHOD_IGNORE:
@@ -239,6 +241,7 @@ void                   nm_setting_ip6_co
 gboolean               nm_setting_ip6_config_get_ignore_auto_routes (NMSettingIP6Config *setting);
 
 gboolean               nm_setting_ip6_config_get_ignore_auto_dns    (NMSettingIP6Config *setting);
+const char *           nm_setting_ip6_config_get_dhcp_hostname      (NMSettingIP6Config *setting);
 gboolean               nm_setting_ip6_config_get_never_default      (NMSettingIP6Config *setting);
 gboolean               nm_setting_ip6_config_get_may_fail           (NMSettingIP6Config *setting);
 NMSettingIP6ConfigPrivacy nm_setting_ip6_config_get_ip6_privacy (NMSettingIP6Config *setting);
diff -urp NetworkManager-0.9.7.0-orig-0/src/dhcp-manager/nm-dhcp-dhclient.c NetworkManager-0.9.7.0/src/dhcp-manager/nm-dhcp-dhclient.c
--- NetworkManager-0.9.7.0-orig-0/src/dhcp-manager/nm-dhcp-dhclient.c	2012-05-25 15:09:55.000000000 -0400
+++ NetworkManager-0.9.7.0/src/dhcp-manager/nm-dhcp-dhclient.c	2012-09-28 11:11:27.943410022 -0400
@@ -330,7 +330,7 @@ merge_dhclient_config (const char *iface
 		}
 	}
 
-	new = nm_dhcp_dhclient_create_config (iface, s_ip4, anycast_addr, hostname, orig_path, orig);
+	new = nm_dhcp_dhclient_create_ip4_config (iface, s_ip4, anycast_addr, hostname, orig_path, orig);
 	g_assert (new);
 	success = g_file_set_contents (conf_file, new, -1, error);
 	g_free (new);
@@ -346,7 +346,7 @@ merge_dhclient_config (const char *iface
  * config file along with the NM options.
  */
 static char *
-create_dhclient_config (const char *iface,
+create_dhclient_ip4_config (const char *iface,
                         NMSettingIP4Config *s_ip4,
                         guint8 *dhcp_anycast_addr,
                         const char *hostname)
@@ -402,6 +402,39 @@ create_dhclient_config (const char *ifac
 	return conf_file;
 }
 
+/* For NM to support dynamic DNS updated, we need to put some
+ * statements to be sent to the dhcp6 server and this is a new
+ * conf file.
+ */
+static char *
+create_dhclient_ip6_config (const char *iface,
+                        NMSettingIP6Config *s_ip6,
+                        guint8 *dhcp_anycast_addr,
+                        const char *hostname)
+{
+	char *tmp, *new, *conf_file = NULL;
+	GError *error = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (iface != NULL, NULL);
+
+	tmp = g_strdup_printf ("nm-dhclient6-%s.conf", iface);
+	conf_file = g_build_filename ("/var", "run", tmp, NULL);
+	g_free (tmp);
+
+	new = nm_dhcp_dhclient_create_ip6_config (iface, s_ip6, dhcp_anycast_addr, hostname);
+	g_assert (new);
+	success = g_file_set_contents (conf_file, new, -1, &error);
+	g_free (new);
+
+	if (!success) {
+		nm_log_warn (LOGD_DHCP6, "(%s): error creating dhclient6 configuration: %s",
+		             iface, error->message);
+		g_error_free (error);
+	}
+
+	return conf_file;
+}
 
 static void
 dhclient_child_setup (gpointer user_data G_GNUC_UNUSED)
@@ -556,7 +589,7 @@ real_ip4_start (NMDHCPClient *client,
 
 	iface = nm_dhcp_client_get_iface (client);
 
-	priv->conf_file = create_dhclient_config (iface, s_ip4, dhcp_anycast_addr, hostname);
+	priv->conf_file = create_dhclient_ip4_config (iface, s_ip4, dhcp_anycast_addr, hostname);
 	if (!priv->conf_file) {
 		nm_log_warn (LOGD_DHCP4, "(%s): error creating dhclient configuration file.", iface);
 		return -1;
@@ -572,6 +605,17 @@ real_ip6_start (NMDHCPClient *client,
                 const char *hostname,
                 gboolean info_only)
 {
+	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	const char *iface;
+
+	iface = nm_dhcp_client_get_iface (client);
+
+	priv->conf_file = create_dhclient_ip6_config (iface, s_ip6, dhcp_anycast_addr, hostname);
+	if (!priv->conf_file) {
+		nm_log_warn (LOGD_DHCP6, "(%s): error creating dhclient6 configuration file.", iface);
+		return -1;
+	}
+
 	return dhclient_start (client, info_only ? "-S" : "-N", FALSE);
 }
 
diff -urp NetworkManager-0.9.7.0-orig-0/src/dhcp-manager/nm-dhcp-dhclient-utils.c NetworkManager-0.9.7.0/src/dhcp-manager/nm-dhcp-dhclient-utils.c
--- NetworkManager-0.9.7.0-orig-0/src/dhcp-manager/nm-dhcp-dhclient-utils.c	2012-03-29 14:36:42.000000000 -0400
+++ NetworkManager-0.9.7.0/src/dhcp-manager/nm-dhcp-dhclient-utils.c	2012-09-28 11:11:27.944410015 -0400
@@ -48,7 +48,7 @@ add_also_request (GPtrArray *array, cons
 }
 
 char *
-nm_dhcp_dhclient_create_config (const char *interface,
+nm_dhcp_dhclient_create_ip4_config (const char *interface,
                                 NMSettingIP4Config *s_ip4,
                                 guint8 *anycast_addr,
                                 const char *hostname,
@@ -225,4 +225,58 @@ nm_dhcp_dhclient_create_config (const ch
 
 	return g_string_free (new_contents, FALSE);
 }
+
+static char *ip6_data1 =
+            "send fqdn.encoded on;\n"
+            "send fqdn.no-client-update on;\n"
+            "send fqdn.server-update on;\n";
+
+/* the stuff below should work but dhclient never sends it. 
+ * Right now I am leaving this in but it is not sent.
+ */
+static char *ip6_data2 = 
+            "also request dhcp6.fqdn;\n"
+            "also request dhcp6.hostname;\n"
+            "also request dhcp6.domainname;\n"
+            "also request fqdn.fqdn;\n"
+            "also request fqdn.hostname;\n"
+            "also request fqdn.domainname;\n";
+
+static char *ip6_data3 = 
+            "also request dhcp6.name-servers;\n"
+            "also request dhcp6.domain-search;\n"
+            "also request dhcp6.client-id;\n"
+            "also request dhcp6.server-id;\n";
+
+char *nm_dhcp_dhclient_create_ip6_config (const char *interface,
+                                      NMSettingIP6Config *s_ip6,
+                                      guint8 *anycast_addr,
+                                      const char *hostname)
+{
+	GString *new_contents;
+
+	new_contents = g_string_new (_("# Created by NetworkManager\n\n"));
+
+	if (hostname) {
+		char *plain_hostname, *dot;
+
+		plain_hostname = g_strdup (hostname);
+		dot = strchr (plain_hostname, '.');
+
+		/* get rid of the domain */
+		if (dot)
+			*dot = '\0';
+
+		g_string_append_printf (new_contents, "send fqdn.fqdn \"%s\"; \n",
+				plain_hostname);
+		g_free (plain_hostname);
+	}
+	/* OK, lets not get fancy; just send the stuff out */
+	g_string_append (new_contents, ip6_data1);
+	g_string_append (new_contents, ip6_data3);
+	g_string_append (new_contents, "\n\n");
+
+
+	return g_string_free (new_contents, FALSE);
+}
 
diff -urp NetworkManager-0.9.7.0-orig-0/src/dhcp-manager/nm-dhcp-dhclient-utils.h NetworkManager-0.9.7.0/src/dhcp-manager/nm-dhcp-dhclient-utils.h
--- NetworkManager-0.9.7.0-orig-0/src/dhcp-manager/nm-dhcp-dhclient-utils.h	2012-02-16 08:34:22.000000000 -0500
+++ NetworkManager-0.9.7.0/src/dhcp-manager/nm-dhcp-dhclient-utils.h	2012-09-28 11:11:27.944410015 -0400
@@ -23,13 +23,19 @@
 #include <glib-object.h>
 
 #include <nm-setting-ip4-config.h>
+#include <nm-setting-ip6-config.h>
 
-char *nm_dhcp_dhclient_create_config (const char *interface,
+char *nm_dhcp_dhclient_create_ip4_config (const char *interface,
                                       NMSettingIP4Config *s_ip4,
                                       guint8 *anycast_addr,
                                       const char *hostname,
                                       const char *orig_path,
                                       const char *orig_contents);
 
+char *nm_dhcp_dhclient_create_ip6_config (const char *interface,
+                                      NMSettingIP6Config *s_ip6,
+                                      guint8 *anycast_addr,
+                                      const char *hostname);
+
 #endif /* NM_DHCP_DHCLIENT_UTILS_H */
 
diff -urp NetworkManager-0.9.7.0-orig-0/src/dhcp-manager/nm-dhcp-manager.c NetworkManager-0.9.7.0/src/dhcp-manager/nm-dhcp-manager.c
--- NetworkManager-0.9.7.0-orig-0/src/dhcp-manager/nm-dhcp-manager.c	2012-06-21 10:07:51.000000000 -0400
+++ NetworkManager-0.9.7.0/src/dhcp-manager/nm-dhcp-manager.c	2012-09-28 11:11:27.945410008 -0400
@@ -424,6 +424,10 @@ client_start (NMDHCPManager *self,
 	g_return_val_if_fail (client != NULL, NULL);
 	add_client (self, client);
 
+	nm_log_dbg (LOGD_DHCP, "hostname is %s for start %s", 
+			hostname ? hostname : "NULL",
+			ipv6 ? "ipv6" : "ipv4");
+
 	if (ipv6)
 		success = nm_dhcp_client_start_ip6 (client, s_ip6, dhcp_anycast_addr, hostname, info_only);
 	else
@@ -498,7 +502,28 @@ nm_dhcp_manager_start_ip6 (NMDHCPManager
                            guint8 *dhcp_anycast_addr,
                            gboolean info_only)
 {
-	return client_start (self, iface, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr, NULL, info_only);
+	NMDHCPManagerPrivate *priv;
+	const char *hostname = NULL;
+
+	g_return_val_if_fail (self, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+
+
+	hostname = nm_setting_ip6_config_get_dhcp_hostname (s_ip6);
+
+	if (hostname==NULL && priv->hostname_provider) {
+		nm_log_dbg (LOGD_DHCP6, 
+			"hostname is NULL from ip6_config_get, trying hostname_provider");
+		hostname = nm_hostname_provider_get_hostname (priv->hostname_provider);
+		if (   hostname
+		    && (!strcmp (hostname, "localhost.localdomain") ||
+		        !strcmp (hostname, "localhost6.localdomain6")))
+			hostname = NULL;
+	}
+
+	return client_start (self, iface, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr, hostname, info_only);
 }
 
 static void
diff -urp NetworkManager-0.9.7.0-orig-0/src/dhcp-manager/tests/test-dhcp-dhclient.c NetworkManager-0.9.7.0/src/dhcp-manager/tests/test-dhcp-dhclient.c
--- NetworkManager-0.9.7.0-orig-0/src/dhcp-manager/tests/test-dhcp-dhclient.c	2012-02-16 08:34:22.000000000 -0500
+++ NetworkManager-0.9.7.0/src/dhcp-manager/tests/test-dhcp-dhclient.c	2012-09-28 11:11:27.946410001 -0400
@@ -40,7 +40,7 @@ test_config (const char *orig,
 	s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
 	g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, dhcp_client_id, NULL);
 
-	new = nm_dhcp_dhclient_create_config (iface,
+	new = nm_dhcp_dhclient_create_ip4_config (iface,
 	                                      s_ip4,
 	                                      anycast_addr,
 	                                      hostname,
diff -urp NetworkManager-0.9.7.0-orig-0/src/settings/plugins/ifcfg-rh/reader.c NetworkManager-0.9.7.0/src/settings/plugins/ifcfg-rh/reader.c
--- NetworkManager-0.9.7.0-orig-0/src/settings/plugins/ifcfg-rh/reader.c	2012-07-23 12:58:35.000000000 -0400
+++ NetworkManager-0.9.7.0/src/settings/plugins/ifcfg-rh/reader.c	2012-09-28 11:11:27.935410077 -0400
@@ -1301,6 +1301,8 @@ make_ip4_setting (shvarFile *ifcfg,
 		}
 	} else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
 		value = svGetValue (ifcfg, "DHCP_HOSTNAME", FALSE);
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "IPv4 hostname is '%s'", 
+			value ? value : "NULL");
 		if (value && strlen (value))
 			g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, value, NULL);
 		g_free (value);
@@ -1556,6 +1558,8 @@ make_ip6_setting (shvarFile *ifcfg,
 	              NM_SETTING_IP6_CONFIG_IP6_PRIVACY, ip6_privacy_val,
 	              NULL);
 
+	PLUGIN_WARN (IFCFG_PLUGIN_NAME, "IPv6 method is '%s'",
+			method ? method : "NULL");
 	/* Don't bother to read IP, DNS and routes when IPv6 is disabled */
 	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0)
 		return NM_SETTING (s_ip6);
@@ -1591,7 +1595,16 @@ make_ip6_setting (shvarFile *ifcfg,
 		}
 		g_strfreev (list);
 	} else if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
-		/* TODO - autoconf or DHCPv6 stuff goes here */
+		/* TODO - autoconf stuff goes here */
+	} else if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
+		/* Handling DHCPv6 here */
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "info: DHCPV6C specified");
+		value = svGetValue (ifcfg, "DHCP_HOSTNAME", FALSE);
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "IPv6 hostname is '%s'", 
+			value ? value : "NULL");
+		if (value && strlen (value))
+			g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME, value, NULL);
+		g_free (value);
 	}
 
 	/* DNS servers
diff -urp NetworkManager-0.9.7.0-orig-0/src/settings/plugins/ifcfg-rh/writer.c NetworkManager-0.9.7.0/src/settings/plugins/ifcfg-rh/writer.c
--- NetworkManager-0.9.7.0-orig-0/src/settings/plugins/ifcfg-rh/writer.c	2012-07-23 12:58:35.000000000 -0400
+++ NetworkManager-0.9.7.0/src/settings/plugins/ifcfg-rh/writer.c	2012-09-28 11:11:27.937410063 -0400
@@ -1529,6 +1529,8 @@ write_ip4_setting (NMConnection *connect
 		            FALSE);
 
 		value = nm_setting_ip4_config_get_dhcp_hostname (s_ip4);
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Set IPv4 DHCP_HOSTNAME to %s",
+		             value ? value : "Not Set");
 		if (value)
 			svSetValue (ifcfg, "DHCP_HOSTNAME", value, FALSE);
 
@@ -1726,9 +1728,15 @@ write_ip6_setting (NMConnection *connect
 		svSetValue (ifcfg, "IPV6_AUTOCONF", "yes", FALSE);
 		svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
+		const char *hostname;
 		svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
 		svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);
 		svSetValue (ifcfg, "DHCPV6C", "yes", FALSE);
+		hostname = nm_setting_ip6_config_get_dhcp_hostname (s_ip6);
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Set IPv6 DHCP_HOSTNAME to %s",
+		             hostname ? hostname : "Not Set");
+		if (hostname)
+			svSetValue (ifcfg, "DHCP_HOSTNAME", hostname, FALSE);
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
 		svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
 		svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]