Reimplement AddPeer and RemovePeer for admin socket (#951)

* Reimplement AddPeer and RemovePeer for admin socket

Fix #950

* Disconnect the peer on `removePeer`

Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
This commit is contained in:
ehmry 2022-10-02 06:35:43 -05:00 committed by GitHub
parent c922eba2d8
commit 7db934488e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 97 additions and 73 deletions

12
src/admin/addpeer.go Normal file
View File

@ -0,0 +1,12 @@
package admin
type AddPeerRequest struct {
Uri string `json:"uri"`
Sintf string `json:"interface,omitempty"`
}
type AddPeerResponse struct{}
func (a *AdminSocket) addPeerHandler(req *AddPeerRequest, res *AddPeerResponse) error {
return a.core.AddPeer(req.Uri, req.Sintf)
}

View File

@ -174,6 +174,34 @@ func (a *AdminSocket) SetupAdminHandlers() {
return res, nil return res, nil
}, },
) )
_ = a.AddHandler(
"addPeer", "Add a peer to the peer list", []string{"uri", "interface"},
func(in json.RawMessage) (interface{}, error) {
req := &AddPeerRequest{}
res := &AddPeerResponse{}
if err := json.Unmarshal(in, &req); err != nil {
return nil, err
}
if err := a.addPeerHandler(req, res); err != nil {
return nil, err
}
return res, nil
},
)
_ = a.AddHandler(
"removePeer", "Remove a peer from the peer list", []string{"uri", "interface"},
func(in json.RawMessage) (interface{}, error) {
req := &RemovePeerRequest{}
res := &RemovePeerResponse{}
if err := json.Unmarshal(in, &req); err != nil {
return nil, err
}
if err := a.removePeerHandler(req, res); err != nil {
return nil, err
}
return res, nil
},
)
//_ = a.AddHandler("getNodeInfo", []string{"key"}, t.proto.nodeinfo.nodeInfoAdminHandler) //_ = a.AddHandler("getNodeInfo", []string{"key"}, t.proto.nodeinfo.nodeInfoAdminHandler)
//_ = a.AddHandler("debug_remoteGetSelf", []string{"key"}, t.proto.getSelfHandler) //_ = a.AddHandler("debug_remoteGetSelf", []string{"key"}, t.proto.getSelfHandler)
//_ = a.AddHandler("debug_remoteGetPeers", []string{"key"}, t.proto.getPeersHandler) //_ = a.AddHandler("debug_remoteGetPeers", []string{"key"}, t.proto.getPeersHandler)

12
src/admin/removepeer.go Normal file
View File

@ -0,0 +1,12 @@
package admin
type RemovePeerRequest struct {
Uri string `json:"uri"`
Sintf string `json:"interface,omitempty"`
}
type RemovePeerResponse struct{}
func (a *AdminSocket) removePeerHandler(req *RemovePeerRequest, res *RemovePeerResponse) error {
return a.core.RemovePeer(req.Uri, req.Sintf)
}

View File

