Merge "security: Make Blessings a struct instead of an interface."
diff --git a/runtimes/google/ipc/stream/vc/vc_test.go b/runtimes/google/ipc/stream/vc/vc_test.go
index 7d5cf9d..5baf9d5 100644
--- a/runtimes/google/ipc/stream/vc/vc_test.go
+++ b/runtimes/google/ipc/stream/vc/vc_test.go
@@ -526,3 +526,4 @@
 func (e endpoint) RoutingID() naming.RoutingID { return naming.RoutingID(e) }
 func (e endpoint) Addr() net.Addr              { return nil }
 func (e endpoint) ServesMountTable() bool      { return false }
+func (e endpoint) BlessingNames() []string     { return nil }
diff --git a/runtimes/google/naming/endpoint.go b/runtimes/google/naming/endpoint.go
index daba3b1..1300684 100644
--- a/runtimes/google/naming/endpoint.go
+++ b/runtimes/google/naming/endpoint.go
@@ -4,6 +4,7 @@
 	"errors"
 	"fmt"
 	"net"
+	"regexp"
 	"strconv"
 	"strings"
 
@@ -12,11 +13,15 @@
 )
 
 const (
-	separator = "@"
-	suffix    = "@@"
+	separator          = "@"
+	suffix             = "@@"
+	blessingsSeparator = ","
 )
 
-var errInvalidEndpointString = errors.New("invalid endpoint string")
+var (
+	errInvalidEndpointString = errors.New("invalid endpoint string")
+	hostportEP               = regexp.MustCompile("^(?:(.*)@)?([^@]+)$")
+)
 
 // Network is the string returned by naming.Endpoint.Network implementations
 // defined in this package.
@@ -29,6 +34,7 @@
 	RID           naming.RoutingID
 	MinIPCVersion version.IPCVersion
 	MaxIPCVersion version.IPCVersion
+	Blessings     []string
 	IsMountTable  bool
 }
 
@@ -39,15 +45,19 @@
 	// We have to guess this is a mount table if we don't know.
 	ep.IsMountTable = true
 
-	// The prefix and suffix are optional.
-	input = strings.TrimPrefix(strings.TrimSuffix(input, suffix), separator)
-
-	parts := strings.Split(input, separator)
-	if len(parts) == 1 {
-		err := ep.parseHostPort(parts[0])
+	// If the endpoint does not end in a @, it must be in [blessing@]host:port format.
+	if parts := hostportEP.FindStringSubmatch(input); len(parts) > 0 {
+		hostport := parts[len(parts)-1]
+		var blessing string
+		if len(parts) > 2 {
+			blessing = parts[1]
+		}
+		err := ep.parseHostPort(blessing, hostport)
 		return ep, err
 	}
-
+	// Trim the prefix and suffix and parse the rest.
+	input = strings.TrimPrefix(strings.TrimSuffix(input, suffix), separator)
+	parts := strings.Split(input, separator)
 	version, err := strconv.ParseUint(parts[0], 10, 16)
 	if err != nil {
 		return nil, fmt.Errorf("invalid version: %v", err)
@@ -60,21 +70,25 @@
 		err = ep.parseV2(parts)
 	case 3:
 		err = ep.parseV3(parts)
+	case 4:
+		err = ep.parseV4(parts)
 	default:
 		err = errInvalidEndpointString
 	}
 	return ep, err
 }
 
-func (ep *Endpoint) parseHostPort(input string) error {
+func (ep *Endpoint) parseHostPort(blessing, hostport string) error {
 	// Could be in host:port format.
-	if _, _, err := net.SplitHostPort(input); err != nil {
+	if _, _, err := net.SplitHostPort(hostport); err != nil {
 		return errInvalidEndpointString
 	}
 	ep.Protocol = naming.UnknownProtocol
-	ep.Address = input
+	ep.Address = hostport
 	ep.RID = naming.NullRoutingID
-
+	if len(blessing) > 0 {
+		ep.Blessings = []string{blessing}
+	}
 	return nil
 }
 
@@ -166,6 +180,20 @@
 	return nil
 }
 
+func (ep *Endpoint) parseV4(parts []string) error {
+	if len(parts) < 7 {
+		return errInvalidEndpointString
+	}
+	if err := ep.parseV3(parts[:7]); err != nil {
+		return err
+	}
+	// Join the remaining and re-split.
+	if str := strings.Join(parts[7:], separator); len(str) > 0 {
+		ep.Blessings = strings.Split(str, blessingsSeparator)
+	}
+	return nil
+}
+
 func (ep *Endpoint) RoutingID() naming.RoutingID {
 	//nologcall
 	return ep.RID
@@ -175,7 +203,7 @@
 	return Network
 }
 
-var defaultVersion = 3
+var defaultVersion = 3 // TODO(ashankar): Change to 4?
 
 func (ep *Endpoint) VersionedString(version int) string {
 	switch version {
@@ -196,12 +224,27 @@
 			ep.Protocol, ep.Address, ep.RID,
 			printIPCVersion(ep.MinIPCVersion), printIPCVersion(ep.MaxIPCVersion),
 			mt)
+	case 4:
+		mt := "s"
+		blessings := strings.Join(ep.Blessings, blessingsSeparator)
+		if ep.IsMountTable {
+			mt = "m"
+		}
+		return fmt.Sprintf("@4@%s@%s@%s@%s@%s@%s@%s@@",
+			ep.Protocol, ep.Address, ep.RID,
+			printIPCVersion(ep.MinIPCVersion), printIPCVersion(ep.MaxIPCVersion),
+			mt, blessings)
 	}
 }
 
 func (ep *Endpoint) String() string {
 	//nologcall
-	return ep.VersionedString(defaultVersion)
+	// Use version 4 if blessings are present, otherwise there is a loss of information.
+	v := defaultVersion
+	if len(ep.Blessings) > 0 && v < 4 {
+		v = 4
+	}
+	return ep.VersionedString(v)
 }
 
 func (ep *Endpoint) Name() string {
@@ -219,6 +262,11 @@
 	return ep.IsMountTable
 }
 
+func (ep *Endpoint) BlessingNames() []string {
+	//nologcall
+	return ep.Blessings
+}
+
 type addr struct {
 	network, address string
 }
diff --git a/runtimes/google/naming/endpoint_test.go b/runtimes/google/naming/endpoint_test.go
index 303d3fe..de13f54 100644
--- a/runtimes/google/naming/endpoint_test.go
+++ b/runtimes/google/naming/endpoint_test.go
@@ -10,6 +10,10 @@
 )
 
 func TestEndpoint(t *testing.T) {
+	defver := defaultVersion
+	defer func() {
+		defaultVersion = defver
+	}()
 	v1 := &Endpoint{
 		Protocol:     naming.UnknownProtocol,
 		Address:      "batman.com:1234",
@@ -64,6 +68,25 @@
 		MaxIPCVersion: 3,
 		IsMountTable:  false,
 	}
+	v4 := &Endpoint{
+		Protocol:      "tcp",
+		Address:       "batman.com:2345",
+		RID:           naming.FixedRoutingID(0xba77),
+		MinIPCVersion: 4,
+		MaxIPCVersion: 5,
+		IsMountTable:  true,
+		Blessings:     []string{"dev.v.io/foo@bar.com", "dev.v.io/bar@bar.com/delegate"},
+	}
+	v4b := &Endpoint{
+		Protocol:      "tcp",
+		Address:       "batman.com:2345",
+		RID:           naming.FixedRoutingID(0xba77),
+		MinIPCVersion: 4,
+		MaxIPCVersion: 5,
+		IsMountTable:  true,
+		// Blessings that look similar to other parts of the endpoint.
+		Blessings: []string{"@@", "@s", "@m"},
+	}
 
 	testcasesA := []struct {
 		endpoint naming.Endpoint
@@ -80,42 +103,32 @@
 		}
 	}
 
-	// Test v3 endpoints.
-	defaultVersion = 3
+	// Test v3 & v4 endpoints.
 	testcasesC := []struct {
 		Endpoint naming.Endpoint
 		String   string
-		Input    string
-		min, max version.IPCVersion
-		servesMT bool
+		Version  int
 	}{
-		{v3s, "@3@@batman.com:2345@00000000000000000000000000000000@2@3@s@@", "", 2, 3, false},
-		{v3m, "@3@@batman.com:2345@000000000000000000000000dabbad00@2@3@m@@", "", 2, 3, true},
-		{v3tcp, "@3@tcp@batman.com:2345@00000000000000000000000000000000@2@3@s@@", "", 2, 3, false},
-		{v3ws6, "@3@ws6@batman.com:2345@00000000000000000000000000000000@2@3@s@@", "", 2, 3, false},
+		{v3s, "@3@@batman.com:2345@00000000000000000000000000000000@2@3@s@@", 3},
+		{v3m, "@3@@batman.com:2345@000000000000000000000000dabbad00@2@3@m@@", 3},
+		{v3tcp, "@3@tcp@batman.com:2345@00000000000000000000000000000000@2@3@s@@", 3},
+		{v3ws6, "@3@ws6@batman.com:2345@00000000000000000000000000000000@2@3@s@@", 3},
+		{v3s, "@4@@batman.com:2345@00000000000000000000000000000000@2@3@s@@@", 4},
+		{v4, "@4@tcp@batman.com:2345@0000000000000000000000000000ba77@4@5@m@dev.v.io/foo@bar.com,dev.v.io/bar@bar.com/delegate@@", 4},
+		{v4b, "@4@tcp@batman.com:2345@0000000000000000000000000000ba77@4@5@m@@@,@s,@m@@", 4},
 	}
 
 	for _, test := range testcasesC {
-		if got, want := test.Endpoint.String(), test.String; got != want {
-			t.Errorf("Got %q want %q for endpoint %T = %#v", got, want, test.Endpoint, test.Endpoint)
+		if got, want := test.Endpoint.VersionedString(test.Version), test.String; got != want {
+			t.Errorf("Got %q want %q for endpoint (v%d): %#v", got, want, test.Version, test.Endpoint)
 		}
-		str := test.Input
-		var ep naming.Endpoint
-		var err error
-		if str == "" {
-			str = test.String
-			ep, err = NewEndpoint(str)
-		} else {
-			ep, err = NewEndpoint(naming.FormatEndpoint("tcp", str,
-				version.IPCVersionRange{test.min, test.max},
-				naming.ServesMountTableOpt(test.servesMT)))
-		}
+		ep, err := NewEndpoint(test.String)
 		if err != nil {
-			t.Errorf("Endpoint(%q) failed with %v", str, err)
+			t.Errorf("Endpoint(%q) failed with %v", test.String, err)
 			continue
 		}
 		if !reflect.DeepEqual(ep, test.Endpoint) {
-			t.Errorf("Got endpoint %T = %#v, want %T = %#v for string %q", ep, ep, test.Endpoint, test.Endpoint, str)
+			t.Errorf("Got endpoint %#v, want %#v for string %q", ep, test.Endpoint, test.String)
 		}
 	}
 
@@ -156,7 +169,6 @@
 			t.Errorf("Got endpoint %T = %#v, want %T = %#v for string %q", ep, ep, test.Endpoint, test.Endpoint, str)
 		}
 	}
