| package ipc |
| |
| import ( |
| "strings" |
| "time" |
| |
| "v.io/core/veyron2/context" |
| "v.io/core/veyron2/ipc" |
| "v.io/core/veyron2/naming" |
| "v.io/core/veyron2/security" |
| "v.io/core/veyron2/services/security/access" |
| "v.io/core/veyron2/vdl/vdlroot/src/signature" |
| "v.io/core/veyron2/verror" |
| "v.io/core/veyron2/vlog" |
| |
| "v.io/core/veyron/lib/glob" |
| ) |
| |
| // TODO(toddw): Rename this file to "reserved.go". |
| |
| // 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.ReflectInvoker(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(ctxOrig ipc.ServerContext) ([]signature.Interface, error) { |
| // Copy the original context to shield ourselves from changes the flowServer makes. |
| ctx := copyMutableContext(ctxOrig) |
| ctx.M.Method = "__Signature" |
| disp := r.dispNormal |
| if naming.IsReserved(ctx.Suffix()) { |
| disp = r.dispReserved |
| } |
| if disp == nil { |
| return nil, verror.NoExistf("ipc: no dispatcher for %q.%s", ctx.Suffix(), ctx.Method()) |
| } |
| obj, auth, err := disp.Lookup(ctx.Suffix()) |
| switch { |
| case err != nil: |
| return nil, err |
| case obj == nil: |
| return nil, verror.NoExistf("ipc: no invoker for %q.%s", ctx.Suffix(), ctx.Method()) |
| } |
| if verr := authorize(ctx, auth); verr != nil { |
| return nil, verr |
| } |
| sig, err := objectToInvoker(obj).Signature(ctx) |
| if err != nil { |
| return nil, err |
| } |
| // TODO(toddw): add the signatures returned from reservedInvoker. |
| // TODO(toddw): filter based on authorization. |
| return sig, nil |
| } |
| |
| func (r *reservedMethods) MethodSignature(ctxOrig ipc.ServerContext, method string) (signature.Method, error) { |
| // Copy the original context to shield ourselves from changes the flowServer makes. |
| ctx := copyMutableContext(ctxOrig) |
| ctx.M.Method = method |
| // Reserved methods use our self invoker, to describe our own methods, |
| if naming.IsReserved(method) { |
| ctx.M.Method = naming.StripReserved(method) |
| return r.selfInvoker.MethodSignature(ctx, ctx.Method()) |
| } |
| disp := r.dispNormal |
| if naming.IsReserved(ctx.Suffix()) { |
| disp = r.dispReserved |
| } |
| if disp == nil { |
| return signature.Method{}, verror.NoExistf("ipc: no such method %q", ctx.Method()) |
| } |
| obj, auth, err := disp.Lookup(ctx.Suffix()) |
| switch { |
| case err != nil: |
| return signature.Method{}, err |
| case obj == nil: |
| return signature.Method{}, verror.NoExistf("ipc: no such method %q", ctx.Method()) |
| } |
| if verr := authorize(ctx, auth); verr != nil { |
| return signature.Method{}, verr |
| } |
| return objectToInvoker(obj).MethodSignature(ctx, ctx.Method()) |
| } |
| |
| func (r *reservedMethods) Glob(ctx ipc.ServerCall, pattern string) error { |
| // Copy the original call to shield ourselves from changes the flowServer makes. |
| glob := globInternal{r.dispNormal, r.dispReserved, ctx.Suffix()} |
| return glob.Glob(copyMutableCall(ctx), 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 *mutableCall, 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 = []interface{}{access.Resolve} |
| if naming.IsReserved(i.receiver) || (i.receiver == "" && naming.IsReserved(pattern)) { |
| disp = i.dispReserved |
| call.M.MethodTags = []interface{}{access.Debug} |
| } |
| if disp == nil { |
| return verror.NoExistf("ipc: Glob is not implemented by %q", i.receiver) |
| } |
| |
| type gState struct { |
| name string |
| glob *glob.Glob |
| depth int |
| } |
| queue := []gState{gState{glob: g}} |
| |
| 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()) |
| 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) |
| continue |
| } |
| if obj == nil { |
| vlog.VI(3).Infof("ipc Glob: object not found for %q", call.Suffix()) |
| continue |
| } |
| |
| // Verify that that requester is authorized for the current object. |
| if err := authorize(call, auth); err != nil { |
| 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. |
| gs := objectToInvoker(obj).Globber() |
| if gs == nil || (gs.AllGlobber == nil && gs.ChildrenGlobber == nil) { |
| if state.glob.Len() == 0 { |
| call.Send(naming.VDLMountEntry{Name: state.name}) |
| } |
| 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) |
| continue |
| } |
| if ch == nil { |
| continue |
| } |
| for me := range ch { |
| me.Name = naming.Join(state.name, me.Name) |
| call.Send(me) |
| } |
| 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 { |
| continue |
| } |
| // The glob pattern matches the current object. |
| if state.glob.Len() == 0 { |
| call.Send(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}) |
| } |
| } |
| } |
| return nil |
| } |
| |
| // copyMutableCall returns a new mutableCall copied from call. Changes to the |
| // original call don't affect the mutable fields in the returned object. |
| func copyMutableCall(call ipc.ServerCall) *mutableCall { |
| return &mutableCall{Stream: call, mutableContext: copyMutableContext(call)} |
| } |
| |
| // copyMutableContext returns a new mutableContext copied from ctx. Changes to |
| // the original ctx don't affect the mutable fields in the returned object. |
| func copyMutableContext(ctx ipc.ServerContext) *mutableContext { |
| c := &mutableContext{T: ctx.Context()} |
| c.M.ContextParams.Copy(ctx) |
| c.M.Blessings = ctx.Blessings() |
| c.M.Server = ctx.Server() |
| return c |
| } |
| |
| // mutableCall provides a mutable implementation of ipc.ServerCall, useful for |
| // our various special-cased reserved methods. |
| type mutableCall struct { |
| ipc.Stream |
| *mutableContext |
| } |
| |
| // mutableContext is like mutableCall but only provides the context portion. |
| type mutableContext struct { |
| *context.T |
| M struct { |
| security.ContextParams |
| Blessings security.Blessings |
| Server ipc.Server |
| } |
| } |
| |
| func (c *mutableContext) Context() *context.T { return c.T } |
| func (c *mutableContext) Timestamp() time.Time { return c.M.Timestamp } |
| func (c *mutableContext) Method() string { return c.M.Method } |
| func (c *mutableContext) MethodTags() []interface{} { return c.M.MethodTags } |
| func (c *mutableContext) Name() string { return c.M.Suffix } |
| func (c *mutableContext) Suffix() string { return c.M.Suffix } |
| func (c *mutableContext) LocalPrincipal() security.Principal { return c.M.LocalPrincipal } |
| func (c *mutableContext) LocalBlessings() security.Blessings { return c.M.LocalBlessings } |
| func (c *mutableContext) RemoteBlessings() security.Blessings { return c.M.RemoteBlessings } |
| func (c *mutableContext) LocalEndpoint() naming.Endpoint { return c.M.LocalEndpoint } |
| func (c *mutableContext) RemoteEndpoint() naming.Endpoint { return c.M.RemoteEndpoint } |
| func (c *mutableContext) RemoteDischarges() map[string]security.Discharge { return c.M.RemoteDischarges } |
| func (c *mutableContext) Blessings() security.Blessings { return c.M.Blessings } |
| func (c *mutableContext) Server() ipc.Server { return c.M.Server } |