blob: 34579fa964ba84899a0850f0191ae61151879606 [file] [log] [blame]
package ipc
import (
"strings"
"time"
"v.io/v23/context"
"v.io/v23/ipc"
"v.io/v23/ipc/reserved"
"v.io/v23/naming"
"v.io/v23/security"
"v.io/v23/services/security/access"
"v.io/v23/vdl"
"v.io/v23/vdlroot/signature"
"v.io/v23/verror"
"v.io/x/lib/vlog"
"v.io/x/ref/lib/glob"
)
// reservedInvoker returns a special invoker for reserved methods. This invoker
// has access to the internal dispatchers, which allows it to perform special
// handling for methods like Glob and Signature.
func reservedInvoker(dispNormal, dispReserved ipc.Dispatcher) ipc.Invoker {
methods := &reservedMethods{dispNormal: dispNormal, dispReserved: dispReserved}
invoker := ipc.ReflectInvokerOrDie(methods)
methods.selfInvoker = invoker
return invoker
}
// reservedMethods is a regular server implementation object, which is passed to
// the regular ReflectInvoker in order to implement reserved methods. The
// leading reserved "__" prefix is stripped before any methods are called.
//
// To add a new reserved method, simply add a method below, along with a
// description of the method.
type reservedMethods struct {
dispNormal ipc.Dispatcher
dispReserved ipc.Dispatcher
selfInvoker ipc.Invoker
}
func (r *reservedMethods) Describe__() []ipc.InterfaceDesc {
return []ipc.InterfaceDesc{{
Name: "__Reserved",
Doc: `Reserved methods implemented by the IPC framework. Each method name is prefixed with a double underscore "__".`,
Methods: []ipc.MethodDesc{
{
Name: "Glob",
Doc: "Glob returns all entries matching the pattern.",
InArgs: []ipc.ArgDesc{{Name: "pattern", Doc: ""}},
OutStream: ipc.ArgDesc{Doc: "Streams matching entries back to the client."},
},
{
Name: "MethodSignature",
Doc: "MethodSignature returns the signature for the given method.",
InArgs: []ipc.ArgDesc{{
Name: "method",
Doc: "Method name whose signature will be returned.",
}},
OutArgs: []ipc.ArgDesc{{
Doc: "Method signature for the given method.",
}},
},
{
Name: "Signature",
Doc: "Signature returns all interface signatures implemented by the object.",
OutArgs: []ipc.ArgDesc{{
Doc: "All interface signatures implemented by the object.",
}},
},
},
}}
}
func (r *reservedMethods) Signature(callOrig ipc.ServerCall) ([]signature.Interface, error) {
// Copy the original context to shield ourselves from changes the flowServer makes.
call := copyMutableServerCall(callOrig)
call.M.Method = "__Signature"
disp := r.dispNormal
if naming.IsReserved(call.Suffix()) {
disp = r.dispReserved
}
if disp == nil {
return nil, ipc.NewErrUnknownSuffix(call.Context(), call.Suffix())
}
obj, _, err := disp.Lookup(call.Suffix())
switch {
case err != nil:
return nil, err
case obj == nil:
return nil, ipc.NewErrUnknownSuffix(call.Context(), call.Suffix())
}
invoker, err := objectToInvoker(obj)
if err != nil {
return nil, err
}
sig, err := invoker.Signature(call)
if err != nil {
return nil, err
}
// Append the reserved methods. We wait until now to add the "__" prefix to
// each method, so that we can use the regular ReflectInvoker.Signature logic.
rsig, err := r.selfInvoker.Signature(call)
if err != nil {
return nil, err
}
for i := range rsig {
for j := range rsig[i].Methods {
rsig[i].Methods[j].Name = "__" + rsig[i].Methods[j].Name
}
}
return signature.CleanInterfaces(append(sig, rsig...)), nil
}
func (r *reservedMethods) MethodSignature(callOrig ipc.ServerCall, method string) (signature.Method, error) {
// Copy the original context to shield ourselves from changes the flowServer makes.
call := copyMutableServerCall(callOrig)
call.M.Method = method
// Reserved methods use our self invoker, to describe our own methods,
if naming.IsReserved(method) {
call.M.Method = naming.StripReserved(method)
return r.selfInvoker.MethodSignature(call, call.Method())
}
disp := r.dispNormal
if naming.IsReserved(call.Suffix()) {
disp = r.dispReserved
}
if disp == nil {
return signature.Method{}, ipc.NewErrUnknownMethod(call.Context(), call.Method())
}
obj, _, err := disp.Lookup(call.Suffix())
switch {
case err != nil:
return signature.Method{}, err
case obj == nil:
return signature.Method{}, ipc.NewErrUnknownMethod(call.Context(), call.Method())
}
invoker, err := objectToInvoker(obj)
if err != nil {
return signature.Method{}, err
}
// TODO(toddw): Decide if we should hide the method signature if the
// caller doesn't have access to call it.
return invoker.MethodSignature(call, call.Method())
}
func (r *reservedMethods) Glob(call ipc.StreamServerCall, pattern string) error {
// Copy the original call to shield ourselves from changes the flowServer makes.
glob := globInternal{r.dispNormal, r.dispReserved, call.Suffix()}
return glob.Glob(copyMutableStreamServerCall(call), pattern)
}
// globInternal handles ALL the Glob requests received by a server and
// constructs a response from the state of internal server objects and the
// service objects.
//
// Internal objects exist only at the root of the server and have a name that
// starts with a double underscore ("__"). They are only visible in the Glob
// response if the double underscore is explicitly part of the pattern, e.g.
// "".Glob("__*/*"), or "".Glob("__debug/...").
//
// Service objects may choose to implement either AllGlobber or ChildrenGlobber.
// AllGlobber is more flexible, but ChildrenGlobber is simpler to implement and
// less prone to errors.
//
// If objects implement AllGlobber, it must be able to handle recursive pattern
// for the entire namespace below the receiver object, i.e. "a/b".Glob("...")
// must return the name of all the objects under "a/b".
//
// If they implement ChildrenGlobber, it provides a list of the receiver's
// immediate children names, or a non-nil error if the receiver doesn't exist.
//
// globInternal constructs the Glob response by internally accessing the
// AllGlobber or ChildrenGlobber interface of objects as many times as needed.
//
// Before accessing an object, globInternal ensures that the requester is
// authorized to access it. Internal objects require access.Debug. Service
// objects require access.Resolve.
type globInternal struct {
dispNormal ipc.Dispatcher
dispReserved ipc.Dispatcher
receiver string
}
// The maximum depth of recursion in Glob. We only count recursion levels
// associated with a recursive glob pattern, e.g. a pattern like "..." will be
// allowed to recurse up to 10 levels, but "*/*/*/*/..." will go up to 14
// levels.
const maxRecursiveGlobDepth = 10
func (i *globInternal) Glob(call *mutableStreamServerCall, pattern string) error {
vlog.VI(3).Infof("ipc Glob: Incoming request: %q.Glob(%q)", i.receiver, pattern)
g, err := glob.Parse(pattern)
if err != nil {
return err
}
disp := i.dispNormal
call.M.Method = ipc.GlobMethod
call.M.MethodTags = []*vdl.Value{vdl.ValueOf(access.Resolve)}
if naming.IsReserved(i.receiver) || (i.receiver == "" && naming.IsReserved(pattern)) {
disp = i.dispReserved
call.M.MethodTags = []*vdl.Value{vdl.ValueOf(access.Debug)}
}
if disp == nil {
return reserved.NewErrGlobNotImplemented(call.Context())
}
type gState struct {
name string
glob *glob.Glob
depth int
}
queue := []gState{gState{glob: g}}
someMatchesOmitted := false
for len(queue) != 0 {
select {
case <-call.Done():
// RPC timed out or was canceled.
return nil
default:
}
state := queue[0]
queue = queue[1:]
call.M.Suffix = naming.Join(i.receiver, state.name)
if state.depth > maxRecursiveGlobDepth {
vlog.Errorf("ipc Glob: exceeded recursion limit (%d): %q", maxRecursiveGlobDepth, call.Suffix())
call.Send(naming.VDLGlobReplyError{
naming.GlobError{Name: state.name, Error: reserved.NewErrGlobMaxRecursionReached(call.Context())},
})
continue
}
obj, auth, err := disp.Lookup(call.Suffix())
if err != nil {
vlog.VI(3).Infof("ipc Glob: Lookup failed for %q: %v", call.Suffix(), err)
call.Send(naming.VDLGlobReplyError{
naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrNoExist, call.Context(), err)},
})
continue
}
if obj == nil {
vlog.VI(3).Infof("ipc Glob: object not found for %q", call.Suffix())
call.Send(naming.VDLGlobReplyError{
naming.GlobError{Name: state.name, Error: verror.New(verror.ErrNoExist, call.Context(), "nil object")},
})
continue
}
// Verify that that requester is authorized for the current object.
if err := authorize(call, auth); err != nil {
someMatchesOmitted = true
vlog.VI(3).Infof("ipc Glob: client is not authorized for %q: %v", call.Suffix(), err)
continue
}
// If the object implements both AllGlobber and ChildrenGlobber, we'll
// use AllGlobber.
invoker, err := objectToInvoker(obj)
if err != nil {
vlog.VI(3).Infof("ipc Glob: object for %q cannot be converted to invoker: %v", call.Suffix(), err)
call.Send(naming.VDLGlobReplyError{
naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, call.Context(), err)},
})
continue
}
gs := invoker.Globber()
if gs == nil || (gs.AllGlobber == nil && gs.ChildrenGlobber == nil) {
if state.glob.Len() == 0 {
call.Send(naming.VDLGlobReplyEntry{naming.VDLMountEntry{Name: state.name}})
} else {
call.Send(naming.VDLGlobReplyError{
naming.GlobError{Name: state.name, Error: reserved.NewErrGlobNotImplemented(call.Context())},
})
}
continue
}
if gs.AllGlobber != nil {
vlog.VI(3).Infof("ipc Glob: %q implements AllGlobber", call.Suffix())
ch, err := gs.AllGlobber.Glob__(call, state.glob.String())
if err != nil {
vlog.VI(3).Infof("ipc Glob: %q.Glob(%q) failed: %v", call.Suffix(), state.glob, err)
call.Send(naming.VDLGlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, call.Context(), err)}})
continue
}
if ch == nil {
continue
}
for gr := range ch {
switch v := gr.(type) {
case naming.VDLGlobReplyEntry:
v.Value.Name = naming.Join(state.name, v.Value.Name)
call.Send(v)
case naming.VDLGlobReplyError:
v.Value.Name = naming.Join(state.name, v.Value.Name)
call.Send(v)
}
}
continue
}
vlog.VI(3).Infof("ipc Glob: %q implements ChildrenGlobber", call.Suffix())
children, err := gs.ChildrenGlobber.GlobChildren__(call)
// The requested object doesn't exist.
if err != nil {
call.Send(naming.VDLGlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, call.Context(), err)}})
continue
}
// The glob pattern matches the current object.
if state.glob.Len() == 0 {
call.Send(naming.VDLGlobReplyEntry{naming.VDLMountEntry{Name: state.name}})
}
// The current object has no children.
if children == nil {
continue
}
depth := state.depth
// This is a recursive pattern. Make sure we don't recurse forever.
if state.glob.Len() == 0 {
depth++
}
for child := range children {
if len(child) == 0 || strings.Contains(child, "/") {
vlog.Errorf("ipc Glob: %q.GlobChildren__() sent an invalid child name: %q", call.Suffix(), child)
continue
}
if ok, _, left := state.glob.MatchInitialSegment(child); ok {
next := naming.Join(state.name, child)
queue = append(queue, gState{next, left, depth})
}
}
}
if someMatchesOmitted {
call.Send(naming.VDLGlobReplyError{naming.GlobError{Error: reserved.NewErrGlobMatchesOmitted(call.Context())}})
}
return nil
}
// copyMutableStreamServerCall returns a new mutableStreamServerCall copied from call.
// Changes to the original call don't affect the mutable fields in the returned object.
func copyMutableStreamServerCall(call ipc.StreamServerCall) *mutableStreamServerCall {
return &mutableStreamServerCall{Stream: call, mutableServerCall: copyMutableServerCall(call)}
}
// copyMutableServerCall returns a new mutableServerCall copied from call. Changes to
// the original call don't affect the mutable fields in the returned object.
func copyMutableServerCall(call ipc.ServerCall) *mutableServerCall {
c := &mutableServerCall{T: call.Context()}
c.M.CallParams.Copy(call)
c.M.GrantedBlessings = call.GrantedBlessings()
c.M.Server = call.Server()
return c
}
// mutableStreamServerCall provides a mutable implementation of ipc.StreamServerCall,
// useful for our various special-cased reserved methods.
type mutableStreamServerCall struct {
ipc.Stream
*mutableServerCall
}
// mutableServerCall is like mutableStreamServerCall but only provides the ServerCall portion.
type mutableServerCall struct {
*context.T
M struct {
security.CallParams
GrantedBlessings security.Blessings
Server ipc.Server
}
}
func (c *mutableServerCall) Context() *context.T { return c.T }
func (c *mutableServerCall) Timestamp() time.Time { return c.M.Timestamp }
func (c *mutableServerCall) Method() string { return c.M.Method }
func (c *mutableServerCall) MethodTags() []*vdl.Value { return c.M.MethodTags }
func (c *mutableServerCall) Name() string { return c.M.Suffix }
func (c *mutableServerCall) Suffix() string { return c.M.Suffix }
func (c *mutableServerCall) LocalPrincipal() security.Principal { return c.M.LocalPrincipal }
func (c *mutableServerCall) LocalBlessings() security.Blessings { return c.M.LocalBlessings }
func (c *mutableServerCall) RemoteBlessings() security.Blessings { return c.M.RemoteBlessings }
func (c *mutableServerCall) LocalEndpoint() naming.Endpoint { return c.M.LocalEndpoint }
func (c *mutableServerCall) RemoteEndpoint() naming.Endpoint { return c.M.RemoteEndpoint }
func (c *mutableServerCall) LocalDischarges() map[string]security.Discharge {
return c.M.LocalDischarges
}
func (c *mutableServerCall) RemoteDischarges() map[string]security.Discharge {
return c.M.RemoteDischarges
}
func (c *mutableServerCall) GrantedBlessings() security.Blessings { return c.M.GrantedBlessings }
func (c *mutableServerCall) Server() ipc.Server { return c.M.Server }
func (c *mutableServerCall) VanadiumContext() *context.T { return c.T }