rpc: Ensure status.Endpoints[0] is not an IPv6 endpoint if possible.

https://vanadium-review.googlesource.com/#/c/22654/ and
https://vanadium-review.googlesource.com/#/c/22657/
intentionally removed code that filtered out IPv6 addresses when
possible because we intend for devices that can communicate to do so,
and hiding addresses is problematic for that.

However, it seems that those changes caused continuous integration
test failures when the tests were run in Kubernets on Google Compute
Engine (GCE). For example, you would see error messages like:

dial tcp [fe80::42:aff:fe44:806]:55852: connect: invalid argument]

which is because GCE doesn't support IPv6 addresses
(https://cloud.google.com/compute/docs/networking)
but Kubernetes containers seem to contain interfaces with
the IPv6 link-local address, causing confusion.

This behavior occurs because various tests dial a single
endpoint for the server (rpc.Server.Status().Endpoints[0]).
The hack fix for now is to always re-order Endpoints so that
the first one isn't an IPv6 one if possible.

Change-Id: Ia07c95e9bfc5d6698eff84e7b7e7e27b4be018b3
diff --git a/runtime/internal/rpc/server.go b/runtime/internal/rpc/server.go
index 0f2ed8b..eb226a4 100644
--- a/runtime/internal/rpc/server.go
+++ b/runtime/internal/rpc/server.go
@@ -7,6 +7,7 @@
 import (
 	"fmt"
 	"io"
+	"net"
 	"reflect"
 	"strings"
 	"sync"
@@ -298,6 +299,24 @@
 	for _, e := range s.endpoints {
 		status.Endpoints = append(status.Endpoints, e)
 	}
+	// HACK ALERT: Many tests seem to just pick out Endpoints[0] as the
+	// address of the server.  Furthermore, many tests run on Kubernetes
+	// inside Google Compute Engine (GCE). While GCE doesn't currently
+	// support IPv6 (as per
+	// https://cloud.google.com/compute/docs/networking), the containers
+	// created by kubernetes do show a link-local IPv6 address on the
+	// interfaces.
+	//
+	// Long story short, as a result of this, net.Dial() calls seem to
+	// fail.  For now, hack around this by ensuring that
+	// status.Endpoints[0] does not correspond to an IPv6 address.
+	for i, ep := range status.Endpoints {
+		if i > 0 && !mayBeIPv6(ep) {
+			status.Endpoints[0], status.Endpoints[i] = status.Endpoints[i], status.Endpoints[0]
+			break
+		}
+	}
+
 	mgrStat := s.flowMgr.Status()
 	status.ListenErrors = mgrStat.ListenErrors
 	status.ProxyErrors = mgrStat.ProxyErrors
@@ -980,3 +999,15 @@
 	stats.NewStringFunc(naming.Join(prefix, "proxy-errors"), func() string { return fmt.Sprint(s.Status().ProxyErrors) })
 	stats.NewStringFunc(naming.Join(prefix, "listen-errors"), func() string { return fmt.Sprint(s.Status().ListenErrors) })
 }
+
+func mayBeIPv6(ep naming.Endpoint) bool {
+	// Ignore the protocol because the set of protocols to test for isn't
+	// clear (tcp, wsh, vine, udp?) and false positives for this function
+	// aren't really troublesome.
+	host, _, err := net.SplitHostPort(ep.Addr().String())
+	if err != nil {
+		return false
+	}
+	ip := net.ParseIP(host)
+	return ip != nil && ip.To4() == nil
+}