Blob Blame History Raw
From 94b9fcc03560b36932405727759a5621966cd212 Mon Sep 17 00:00:00 2001
From: Jonathan Lebon <jlebon@redhat.com>
Date: Fri, 13 Dec 2013 16:37:57 -0500
Subject: BZ1044429: fix client.exp and improve client/server

---
 csclient.cxx                                      | 342 ++++++++++++----------
 csclient.h                                        |   4 +-
 nsscommon.cxx                                     |  11 +-
 testsuite/lib/systemtap.exp                       |   1 +
 testsuite/systemtap.server/client.exp             | 333 ++++++++++-----------
 testsuite/systemtap.server/server_args.exp        |  17 +-
 testsuite/systemtap.server/server_concurrency.exp |   4 +-
 7 files changed, 373 insertions(+), 339 deletions(-)

diff --git a/csclient.cxx b/csclient.cxx
index cce5428..568cc8f 100644
--- a/csclient.cxx
+++ b/csclient.cxx
@@ -1,6 +1,6 @@
 /*
  Compile server client functions
- Copyright (C) 2010-2013 Red Hat Inc.
+ Copyright (C) 2010-2014 Red Hat Inc.
 
  This file is part of systemtap, and is free software.  You can
  redistribute it and/or modify it under the terms of the GNU General
@@ -88,66 +88,71 @@ nsscommon_error (const char *msg, int logit __attribute ((unused)))
 // Information about compile servers.
 struct compile_server_info
 {
-  compile_server_info ()
+  compile_server_info () : port(0), fully_specified(false)
   {
     memset (& address, 0, sizeof (address));
   }
 
   string host_name;
   PRNetAddr address;
+  unsigned short port;
+  bool fully_specified;
   string version;
   string sysinfo;
   string certinfo;
 
   bool empty () const
   {
-    return this->host_name.empty () && ! this->hasAddress ();
+    return this->host_name.empty () && ! this->hasAddress () && certinfo.empty ();
   }
   bool hasAddress () const
   {
     return this->address.raw.family != 0;
   }
-  unsigned short port () const
-  {
-    if (this->address.raw.family == PR_AF_INET)
-      return ntohs (this->address.inet.port);
-    if (this->address.raw.family == PR_AF_INET6)
-      return ntohs (this->address.ipv6.port);
-    return 0;
-  }
-  unsigned short setPort (unsigned short port)
+  unsigned short setAddressPort (unsigned short port)
   {
     if (this->address.raw.family == PR_AF_INET)
       return this->address.inet.port = htons (port);
     if (this->address.raw.family == PR_AF_INET6)
       return this->address.ipv6.port = htons (port);
+    assert (0);
     return 0;
   }
+  bool isComplete () const
+  {
+    return this->hasAddress () && port != 0;
+  }
 
   bool operator== (const compile_server_info &that) const
   {
-    // If both ip addressed are not set, then the host names must match, otherwise
-    // the addresses must match.
-    if (! this->hasAddress() || ! that.hasAddress())
+    // If one item or the other has only a name, and possibly a port specified,
+    // then allow a match by name and port only. This is so that the user can specify
+    // names which are returned by avahi, but are not dns resolvable.
+    // Otherwise, we will ignore the host_name.
+    if ((! this->hasAddress() && this->version.empty () &&
+	 this->sysinfo.empty () && this->certinfo.empty ()) ||
+	(! that.hasAddress() && that.version.empty () &&
+	 that.sysinfo.empty () && that.certinfo.empty ()))
       {
 	if (this->host_name != that.host_name)
 	  return false;
       }
-    else if (this->address != that.address)
-      return false;
 
     // Compare the other fields only if they have both been set.
-    if (this->port() != 0 && that.port() != 0 &&
-	this->port() != that.port())
+    if (this->hasAddress() && that.hasAddress() &&
+	this->address != that.address)
+      return false;
+    if (this->port != 0 && that.port != 0 &&
+        this->port != that.port)
       return false;
     if (! this->version.empty () && ! that.version.empty () &&
-	this->version != that.version)
+        this->version != that.version)
       return false;
     if (! this->sysinfo.empty () && ! that.sysinfo.empty () &&
-	this->sysinfo != that.sysinfo)
+        this->sysinfo != that.sysinfo)
       return false;
     if (! this->certinfo.empty () && ! that.certinfo.empty () &&
-	this->certinfo != that.certinfo)
+        this->certinfo != that.certinfo)
       return false;
 
     return true; // They are equal
@@ -179,6 +184,14 @@ preferred_order (vector<compile_server_info> &servers)
   sort (servers.begin (), servers.end ());
 }
 
+struct resolved_host
+{
+  string host_name;
+  PRNetAddr address;
+  resolved_host(string chost_name, PRNetAddr caddress):
+    host_name(chost_name), address(caddress) {}
+};
+
 struct compile_server_cache
 {
   vector<compile_server_info> default_servers;
@@ -187,7 +200,7 @@ struct compile_server_cache
   vector<compile_server_info> signing_servers;
   vector<compile_server_info> online_servers;
   vector<compile_server_info> all_servers;
-  map<string,vector<compile_server_info> > resolved_servers;
+  map<string,vector<resolved_host> > resolved_hosts;
 };
 
 // For filtering queries.
@@ -234,7 +247,7 @@ static void resolve_host (systemtap_session& s, compile_server_info &server, vec
 // -----------------------------------------------------
 // NSS related code used by the compile server client
 // -----------------------------------------------------
-static void add_server_trust (systemtap_session &s, const string &cert_db_path, const vector<compile_server_info> &server_list);
+static void add_server_trust (systemtap_session &s, const string &cert_db_path, vector<compile_server_info> &server_list);
 static void revoke_server_trust (systemtap_session &s, const string &cert_db_path, const vector<compile_server_info> &server_list);
 static void get_server_info_from_db (systemtap_session &s, vector<compile_server_info> &servers, const string &cert_db_path);
 
@@ -1132,10 +1145,10 @@ compile_server_client::find_and_connect_to_server ()
        i != specified_servers.end ();
        ++i)
     {
-      // If we have an ip address and port number, then just use the one we've
-      // been given. Otherwise, check for matching online servers and try their
+      // If we have an ip address and were given a port number, then just use the one we've
+      // been given. Otherwise, check for matching compatible online servers and try their
       // ip addresses and ports.
-      if (i->hasAddress() && i->port() != 0)
+      if (i->hasAddress() && i->fully_specified)
 	add_server_info (*i, server_list);
       else
 	{
@@ -1145,8 +1158,8 @@ compile_server_client::find_and_connect_to_server ()
 
 	  // If no specific server (port) has been specified,
 	  // then we'll need the servers to be
-	  // compatible and possible trusted as signers as well.
-	  if (i->port() == 0)
+	  // compatible and possibly trusted as signers as well.
+	  if (! i->fully_specified)
 	    {
 	      get_or_keep_compatible_server_info (s, online_servers, true/*keep*/);
 	      if (! pr_contains (s.privilege, pr_stapdev))
