blob: 24e6d151651dc57733b38a4bebec79a5fe001397 [file] [log] [blame]
// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package remote implements an inprocess key server that uses RPC to
// connect to a remote key server.
package remote // import "upspin.io/key/remote"
import (
"fmt"
"upspin.io/bind"
"upspin.io/errors"
"upspin.io/key/usercache"
"upspin.io/log"
"upspin.io/rpc"
"upspin.io/upspin"
"upspin.io/upspin/proto"
)
// dialConfig contains the destination and authenticated user of the dial.
type dialConfig struct {
endpoint upspin.Endpoint
userName upspin.UserName
}
// remote implements upspin.KeyServer.
type remote struct {
rpc.Client // For sessions and Close.
cfg dialConfig
}
var _ upspin.KeyServer = (*remote)(nil)
// Lookup implements upspin.Key.Lookup.
func (r *remote) Lookup(name upspin.UserName) (*upspin.User, error) {
op := r.opf("Lookup", "%q", name)
// TODO(adg): don't send auth requests when performing lookups.
req := &proto.KeyLookupRequest{
UserName: string(name),
}
resp := new(proto.KeyLookupResponse)
if err := r.InvokeUnauthenticated("Key/Lookup", req, resp); err != nil {
return nil, op.error(err)
}
if len(resp.Error) != 0 {
return nil, op.error(errors.UnmarshalError(resp.Error))
}
return proto.UpspinUser(resp.User), nil
}
func userName(user *upspin.User) string {
if user == nil {
return "<nil>"
}
return fmt.Sprintf("%q", user.Name)
}
// Put implements upspin.Key.Put.
func (r *remote) Put(user *upspin.User) error {
op := r.opf("Put", "%v", userName(user))
req := &proto.KeyPutRequest{
User: proto.UserProto(user),
}
resp := new(proto.KeyPutResponse)
if err := r.Invoke("Key/Put", req, resp, nil, nil); err != nil {
return op.error(err)
}
if len(resp.Error) != 0 {
return op.error(errors.UnmarshalError(resp.Error))
}
return nil
}
// Endpoint implements upspin.StoreServer.Endpoint.
func (r *remote) Endpoint() upspin.Endpoint {
return r.cfg.endpoint
}
// Dial implements upspin.Service.
func (r *remote) Dial(config upspin.Config, e upspin.Endpoint) (upspin.Service, error) {
op := r.opf("Dial", "%q, %q", config.UserName(), e)
if e.Transport != upspin.Remote {
return nil, op.error(errors.Invalid, errors.Str("unrecognized transport"))
}
authClient, err := rpc.NewClient(config, e.NetAddr, rpc.Secure, upspin.Endpoint{})
if err != nil {
return nil, op.error(errors.IO, err)
}
return &remote{
Client: authClient,
cfg: dialConfig{
endpoint: e,
userName: config.UserName(),
},
}, nil
}
const transport = upspin.Remote
func init() {
r := &remote{} // uninitialized until Dial time.
bind.RegisterKeyServer(transport, usercache.Global(r))
}
func (r *remote) opf(method string, format string, args ...interface{}) *operation {
ep := r.cfg.endpoint.String()
s := fmt.Sprintf("key/remote: %q: key.%s", ep, method)
op := &operation{s, fmt.Sprintf(format, args...)}
log.Debug.Print(op)
return op
}
type operation struct {
op string
args string
}
func (op *operation) String() string {
return fmt.Sprintf("%s(%s)", op.op, op.args)
}
func (op *operation) error(args ...interface{}) error {
if len(args) == 0 {
panic("error called with zero args")
}
if len(args) == 1 {
if e, ok := args[0].(error); ok && e == upspin.ErrFollowLink {
return e
}
if args[0] == nil {
return nil
}
}
log.Debug.Printf("%v error: %v", op, errors.E(args...))
return errors.E(append([]interface{}{op.op}, args...)...)
}