-	defaultVersion = 3
 }
 
 type endpointTest struct {
@@ -171,7 +183,6 @@
 		{"@1@@@@@", "@3@@:0@00000000000000000000000000000000@@@m@@", nil},
 		{"@2@@@@@@@", "@3@@:0@00000000000000000000000000000000@@@m@@", nil},
 		{"@1@tcp@batman:12@@@", "@3@tcp@batman:12@00000000000000000000000000000000@@@m@@", nil},
-		{"@host:10@@", "@3@@host:10@00000000000000000000000000000000@@@m@@", nil},
 		{"@2@tcp@foo:12@@9@@@", "@3@tcp@foo:12@00000000000000000000000000000000@9@@m@@", nil},
 		{"@2@tcp@foo:12@@@4@@", "@3@tcp@foo:12@00000000000000000000000000000000@@4@m@@", nil},
 		{"@2@tcp@foo:12@@2@4@@", "@3@tcp@foo:12@00000000000000000000000000000000@2@4@m@@", nil},
@@ -202,10 +213,18 @@
 }
 
 func TestHostPortEndpoint(t *testing.T) {
+	defver := defaultVersion
+	defer func() {
+		defaultVersion = defver
+	}()
+	defaultVersion = 4
 	testcases := []endpointTest{
-		{"localhost:10", "@3@@localhost:10@00000000000000000000000000000000@@@m@@", nil},
-		{"localhost:", "@3@@localhost:@00000000000000000000000000000000@@@m@@", nil},
+		{"localhost:10", "@4@@localhost:10@00000000000000000000000000000000@@@m@@@", nil},
+		{"localhost:", "@4@@localhost:@00000000000000000000000000000000@@@m@@@", nil},
 		{"localhost", "", errInvalidEndpointString},
+		{"dev.v.io/service/mounttabled@ns.dev.v.io:8101", "@4@@ns.dev.v.io:8101@00000000000000000000000000000000@@@m@dev.v.io/service/mounttabled@@", nil},
+		{"dev.v.io/users/foo@bar.com@ns.dev.v.io:8101", "@4@@ns.dev.v.io:8101@00000000000000000000000000000000@@@m@dev.v.io/users/foo@bar.com@@", nil},
+		{"@1@tcp@ns.dev.v.io:8101", "@4@@ns.dev.v.io:8101@00000000000000000000000000000000@@@m@@1@tcp@@", nil},
 	}
 	runEndpointTests(t, testcases)
 }
diff --git a/services/mounttable/lib/mounttable_test.go b/services/mounttable/lib/mounttable_test.go
index b61188f..bf0cb04 100644
--- a/services/mounttable/lib/mounttable_test.go
+++ b/services/mounttable/lib/mounttable_test.go
@@ -576,7 +576,6 @@
 
 	doMount(t, rootCtx, estr, "endpoint", naming.JoinAddressName(estr, "life/on/the/mississippi"), nil, true)
 	doMount(t, rootCtx, estr, "hostport", "/atrampabroad:8000", nil, true)
-	doMount(t, rootCtx, estr, "hostport-endpoint-platypus", "/@atrampabroad:8000@@", nil, true)
 	doMount(t, rootCtx, estr, "invalid/not/rooted", "atrampabroad:8000", nil, false)
 	doMount(t, rootCtx, estr, "invalid/no/port", "/atrampabroad", nil, false)
 	doMount(t, rootCtx, estr, "invalid/endpoint", "/@following the equator:8000@@@", nil, false)