@@ -1227,7 +1240,7 @@ compile_server_client::find_and_connect_to_server ()
 
 int 
 compile_server_client::compile_using_server (
-  const vector<compile_server_info> &servers
+  vector<compile_server_info> &servers
 )
 {
   // Make sure NSPR is initialized. Must be done before NSS is initialized
@@ -1275,14 +1288,16 @@ compile_server_client::compile_using_server (
       server_zipfile = s.tmpdir + "/server.zip";
 
       // Try each server in turn.
-      for (vector<compile_server_info>::const_iterator j = servers.begin ();
+      for (vector<compile_server_info>::iterator j = servers.begin ();
 	   j != servers.end ();
 	   ++j)
 	{
 	  // At a minimum we need an ip_address along with a port
 	  // number in order to contact the server.
-	  if (! j->hasAddress() || j->port() == 0)
+	  if (! j->hasAddress() || j->port == 0)
 	    continue;
+	  // Set the port within the address.
+	  j->setAddressPort (j->port);
 
 	  if (s.verbose >= 2)
            clog << _F("Attempting SSL connection with %s\n"
@@ -1673,7 +1688,7 @@ static void
 add_server_trust (
   systemtap_session &s,
   const string &cert_db_path,
-  const vector<compile_server_info> &server_list
+  vector<compile_server_info> &server_list
 )
 {
   // Get a list of servers already trusted. This opens the database, so do it
@@ -1716,7 +1731,7 @@ add_server_trust (
   // Iterate over the servers to become trusted. Contact each one and
   // add it to the list of trusted servers if it is not already trusted.
   // client_connect will issue any error messages.
-  for (vector<compile_server_info>::const_iterator server = server_list.begin();
+  for (vector<compile_server_info>::iterator server = server_list.begin();
        server != server_list.end ();
        ++server)
     {
@@ -1735,10 +1750,14 @@ add_server_trust (
 	    trust_already_in_place (*server, server_list, cert_db_path, false/*revoking*/);
 	  continue;
 	}
+
       // At a minimum we need an ip_address along with a port
       // number in order to contact the server.
-      if (! server->hasAddress() || server->port() == 0)
+      if (! server->hasAddress() || server->port == 0)
 	continue;
+      // Set the port within the address.
+      server->setAddressPort (server->port);
+
       int rc = client_connect (*server, NULL, NULL, "permanent");
       if (rc != SUCCESS)
 	{
@@ -2076,8 +2095,8 @@ ostream &operator<< (ostream &s, const compile_server_info &i)
   else
     s << "offline";
   s << " port=";
-  if (i.port() != 0)
-    s << i.port();
+  if (i.port != 0)
+    s << i.port;
   else
     s << "unknown";
   s << " sysinfo=\"";
@@ -2586,7 +2605,8 @@ isPort (const char *pstr, compile_server_info &server_info)
       clog << _F("Invalid port number specified: %s", pstr) << endl;
       return false;
     }
-  server_info.setPort (p);
+  server_info.port = p;
+  server_info.fully_specified = true;
   return true;
 }
 
@@ -2665,8 +2685,10 @@ isIPv6 (const string &server, compile_server_info &server_info)
   if (! empty && components.size() != 8)
     return false; // Not a valid IPv6 address
 
-  // Calls to setPort and isPort need to know that this is an IPv6 address.
-  server_info.address.raw.family = PR_AF_INET6;
+  // Try to convert the string to an address.
+  PRStatus prStatus = PR_StringToNetAddr (ip.c_str(), & server_info.address);
+  if (prStatus != PR_SUCCESS)
+    return false;
 
   // Examine the optional port
   if (portIx != string::npos)
@@ -2683,10 +2705,8 @@ isIPv6 (const string &server, compile_server_info &server_info)
 	}
     }
   else
-    server_info.setPort (0);
+    server_info.port = 0;
 
-  // Treat the ip address string like a host name.
-  server_info.host_name = ip;
   return true; // valid IPv6 address.
 }
 
@@ -2703,20 +2723,20 @@ isIPv4 (const string &server, compile_server_info &server_info)
   if (components.size() > 2)
     return false; // Not a valid IPv4 address
 
-  // Separate the host from the port (if any).
-  string host;
+  // Separate the address from the port (if any).
+  string addr;
   string port;
   if (components.size() <= 1)
-    host = server;
+    addr = server;
   else {
-    host = components[0];
+    addr = components[0];
     port = components[1];
   }
 
-  // Separate the host components.
+  // Separate the address components.
   // There must be exactly 4 components.
   components.clear ();
-  tokenize (server, components, ".");
+  tokenize (addr, components, ".");
   if (components.size() != 4)
     return false; // Not a valid IPv4 address
   
@@ -2732,8 +2752,10 @@ isIPv4 (const string &server, compile_server_info &server_info)
 	return false; // Not a valid IPv4 address
     }
 
-  // Calls to setPort and isPort need to know that this is an IPv4 address.
-  server_info.address.raw.family = PR_AF_INET;
+  // Try to convert the string to an address.
+  PRStatus prStatus = PR_StringToNetAddr (addr.c_str(), & server_info.address);
+  if (prStatus != PR_SUCCESS)
+    return false;
 
   // Examine the optional port
   if (! port.empty ()) {
@@ -2741,10 +2763,8 @@ isIPv4 (const string &server, compile_server_info &server_info)
       return false; // not a valid port
   }
   else
-    server_info.setPort (0);
+    server_info.port = 0;
 
-  // Treat the ip address string like a host name.
-  server_info.host_name = host;
   return true; // valid IPv4 address.
 }
 
@@ -2754,8 +2774,6 @@ isCertSerialNumber (const string &server, compile_server_info &server_info)
   // This function assumes that we have already ruled out the server spec being an IPv6 address.
   // Certificate serial numbers are 5 fields separated by colons plus an optional 6th decimal
   // field specifying a port.
-  // Assume IPv4 (for now) when storing the port.
-  server_info.address.raw.family = PR_AF_INET;
   assert (! server.empty());
   string host = server;
   vector<string> components;
@@ -2782,8 +2800,6 @@ isDomain (const string &server, compile_server_info &server_info)
 {
   // Accept one or two components separated by a colon. The first will be the domain name and
   // the second must a port number.
-  // Assume IPv4 (for now) when storing the port.
-  server_info.address.raw.family = PR_AF_INET;
   assert (! server.empty());
   string host = server;
   vector<string> components;
@@ -2858,50 +2874,69 @@ get_specified_server_info (
 	      // Check for IPv6 addresses first. It reduces the amount of checking necessary for
 	      // certificate serial numbers.
 	      compile_server_info server_info;
-	      vector<compile_server_info> known_servers;
+	      vector<compile_server_info> resolved_servers;
 	      if (isIPv6 (server, server_info) || isIPv4 (server, server_info) ||
-		  isDomain (server, server_info))
+		  isCertSerialNumber (server, server_info))
 		{
-		  // Resolve this host and add any information that is discovered.
-		  // Try to augment the resolved servers with information about known servers.
-		  // There may be no intersection.
-		  get_all_server_info (s, known_servers);
-
-		  vector<compile_server_info> resolved_servers;
+		  // An address or cert serial number has been specified.
+		  // No resolution is needed.
+		  resolved_servers.push_back (server_info);
+		}		  
+	      else if (isDomain (server, server_info))
+		{
+		  // Try to resolve the given name.
 		  resolve_host (s, server_info, resolved_servers);
-
-		  vector<compile_server_info> common_servers = resolved_servers;
-		  keep_common_server_info (known_servers, common_servers);
-		  if (! common_servers.empty ())
-		    add_server_info (common_servers, resolved_servers);
-
-		  if (s.verbose >= 3)
-		    {
-		      clog << _F("Servers matching %s: ", server.c_str()) << endl;
-		      clog << resolved_servers;
-		    }
-		  add_server_info (resolved_servers, specified_servers);
-		}
-	      else if (isCertSerialNumber (server, server_info))
-		{
-		  // The host could not be resolved. Try resolving it as a certificate serial
-		  // number. Look for all known servers with this serial number and (optional)
-		  // port number.
-		  get_all_server_info (s, known_servers);
-		  keep_server_info_with_cert_and_port (s, server_info, known_servers);
-		  if (s.verbose >= 3)
-		    {
-		      clog << _F("Servers matching %s: ", server.c_str()) << endl;
-		      clog << known_servers;
-		    }
-
-		  add_server_info (known_servers, specified_servers);
 		}
 	      else
 		{
 		  clog << _F("Invalid server specification for --use-server: %s", server.c_str())
 		       << endl;
+		  continue;
 		}
+
+	      // Now examine the server(s) identified and add them to the list of specified
+	      // servers.
+	      vector<compile_server_info> known_servers;
+	      vector<compile_server_info> new_servers;
+	      for (vector<compile_server_info>::iterator i = resolved_servers.begin();
+		   i != resolved_servers.end();
+		   ++i)
+		{
+		  // If this item was fully specified, then just add it.
+		  if (i->fully_specified)
+		    add_server_info (*i, new_servers);
+		  else {
+		    // It was not fully specified, so we need additional info. Try
+		    // to get it by matching what we have against other known servers.
+		    if (known_servers.empty ())
+		      get_all_server_info (s, known_servers);
+
+		    // See if this server spec matches that of a known server
+		    vector<compile_server_info> matched_servers = known_servers;
+		    keep_common_server_info (*i, matched_servers);
+
+		    // If this server spec matches one or more known servers, then add the
+		    // augmented info to the specified_servers. Otherwise, if this server
+		    // spec is complete, then add it directly. Otherwise this server spec
+		    // is incomplete.
+		    if (! matched_servers.empty())
+		      add_server_info (matched_servers, new_servers);
+		    else if (i->isComplete ())
+		      add_server_info (*i, new_servers);
+		    else if (s.verbose >= 3)
+		      clog << _("Incomplete server spec: ") << *i << endl;
+		  }
+		}
+
+	      if (s.verbose >= 3)
+		{
+		  clog << _F("Servers matching %s: ", server.c_str()) << endl;
+		  clog << new_servers;
+		}
+
+	      // Add the newly identified servers to the list.
+	      if (! new_servers.empty())
+		add_server_info (new_servers, specified_servers);
 	    } // Loop over --use-server options
 	} // -- use-server specified
 
@@ -3087,13 +3122,16 @@ keep_server_info_with_cert_and_port (
 	  continue;
 	}
       if (servers[i].certinfo == server.certinfo &&
-	  (servers[i].port() == 0 || server.port() == 0 ||
-	   servers[i].port() == server.port()))
+	  (servers[i].port == 0 || server.port == 0 ||
+	   servers[i].port == server.port))
 	{
 	  // If the server is not online, then use the specified
 	  // port, if any.
-	  if (servers[i].port() == 0)
-	    servers[i].setPort (server.port());
+	  if (servers[i].port == 0)
+	    {
+	      servers[i].port = server.port;
+	      servers[i].fully_specified = server.fully_specified;
+	    }
 	  ++i;
 	  continue;
 	}
@@ -3110,8 +3148,8 @@ resolve_host (
   vector<compile_server_info> &resolved_servers
 )
 {
-  vector<compile_server_info>& cached_servers = cscache(s)->resolved_servers[server.host_name];
-  if (cached_servers.empty ())
+  vector<resolved_host>& cached_hosts = cscache(s)->resolved_hosts[server.host_name];
+  if (cached_hosts.empty ())
     {
       // The server's host_name member is a string containing either a host name or an ip address.
       // Either is acceptable for lookup.
@@ -3128,10 +3166,8 @@ resolve_host (
       // Failure to resolve will result in an appropriate message later, if other methods fail.
       if (rc != 0)
 	{
-	  // At a minimum, return the information we were given.
 	  if (s.verbose >= 6)
 	    clog << _F("%s not found: %s", lookup_name, gai_strerror (rc)) << endl;
-	  add_server_info (server, cached_servers);
 	}
       else
 	{
@@ -3139,28 +3175,23 @@ resolve_host (
 	  assert (addr_info);
 	  for (const struct addrinfo *ai = addr_info; ai != NULL; ai = ai->ai_next)
 	    {
-	      // Start with the info we were given.
-	      compile_server_info new_server = server;
+	      PRNetAddr new_address;
 
 	      // We support IPv4 and IPv6, Ignore other protocols,
 	      if (ai->ai_family == AF_INET)
 		{
 		  // IPv4 Address
 		  struct sockaddr_in *ip = (struct sockaddr_in *)ai->ai_addr;
-		  new_server.address.inet.family = PR_AF_INET;
-		  if (ip->sin_port != 0)
-		    new_server.address.inet.port = ip->sin_port;
-		  new_server.address.inet.ip = ip->sin_addr.s_addr;
+		  new_address.inet.family = PR_AF_INET;
+		  new_address.inet.ip = ip->sin_addr.s_addr;
 		}
 	      else if (ai->ai_family == AF_INET6)
 		{
 		  // IPv6 Address
 		  struct sockaddr_in6 *ip = (struct sockaddr_in6 *)ai->ai_addr;
-		  new_server.address.ipv6.family = PR_AF_INET6;
-		  if (ip->sin6_port != 0)
-		    new_server.address.ipv6.port = ip->sin6_port;
-		  new_server.address.ipv6.scope_id = ip->sin6_scope_id;
-		  copyAddress (new_server.address.ipv6.ip, ip->sin6_addr);
+		  new_address.ipv6.family = PR_AF_INET6;
+		  new_address.ipv6.scope_id = ip->sin6_scope_id;
+		  copyAddress (new_address.ipv6.ip, ip->sin6_addr);
 		}
 	      else
 		continue;
@@ -3169,25 +3200,54 @@ resolve_host (
 	      char hbuf[NI_MAXHOST];
 	      int status = getnameinfo (ai->ai_addr, ai->ai_addrlen, hbuf, sizeof (hbuf), NULL, 0,
 					NI_NAMEREQD | NI_IDN);
-	      if (status == 0)
-		new_server.host_name = hbuf;
+	      if (status != 0)
+		hbuf[0] = '\0';
 
-	      // Add the new resolved server to the list.
-	      add_server_info (new_server, cached_servers);
+	      resolved_host *new_host = new resolved_host(hbuf, new_address);
+	      cached_hosts.push_back(*new_host);
 	    }
 	}
       if (addr_info)
 	freeaddrinfo (addr_info); // free the linked list
-
-      if (s.verbose >= 6)
-	{
-	  clog << _F("%s resolves to:", lookup_name) << endl;
-	  clog << cached_servers;
-	}
     }
 
-  // Add the information, but not duplicates.
-  add_server_info (cached_servers, resolved_servers);
+  // If no addresses were resolved, then return the info we were given.
+  if (cached_hosts.empty())
+    add_server_info (server, resolved_servers);
+  else {
+    // We will add a new server for each address resolved
+    vector<compile_server_info> new_servers;
+    for (vector<resolved_host>::const_iterator it = cached_hosts.begin();
+	 it != cached_hosts.end(); ++it)
+      {
+	// Start with the info we were given
+	compile_server_info new_server = server;
+
+	// NB: do not overwrite port info
+	if (it->address.raw.family == AF_INET)
+	  {
+	    new_server.address.inet.family = PR_AF_INET;
+	    new_server.address.inet.ip = it->address.inet.ip;
+	  }
+	else // AF_INET6
+	  {
+	    new_server.address.ipv6.family = PR_AF_INET6;
+	    new_server.address.ipv6.scope_id = it->address.ipv6.scope_id;
+	    new_server.address.ipv6.ip = it->address.ipv6.ip;
+	  }
+	if (!it->host_name.empty())
+	  new_server.host_name = it->host_name;
+	add_server_info (new_server, new_servers);
+      }
+
+    if (s.verbose >= 6)
+      {
+	clog << _F("%s resolves to:", server.host_name.c_str()) << endl;
+	clog << new_servers;
+      }
+
+    add_server_info (new_servers, resolved_servers);
+  }
 }
 
 #if HAVE_AVAHI
@@ -3268,12 +3328,12 @@ void resolve_callback(
 	    // We support both IPv4 and IPv6. Ignore other protocols.
 	    if (protocol == AVAHI_PROTO_INET6) {
 	      info.address.ipv6.family = PR_AF_INET6;
-	      info.address.ipv6.port = htons (port);
 	      info.address.ipv6.scope_id = interface;
+	      info.port = port;
 	    }
 	    else if (protocol == AVAHI_PROTO_INET) {
 	      info.address.inet.family = PR_AF_INET;
-	      info.address.inet.port = htons (port);
+	      info.port = port;
 	    }
 	    else
 	      break;
@@ -3388,7 +3448,6 @@ get_or_keep_online_server_info (
       online_servers.push_back (compile_server_info ());
 #if HAVE_AVAHI
       // Must predeclare these due to jumping on error to fail:
-      unsigned limit;
       vector<compile_server_info> avahi_servers;
 
       // Initialize.
@@ -3449,25 +3508,6 @@ get_or_keep_online_server_info (
 	  clog << avahi_servers;
 	}
 
-      // Resolve each server discovered, in case there are alternate ways to reach them
-      // (e.g. localhost).
-      limit = avahi_servers.size ();
-      for (unsigned i = 0; i < limit; ++i)
-	{
-	  compile_server_info &avahi_server = avahi_servers[i];
-
-	  // Delete the domain, if it is '.local'
-	  string &host_name = avahi_server.host_name;
-	  string::size_type dot_index = host_name.find ('.');
-	  assert (dot_index != 0);
-	  string domain = host_name.substr (dot_index + 1);
-	  if (domain == "local")
-	    host_name = host_name.substr (0, dot_index);
-
-	  // Add it to the list of servers, unless it is duplicate.
-	  resolve_host (s, avahi_server, online_servers);
-	}
-
       // Merge with the list of servers, as obtained by avahi.
       add_server_info (avahi_servers, online_servers);
 
@@ -3619,15 +3659,19 @@ merge_server_info (
   compile_server_info &target
 )
 {
-  if (target.host_name.empty ())
+  // Copy the host name if the source has one.
+  if (! source.host_name.empty())
     target.host_name = source.host_name;
   // Copy the address unconditionally, if the source has an address, even if they are already
   // equal. The source address may be an IPv6 address with a scope_id that the target is missing.
   assert (! target.hasAddress () || ! source.hasAddress () || source.address == target.address);
   if (source.hasAddress ())
     copyNetAddr (target.address, source.address);
-  if (target.port() == 0)
-    target.setPort (source.port());
+  if (target.port == 0)
+    {
+      target.port = source.port;
+      target.fully_specified = source.fully_specified;
+    }
   if (target.sysinfo.empty ())
     target.sysinfo = source.sysinfo;
   if (target.version.empty ())
diff --git a/csclient.h b/csclient.h
index b7eeda4..e4508ea 100644
--- a/csclient.h
+++ b/csclient.h
@@ -1,5 +1,5 @@
 // -*- C++ -*-
-// Copyright (C) 2010-2011 Red Hat Inc.
+// Copyright (C) 2010-2014 Red Hat Inc.
 //
 // This file is part of systemtap, and is free software.  You can
 // redistribute it and/or modify it under the terms of the GNU General
@@ -35,7 +35,7 @@ private:
   );
   int add_package_args ();
   int add_package_arg (const std::string &arg);
-  int compile_using_server (const std::vector<compile_server_info> &servers);
+  int compile_using_server (std::vector<compile_server_info> &servers);
   int add_localization_variables();
 
   int read_from_file (const std::string &fname, int &data);
diff --git a/nsscommon.cxx b/nsscommon.cxx
index 70aac54..78e38b0 100644
--- a/nsscommon.cxx
+++ b/nsscommon.cxx
@@ -1,7 +1,7 @@
 /*
   Common functions used by the NSS-aware code in systemtap.
 
-  Copyright (C) 2009-2013 Red Hat Inc.
+  Copyright (C) 2009-2014 Red Hat Inc.
 
   This file is part of systemtap, and is free software.  You can
   redistribute it and/or modify it under the terms of the GNU General Public
@@ -998,12 +998,19 @@ gen_cert_db (const string &db_path, const string &extraDnsNames, bool use_passwo
       goto error;
     }
 
-  // Now, generate the cert. We need our host name and the supplied additional dns names (if any).
+  // For the cert, we need our host name.
   struct utsname utsname;
   uname (& utsname);
   dnsNames = utsname.nodename;
+
+  // Because avahi identifies hosts using a ".local" domain, add one to the list of names.
+  dnsNames += string(",") + dnsNames + ".local";
+
+  // Add any extra names that were supplied.
   if (! extraDnsNames.empty ())
     dnsNames += "," + extraDnsNames;
+
+  // Now, generate the cert.
   cert = create_cert (cr, dnsNames);
   CERT_DestroyCertificateRequest (cr);
   if (! cert)
diff --git a/testsuite/lib/systemtap.exp b/testsuite/lib/systemtap.exp
index d4e4d85..64bbed1 100644
--- a/testsuite/lib/systemtap.exp
+++ b/testsuite/lib/systemtap.exp
@@ -244,6 +244,7 @@ proc setup_server { args } {
     }
 
     # Make sure that stap can find the server.
+    exec sleep 1
     set use_server --use-server
     set res [catch { exec stap --list-servers=online,trusted,compatible >& stap-list-servers.out } looksee]
     verbose -log "stap --list-servers returned: res==$res"
diff --git a/testsuite/systemtap.server/client.exp b/testsuite/systemtap.server/client.exp
index f0d0728..41a06cc 100644
--- a/testsuite/systemtap.server/client.exp
+++ b/testsuite/systemtap.server/client.exp
@@ -1,3 +1,6 @@
+# In this test, we are using arrays as sets. The key itself does not matter,
+# only the value
+
 # many of these tests use as_root
 if {! [installtest_p]} { return }
 if {! [nss_p]} { return }
@@ -5,37 +8,111 @@ if {! [nss_p]} { return }
 # Let's start with a clean slate in terms of trust.
 exec rm -fr $env(SYSTEMTAP_DIR)/ssl
 
-# Compare two arrays. If equal, return 1, otherwise 0.
-# (Borrowed from http://wiki.tcl.tk/1032.)
-proc array_compare {array1 array2} {
-    upvar 1 $array1 foo $array2 bar
+# arr given as list (e.g. [array_has [array get myarr] elem])
+proc array_has {arr elem} {
 
-    if {![array exists foo]} {
-        return -code error "$array1 is not an array"
-    }
-    if {![array exists bar]} {
-        return -code error "$array2 is not an array"
-    }
-    if {[array size foo] != [array size bar]} {
-        return 0
-    }
+    array set foo $arr
     if {[array size foo] == 0} {
+        return 0
+    }
+
+    foreach {key val} [array get foo] {
+        if {[string equal $val $elem]} {
+            return 1
+        }
+    }
+
+    return 0
+}
+
+# Check if array1 is a subset of array2. Returns 1 if yes, otherwise 0.
+# (Modified from http://wiki.tcl.tk/1032.)
+proc array_in {array1 array2} {
+    upvar 1 $array1 sub $array2 super
+
+    if {![array exists sub]} {
+        return -code error "$array1 is not an array"
+    }
+    if {![array exists super]} {
+        return -code error "$array2 is not an array"
+    }
+    if {[array size sub] > [array size super]} {
+        return 0
+    }
+    if {[array size sub] == 0} {
         return 1
     }
 
-    set keys [lsort -unique [concat [array names foo] [array names bar]]]
-    if {[llength $keys] != [array size foo]} {
-       return 0
-    }
-
-    foreach key $keys {
-        if {$foo($key) ne $bar($key)} {
+    foreach key [array names sub] {
+        if {![array_has [array get super] $sub($key)]} {
             return 0
         }
     }
     return 1
 }
 
+# Check if array1 and array2 have all the same elements
+proc array_equal {array1 array2} {
+    upvar 1 $array1 foo $array2 bar
+    return [array_in foo bar] \
+        && [array_in bar foo]
+}
+
+# Returns the union of the three arrays
+proc array_union {array1 array2 array3} {
+    upvar 1 $array1 foo $array2 bar $array3 baz
+    array unset ::union
+
+    if {![array exists foo]} {
+        return -code error "$array1 is not an array"
+    }
+    if {![array exists bar]} {
+        return -code error "$array2 is not an array"
+    }
+    if {![array exists baz]} {
+        return -code error "$array3 is not an array"
+    }
+
+    set n 0
+    foreach key [array names foo] {
+        set ::union($n) $foo($key)
+        incr n
+    }
+    foreach key [array names bar] {
+        if {![array_has [array get ::union] $bar($key)]} {
+            set ::union($n) $bar($key)
+            incr n
+        }
+    }
+    foreach key [array names baz] {
+        if {![array_has [array get ::union] $baz($key)]} {
+            set ::union($n) $baz($key)
+            incr n
+        }
+    }
+}
+
+# Returns all the elements in array_new not in array_old
+proc array_diff {array_new array_old} {
+    upvar 1 $array_new anew $array_old aold
+    array unset ::diff
+
+    if {![array exists anew]} {
+        return -code error "$array_new is not an array"
+    }
+    if {![array exists aold]} {
+        return -code error "$array_old is not an array"
+    }
+
+    set n 0
+    foreach key [array names anew] {
+        if {![array_has [array get aold] $anew($key)]} {
+            set ::diff($n) $anew($key)
+            incr n
+        }
+    }
+}
+
 # Test the --list-servers option and return an array of the servers found.
 proc list_servers { TEST_NAME SERVER_SPEC args } {
     set failed 0
@@ -82,7 +159,6 @@ proc list_servers { TEST_NAME SERVER_SPEC args } {
 # Sometimes, we'll see a server running from the last test, if it
 # hasn't quite died yet. So, make sure we get the same result twice.
 list_servers "List existing online servers" online
-array unset eos1
 array set existing_online_servers [array get servers]
 set i 0
 while {1} {
@@ -94,9 +170,9 @@ while {1} {
 	fail "List existing online servers: never got stable"
 	return
     }
-    
+
     verbose -log "verify existing online servers - attempt $i: [array size existing_online_servers] [array size eos2]"
-    if {[array_compare existing_online_servers eos2]} {
+    if {[array_equal existing_online_servers eos2]} {
 	# Arrays are equal, we're done
 	break
     }
@@ -120,37 +196,23 @@ list_servers "List all existing servers" all
 array unset all_existing_servers
 array set all_existing_servers [array get servers]
 
-set test "Verify existing online server list"
-if {[array_compare existing_online_servers all_existing_servers]} {
-    pass "$test"
-} else {
-    fail "$test"
-}
+# First we create a union
+array_union existing_online_servers \
+            existing_trusted_servers \
+            existing_signing_servers
+array set existing_unioned_servers [array get union]
 
-set test "Verify existing trusted server list"
-if {[array_compare existing_trusted_servers all_existing_servers]} {
-    pass "$test"
-} else {
-    fail "$test"
-}
-
-set test "Verify existing signing server list"
-if {[array_compare existing_signing_servers all_existing_servers]} {
-    pass "$test"
-} else {
-    fail "$test"
-}
-
-set test "Verify all existing server list"
-if {[array_compare existing_online_servers all_existing_servers]} {
+# Now we can compare
+set test "Verify existing server list"
+if {[array_equal existing_unioned_servers all_existing_servers]} {
     pass "$test"
 } else {
     fail "$test"
 }
 
 list_servers "List existing online servers (before start)" online
-array unset existing_online_servers1
-array set existing_online_servers1 [array get servers]
+array unset existing_online_servers
+array set existing_online_servers [array get servers]
 
 # Now start our own server and make sure we can work with it.
 if {! [setup_server] || $avahi_ok_p != 1} {
@@ -159,31 +221,22 @@ if {! [setup_server] || $avahi_ok_p != 1} {
 }
 
 # Our server should now appear online, separate from the previously discovered
-# online servers. Note that our server could generate serveral listings
-# because it could appear at more than one ip address,
+# online servers. Note that our server could generate serveral listings because
+# it could appear at more than one ip address,
 list_servers "List current online servers" online
 array unset current_online_servers
 array set current_online_servers [array get servers]
 
+# array_diff will give us all the servers in current not in existing
+array_diff current_online_servers existing_online_servers
+array unset new_online_servers
+array set new_online_servers [array get diff]
+
 set test "New online servers"
-set n 0
-foreach idx1 [array names current_online_servers] {
-    set found 0
-    foreach idx2 [array names existing_online_servers] {
-	if {"$existing_online_servers($idx2)" == "$current_online_servers($idx1)"} {
-	    set found 1
-	    break
-	}
-    }
-    if {$found == 0} {
-	set new_online_servers($n) "$current_online_servers($idx1)"
-	incr n
-    }
-}
-if {$n == 0} {
-    fail "$test"
-} else {
+if {[array size new_online_servers] > 0} {
     pass "$test"
+} else {
+    fail "$test"
 }
 
 # Our server should now be trusted, separate from the previously discovered
@@ -192,48 +245,27 @@ list_servers "List current trusted servers" online,trusted
 array unset current_trusted_servers
 array set current_trusted_servers [array get servers]
 
+# array_diff will give us all the servers in current not in existing
+array_diff current_trusted_servers existing_trusted_servers
+array unset new_trusted_servers
+array set new_trusted_servers [array get diff]
+
 set test "New trusted servers"
-set n 0
-foreach idx1 [array names current_trusted_servers] {
-    set found 0
-    foreach idx2 [array names existing_trusted_servers] {
-	if {"$existing_trusted_servers($idx2)" == "$current_trusted_servers($idx1)"} {
-	    set found 1
-	    break
-	}
-    }
-    if {$found == 0} {
-	set new_trusted_servers($n) "$current_trusted_servers($idx1)"
-	incr n
-    }
-}
-if {$n == 0} {
-    fail "$test"
-} else {
+if {[array size new_trusted_servers] > 0} {
     pass "$test"
+} else {
+    fail "$test"
 }
 
 # The new servers should automatically be trusted, so the new_trusted_servers
-# array should be a subset of the new_online_servers
-# array, but not necessarilty vice-versa, since new servers may have come
-# online independently of our testing.
+# array should be a subset of the new_online_servers array, but not necessarily
+# vice-versa, since new servers may have come online independently of our
+# testing.
 set test "Verify new trusted server list"
-set failed 0
-foreach idx1 [array names new_trusted_servers] {
-    set found 0
-    foreach idx2 [array names new_online_servers] {
-	if {"$new_trusted_servers($idx1)" == "$new_online_servers($idx2)"} {
-	    set found 1
-	    break
-	}
-    }
-    if {$found == 0} {
-	set failed 1
-	fail "$test $idx1"
-    }
-}
-if {$failed == 0} {
+if {[array_in new_trusted_servers new_online_servers]} {
     pass "$test"
+} else {
+    fail "$test"
 }
 
 # The newly trusted servers represent the server we just started.
@@ -247,7 +279,7 @@ array unset current_signing_servers
 array set current_signing_servers [array get servers]
 
 set test "No new signing servers"
-if {[array_compare current_signing_servers existing_signing_servers]} {
+if {[array_equal current_signing_servers existing_signing_servers]} {
     pass "$test"
 } else {
     fail "$test"
@@ -287,7 +319,7 @@ array unset current_trusted_servers
 array set current_trusted_servers [array get servers]
 
 set test "No longer trusted after revokation by host name"
-if {[array_compare current_trusted_servers existing_trusted_servers]} {
+if {[array_equal current_trusted_servers existing_trusted_servers]} {
     pass "$test"
 } else {
     fail "$test"
@@ -320,54 +352,24 @@ list_servers "List current trusted servers after reinstatement by ip address" on
 array unset current_trusted_servers
 array set current_trusted_servers [array get servers]
 
+array_diff current_trusted_servers existing_trusted_servers
+array unset new_trusted_servers
+array set new_trusted_servers [array get diff]
+
 set test "New trusted servers after reinstatement by ip address"
-array unset new_trusted_servers
-set n 0
-foreach idx1 [array names current_trusted_servers] {
-    set found 0
-    foreach idx2 [array names existing_trusted_servers] {
-	if {"$existing_trusted_servers($idx2)" == "$current_trusted_servers($idx1)"} {
-	    set found 1
-	    break
-	}
-    }
-    if {$found == 0} {
-	set new_trusted_servers($n) "$current_trusted_servers($idx1)"
-	incr n
-    }
-}
-if {$n == 0} {
-    fail "$test"
-} else {
+if {[array size new_trusted_servers] > 0} {
     pass "$test"
+} else {
+    fail "$test"
 }
 
 # The new_trusted_servers array should now match the our_servers array, since
 # the our_servers array is a copy of the original new_trusted_servers array.
-set test "Number of new trusted servers matches after reinstatement by ip address"
-if {[array size new_trusted_servers] == [array size our_servers]} {
-    pass "$test"
-} else {
-    fail "$test"
-}
 set test "New trusted servers matches after reinstatement by ip address"
-set n 0
-foreach idx1 [array names new_trusted_servers] {
-    set found 0
-    foreach idx2 [array names our_servers] {
-	if {"$our_servers($idx2)" == "$new_trusted_servers($idx1)"} {
-	    set found 1
-	    break
-	}
-    }
-    if {$found == 1} {
-	incr n
-    }
-}
-if {$n != [array size new_trusted_servers]} {
-    fail "$test"
-} else {
+if {[array_equal new_trusted_servers our_servers]} {
     pass "$test"
+} else {
+    fail "$test"
 }
 
 # Trust our server as a module signer. This must be done as root. Specify
@@ -394,28 +396,18 @@ list_servers "List current online signing servers" online,signer
 array unset current_signing_servers
 array set current_signing_servers [array get servers]
 
+array_diff current_signing_servers existing_signing_servers
+array unset new_signing_servers
+array set new_signing_servers [array get diff]
+
 set test "New signing servers"
-set n 0
-foreach idx1 [array names current_signing_servers] {
-    set found 0
-    foreach idx2 [array names existing_signing_servers] {
-	if {"$existing_signing_servers($idx2)" == "$current_signing_servers($idx1)"} {
-	    set found 1
-	    break
-	}
-    }
-    if {$found == 0} {
-	set new_signing_servers($n) "$current_signing_servers($idx1)"
-	incr n
-    }
-}
 if {$effective_pid == 0} {
     setup_xfail *-*-*
 }
-if {$n == 0} {
-    fail "$test"
-} else {
+if {[array size new_signing_servers] > 0} {
     pass "$test"
+} else {
+    fail "$test"
 }
 
 # The new_signing_servers array should now match the our_servers array, since
@@ -428,26 +420,13 @@ if {$effective_pid == 0} {
 if {[array size new_signing_servers] == [array size our_servers]} {
     pass "$test"
     set test "New signing servers matches"
-    set n 0
-    foreach idx1 [array names new_signing_servers] {
-	set found 0
-	foreach idx2 [array names our_servers] {
-	    if {"$our_servers($idx2)" == "$new_signing_servers($idx1)"} {
-		set found 1
-		break
-	    }
-	}
-	if {$found == 1} {
-	    incr n
-	}
-    }
     if {$effective_pid == 0} {
 	setup_xfail *-*-*
     }
-    if {$n != [array size new_signing_servers]} {
-	fail "$test"
-    } else {
+    if {[array_equal new_signing_servers our_servers]} {
 	pass "$test"
+    } else {
+	fail "$test"
     }
 } else {
     fail "$test"
@@ -594,7 +573,7 @@ array unset current_signing_servers
 array set current_signing_servers [array get servers]
 
 set test "No longer trusted as a signer after revokation"
-if {[array_compare current_signing_servers existing_signing_servers]} {
+if {[array_in current_signing_servers existing_signing_servers]} {
     pass "$test"
 } else {
     fail "$test"
diff --git a/testsuite/systemtap.server/server_args.exp b/testsuite/systemtap.server/server_args.exp
index 2f5deed..91536a5 100644
--- a/testsuite/systemtap.server/server_args.exp
+++ b/testsuite/systemtap.server/server_args.exp
@@ -66,20 +66,23 @@ proc stap_direct_and_with_client {stap options} {
     verbose -log $res_stap_client
 
     # Now check the output
-    set skip 0
+    set skip_hostname_mode 0
     set n 0
     set expected [split $res_stap "\n"]
     set received [split $res_stap_client "\n"]
     foreach line $received {
-	# Instructed to skip a line?
-	if {$skip} {
-	    set skip [expr $skip - 1]
-	    verbose -log "skipping: $line"
-	    continue
+	# Instructed to skip hostnames?
+	if {$skip_hostname_mode} {
+	    if {[regexp {^  \S+$} $line]} {
+		verbose -log "skipping: $line"
+		continue
+	    } else {
+		set skip_hostname_mode 0
+	    }
 	}
 	# Ignore warnings about the domain name on the certificate not matching
 	if {[regexp {^WARNING: The domain name, [^,]*, does not match the DNS name\(s\) on the server certificate:} $line]} {
-	    set skip 1
+	    set skip_hostname_mode 1
 	    verbose -log "skipping: $line"
 	    continue
 	}
diff --git a/testsuite/systemtap.server/server_concurrency.exp b/testsuite/systemtap.server/server_concurrency.exp
index dbacb51..a31415b 100644
--- a/testsuite/systemtap.server/server_concurrency.exp
+++ b/testsuite/systemtap.server/server_concurrency.exp
@@ -26,7 +26,7 @@ if {! [setup_server --max-threads 6]} {
 set server_port 0
 set f [open $logfile]
 set matched 0
-verbose -log "Server ouput: "
+verbose -log "Server output: "
 while {1} {
     set line [gets $f]
     if {[eof $f]} {
@@ -34,7 +34,7 @@ while {1} {
         break
     }
     verbose -log "$line"
-    if { [regexp {^.*Using network port (\d*)$} $line matched server_port ] } {
+    if { [regexp {^.*Using network address .+:(\d+)$} $line matched server_port ] } {
       close $f
       break
     }
-- 
1.8.3.1