@ -181,78 +181,49 @@ func (c *Core) SetLogger(log util.Logger) {
} }
// AddPeer adds a peer. This should be specified in the peer URI format, e.g.: // AddPeer adds a peer. This should be specified in the peer URI format, e.g.:
// tcp://a.b.c.d:e //
// socks://a.b.c.d:e/f.g.h.i:j // tcp://a.b.c.d:e
// socks://a.b.c.d:e/f.g.h.i:j
//
// This adds the peer to the peer list, so that they will be called again if the // This adds the peer to the peer list, so that they will be called again if the
// connection drops. // connection drops.
/* func (c *Core) AddPeer(uri string, sourceInterface string) error {
func (c *Core) AddPeer(addr string, sintf string) error { u, err := url.Parse(uri)
if err := c.CallPeer(addr, sintf); err != nil { if err != nil {
// TODO: We maybe want this to write the peer to the persistent
// configuration even if a connection attempt fails, but first we'll need to
// move the code to check the peer URI so that we don't deliberately save a
// peer with a known bad URI. Loading peers from config should really do the
// same thing too but I don't think that happens today
return err return err
} }
c.config.Mutex.Lock() info, err := c.links.call(u, sourceInterface)
defer c.config.Mutex.Unlock() if err != nil {
if sintf == "" { return err
for _, peer := range c.config.Current.Peers {
if peer == addr {
return errors.New("peer already added")
}
}
c.config.Current.Peers = append(c.config.Current.Peers, addr)
} else {
if _, ok := c.config.Current.InterfacePeers[sintf]; ok {
for _, peer := range c.config.Current.InterfacePeers[sintf] {
if peer == addr {
return errors.New("peer already added")
}
}
}
if _, ok := c.config.Current.InterfacePeers[sintf]; !ok {
c.config.Current.InterfacePeers[sintf] = []string{addr}
} else {
c.config.Current.InterfacePeers[sintf] = append(c.config.Current.InterfacePeers[sintf], addr)
}
} }
return nil phony.Block(c, func() {
} c.config._peers[Peer{uri, sourceInterface}] = &info
*/
/*
func (c *Core) RemovePeer(addr string, sintf string) error {
if sintf == "" {
for i, peer := range c.config.Current.Peers {
if peer == addr {
c.config.Current.Peers = append(c.config.Current.Peers[:i], c.config.Current.Peers[i+1:]...)
break
}
}
} else if _, ok := c.config.Current.InterfacePeers[sintf]; ok {
for i, peer := range c.config.Current.InterfacePeers[sintf] {
if peer == addr {
c.config.Current.InterfacePeers[sintf] = append(c.config.Current.InterfacePeers[sintf][:i], c.config.Current.InterfacePeers[sintf][i+1:]...)
break
}
}
}
panic("TODO") // Get the net.Conn to this peer (if any) and close it
c.peers.Act(nil, func() {
ports := c.peers.ports
for _, peer := range ports {
if addr == peer.intf.name() {
c.peers._removePeer(peer)
}
}
}) })
return nil return nil
} }
*/
// RemovePeer removes a peer. The peer should be specified in URI format, see AddPeer.
// The peer is not disconnected immediately.
func (c *Core) RemovePeer(uri string, sourceInterface string) error {
var err error
phony.Block(c, func() {
peer := Peer{uri, sourceInterface}
linkInfo, ok := c.config._peers[peer]
if !ok {
err = fmt.Errorf("peer not configured")
return
}
if ok && linkInfo != nil {
c.links.Act(nil, func() {
if link := c.links._links[*linkInfo]; link != nil {
_ = link.close()
}
})
}
delete(c.config._peers, peer)
})
return err
}
// CallPeer calls a peer once. This should be specified in the peer URI format, // CallPeer calls a peer once. This should be specified in the peer URI format,
// e.g.: // e.g.:
@ -263,7 +234,8 @@ func (c *Core) RemovePeer(addr string, sintf string) error {
// This does not add the peer to the peer list, so if the connection drops, the // This does not add the peer to the peer list, so if the connection drops, the
// peer will not be called again automatically. // peer will not be called again automatically.
func (c *Core) CallPeer(u *url.URL, sintf string) error { func (c *Core) CallPeer(u *url.URL, sintf string) error {
return c.links.call(u, sintf) _, err := c.links.call(u, sintf)
return err
} }
func (c *Core) PublicKey() ed25519.PublicKey { func (c *Core) PublicKey() ed25519.PublicKey {

View File

@ -36,7 +36,7 @@ type Core struct {
log util.Logger log util.Logger
addPeerTimer *time.Timer addPeerTimer *time.Timer
config struct { config struct {
_peers map[Peer]struct{} // configurable after startup _peers map[Peer]*linkInfo // configurable after startup
_listeners map[ListenAddress]struct{} // configurable after startup _listeners map[ListenAddress]struct{} // configurable after startup
nodeinfo NodeInfo // immutable after startup nodeinfo NodeInfo // immutable after startup
nodeinfoPrivacy NodeInfoPrivacy // immutable after startup nodeinfoPrivacy NodeInfoPrivacy // immutable after startup
@ -66,7 +66,7 @@ func New(secret ed25519.PrivateKey, logger util.Logger, opts ...SetupOption) (*C
if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil { if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil {
return nil, fmt.Errorf("error creating encryption: %w", err) return nil, fmt.Errorf("error creating encryption: %w", err)
} }
c.config._peers = map[Peer]struct{}{} c.config._peers = map[Peer]*linkInfo{}
c.config._listeners = map[ListenAddress]struct{}{} c.config._listeners = map[ListenAddress]struct{}{}
c.config._allowedPublicKeys = map[[32]byte]struct{}{} c.config._allowedPublicKeys = map[[32]byte]struct{}{}
for _, opt := range opts { for _, opt := range opts {

View File

@ -108,10 +108,10 @@ func (l *links) isConnectedTo(info linkInfo) bool {
return isConnected return isConnected
} }
func (l *links) call(u *url.URL, sintf string) error { func (l *links) call(u *url.URL, sintf string) (linkInfo, error) {
info := linkInfoFor(u.Scheme, sintf, u.Host) info := linkInfoFor(u.Scheme, sintf, u.Host)
if l.isConnectedTo(info) { if l.isConnectedTo(info) {
return nil return info, nil
} }
options := linkOptions{ options := linkOptions{
pinnedEd25519Keys: map[keyArray]struct{}{}, pinnedEd25519Keys: map[keyArray]struct{}{},
@ -119,7 +119,7 @@ func (l *links) call(u *url.URL, sintf string) error {
for _, pubkey := range u.Query()["key"] { for _, pubkey := range u.Query()["key"] {
sigPub, err := hex.DecodeString(pubkey) sigPub, err := hex.DecodeString(pubkey)
if err != nil { if err != nil {
return fmt.Errorf("pinned key contains invalid hex characters") return info, fmt.Errorf("pinned key contains invalid hex characters")
} }
var sigPubKey keyArray var sigPubKey keyArray
copy(sigPubKey[:], sigPub) copy(sigPubKey[:], sigPub)
@ -172,9 +172,9 @@ func (l *links) call(u *url.URL, sintf string) error {
}() }()
default: default:
return errors.New("unknown call scheme: " + u.Scheme) return info, errors.New("unknown call scheme: " + u.Scheme)
} }
return nil return info, nil
} }
func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {

View File

@ -7,7 +7,7 @@ import (
func (c *Core) _applyOption(opt SetupOption) { func (c *Core) _applyOption(opt SetupOption) {
switch v := opt.(type) { switch v := opt.(type) {
case Peer: case Peer:
c.config._peers[v] = struct{}{} c.config._peers[v] = nil
case ListenAddress: case ListenAddress:
c.config._listeners[v] = struct{}{} c.config._listeners[v] = struct{}{}
case NodeInfo: case NodeInfo: