naming/endpoint: escape address field
'address' field of endpoint can be any string. Since we use '@' as a
separator, 'address' with '@' cause a parse failure.
This changes to escape '@' in 'address' with hex encoding.
Assume that we don allow '@' in 'prototol' name.
Change-Id: Ic72038034fc5e56a90ff0ecab364054f3068f3d8
diff --git a/profiles/internal/naming/endpoint.go b/profiles/internal/naming/endpoint.go
index 74b1b2b..5190d82 100644
--- a/profiles/internal/naming/endpoint.go
+++ b/profiles/internal/naming/endpoint.go
@@ -117,7 +117,10 @@
ep.Protocol = naming.UnknownProtocol
}
- ep.Address = parts[2]
+ var err error
+ if ep.Address, err = unescape(parts[2]); err != nil {
+ return fmt.Errorf("invalid address: %v", err)
+ }
if len(ep.Address) == 0 {
ep.Address = net.JoinHostPort("", "0")
}
@@ -126,7 +129,6 @@
return fmt.Errorf("invalid routing id: %v", err)
}
- var err error
if ep.IsMountTable, ep.IsLeaf, err = parseMountTableFlag(parts[4]); err != nil {
return fmt.Errorf("invalid mount table flag: %v", err)
}
@@ -162,7 +164,7 @@
}
blessings := strings.Join(ep.Blessings, blessingsSeparator)
return fmt.Sprintf("@5@%s@%s@%s@%s@%s@@",
- ep.Protocol, ep.Address, ep.RID, mt, blessings)
+ ep.Protocol, escape(ep.Address), ep.RID, mt, blessings)
}
}
@@ -207,3 +209,72 @@
func (a *addr) String() string {
return a.address
}
+
+func escape(s string) string {
+ if !strings.ContainsAny(s, "%@") {
+ return s
+ }
+ t := make([]byte, len(s)*3)
+ j := 0
+ for i := 0; i < len(s); i++ {
+ switch c := s[i]; c {
+ case '@', '%':
+ t[j] = '%'
+ t[j+1] = "0123456789ABCDEF"[c>>4]
+ t[j+2] = "0123456789ABCDEF"[c&15]
+ j += 3
+ default:
+ t[j] = c
+ j++
+ }
+ }
+ return string(t[:j])
+}
+
+func ishex(c byte) bool {
+ switch {
+ case '0' <= c && c <= '9':
+ return true
+ case 'a' <= c && c <= 'f':
+ return true
+ case 'A' <= c && c <= 'F':
+ return true
+ }
+ return false
+}
+
+func unhex(c byte) byte {
+ switch {
+ case '0' <= c && c <= '9':
+ return c - '0'
+ case 'a' <= c && c <= 'f':
+ return c - 'a' + 10
+ case 'A' <= c && c <= 'F':
+ return c - 'A' + 10
+ }
+ return 0
+}
+
+func unescape(s string) (string, error) {
+ if !strings.Contains(s, "%") {
+ return s, nil
+ }
+ t := make([]byte, len(s))
+ j := 0
+ for i := 0; i < len(s); {
+ switch s[i] {
+ case '%':
+ if len(s) <= i+2 || !ishex(s[i+1]) || !ishex(s[i+2]) {
+ return s, fmt.Errorf("invalid escape %q", s)
+ }
+ t[j] = unhex(s[i+1])<<4 | unhex(s[i+2])
+ j++
+ i += 3
+ default:
+ t[j] = s[i]
+ j++
+ i++
+ }
+ }
+ return string(t[:j]), nil
+}
diff --git a/profiles/internal/naming/endpoint_test.go b/profiles/internal/naming/endpoint_test.go
index 6c4fdb6..f1d6d56 100644
--- a/profiles/internal/naming/endpoint_test.go
+++ b/profiles/internal/naming/endpoint_test.go
@@ -92,7 +92,7 @@
}
ep, err := NewEndpoint(test.String)
if err != nil {
- t.Errorf("Test %d: Endpoint(%q) failed with %v", i, test.String, err)
+ t.Errorf("Test %d: NewEndpoint(%q) failed with %v", i, test.String, err)
continue
}
if !reflect.DeepEqual(ep, test.Endpoint) {
@@ -179,3 +179,25 @@
}
}
}
+
+func TestEscapeEndpoint(t *testing.T) {
+ defver := defaultVersion
+ defer func() {
+ defaultVersion = defver
+ }()
+ testcases := []naming.Endpoint{
+ &Endpoint{Protocol: "unix", Address: "@", RID: naming.FixedRoutingID(0xdabbad00)},
+ &Endpoint{Protocol: "unix", Address: "@/%", RID: naming.FixedRoutingID(0xdabbad00)},
+ }
+ for i, ep := range testcases {
+ epstr := ep.String()
+ got, err := NewEndpoint(epstr)
+ if err != nil {
+ t.Errorf("Test %d: NewEndpoint(%q) failed with %v", i, epstr, err)
+ continue
+ }
+ if !reflect.DeepEqual(ep, got) {
+ t.Errorf("Test %d: Got endpoint %#v, want %#v", i, got, ep)
+ }
+ }
+}