diff --git a/core/commands2/root.go b/core/commands2/root.go index 3e711092e..450b6b7e8 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -79,6 +79,7 @@ var rootSubcommands = map[string]*cmds.Command{ "object": objectCmd, "refs": refsCmd, "id": idCmd, + "swarm": swarmCmd, } func init() { diff --git a/core/commands2/swarm.go b/core/commands2/swarm.go new file mode 100644 index 000000000..f09333f83 --- /dev/null +++ b/core/commands2/swarm.go @@ -0,0 +1,181 @@ +package commands + +import ( + "bytes" + "fmt" + "path" + + cmds "github.com/jbenet/go-ipfs/commands" + internal "github.com/jbenet/go-ipfs/core/commands2/internal" + peer "github.com/jbenet/go-ipfs/peer" + errors "github.com/jbenet/go-ipfs/util/debugerror" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" +) + +type stringList struct { + Strings []string +} + +var swarmCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "swarm inspection tool", + Synopsis: ` +ipfs swarm peers - List peers with open connections +ipfs swarm connect
- Open connection to a given peer +`, + ShortDescription: ` +ipfs swarm is a tool to manipulate the network swarm. The swarm is the +component that opens, listens for, and maintains connections to other +ipfs peers in the internet. +`, + }, + Subcommands: map[string]*cmds.Command{ + "peers": swarmPeersCmd, + "connect": swarmConnectCmd, + }, +} + +var swarmPeersCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "List peers with open connections", + ShortDescription: ` +ipfs swarm peers lists the set of peers this node is connected to. +`, + }, + Run: func(req cmds.Request) (interface{}, error) { + + log.Debug("ipfs swarm peers") + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } + + if n.Network == nil { + return nil, errNotOnline + } + + conns := n.Network.GetConnections() + addrs := make([]string, len(conns)) + for i, c := range conns { + pid := c.RemotePeer().ID() + addr := c.RemoteMultiaddr() + addrs[i] = fmt.Sprintf("%s/%s", addr, pid) + } + + return &stringList{addrs}, nil + }, + Marshalers: cmds.MarshalerMap{ + cmds.Text: stringListMarshaler, + }, + Type: &stringList{}, +} + +var swarmConnectCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Open connection to a given peer", + ShortDescription: ` +'ipfs swarm connect' opens a connection to a peer address. The address format +is an ipfs multiaddr: + +ipfs swarm connect /ip4/104.131.131.82/tcp/4001/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ +`, + }, + Arguments: []cmds.Argument{ + cmds.StringArg("address", true, true, "address of peer to connect to"), + }, + Run: func(req cmds.Request) (interface{}, error) { + ctx := context.TODO() + + log.Debug("ipfs swarm connect") + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } + + addrs, err := internal.CastToStrings(req.Arguments()) + if err != nil { + return nil, cmds.ClientError("Improperly formatted peer addresses: " + err.Error()) + } + + if n.Network == nil { + return nil, errNotOnline + } + + peers, err := peersWithAddresses(n.Peerstore, addrs) + if err != nil { + return nil, err + } + + output := make([]string, len(peers)) + for i, p := range peers { + output[i] = "connect " + p.ID().String() + + err := n.Network.DialPeer(ctx, p) + if err != nil { + output[i] += " failure: " + err.Error() + } else { + output[i] += " success" + } + } + + return &stringList{output}, nil + }, + Marshalers: cmds.MarshalerMap{ + cmds.Text: stringListMarshaler, + }, + Type: &stringList{}, +} + +func stringListMarshaler(res cmds.Response) ([]byte, error) { + list, ok := res.Output().(*stringList) + if !ok { + return nil, errors.New("failed to cast []string") + } + + var buf bytes.Buffer + for _, s := range list.Strings { + buf.Write([]byte(s)) + buf.Write([]byte("\n")) + } + return buf.Bytes(), nil +} + +// splitAddresses is a function that takes in a slice of string peer addresses +// (multiaddr + peerid) and returns slices of multiaddrs and peerids. +func splitAddresses(addrs []string) (maddrs []ma.Multiaddr, pids []peer.ID, err error) { + + maddrs = make([]ma.Multiaddr, len(addrs)) + pids = make([]peer.ID, len(addrs)) + for i, addr := range addrs { + a, err := ma.NewMultiaddr(path.Dir(addr)) + if err != nil { + return nil, nil, cmds.ClientError("invalid peer address: " + err.Error()) + } + maddrs[i] = a + pids[i] = peer.DecodePrettyID(path.Base(addr)) + } + return +} + +// peersWithAddresses is a function that takes in a slice of string peer addresses +// (multiaddr + peerid) and returns a slice of properly constructed peers +func peersWithAddresses(ps peer.Peerstore, addrs []string) ([]peer.Peer, error) { + maddrs, pids, err := splitAddresses(addrs) + if err != nil { + return nil, err + } + + peers := make([]peer.Peer, len(pids)) + for i, pid := range pids { + p, err := ps.Get(pid) + if err != nil { + return nil, err + } + + p.AddAddress(maddrs[i]) + peers[i] = p + } + return peers, nil +} diff --git a/net/interface.go b/net/interface.go index 04a74c0bf..3c0e8d204 100644 --- a/net/interface.go +++ b/net/interface.go @@ -2,6 +2,7 @@ package net import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + conn "github.com/jbenet/go-ipfs/net/conn" msg "github.com/jbenet/go-ipfs/net/message" mux "github.com/jbenet/go-ipfs/net/mux" srv "github.com/jbenet/go-ipfs/net/service" @@ -34,6 +35,9 @@ type Network interface { // GetPeerList returns the list of peers currently connected in this network. GetPeerList() []peer.Peer + // GetConnections returns the list of connections currently open in this network. + GetConnections() []conn.Conn + // GetBandwidthTotals returns the total number of bytes passed through // the network since it was instantiated GetBandwidthTotals() (uint64, uint64) diff --git a/net/net.go b/net/net.go index 4fdd7c7e7..0ee1e76b5 100644 --- a/net/net.go +++ b/net/net.go @@ -2,6 +2,7 @@ package net import ( + conn "github.com/jbenet/go-ipfs/net/conn" msg "github.com/jbenet/go-ipfs/net/message" mux "github.com/jbenet/go-ipfs/net/mux" swarm "github.com/jbenet/go-ipfs/net/swarm" @@ -99,6 +100,11 @@ func (n *IpfsNetwork) GetPeerList() []peer.Peer { return n.swarm.GetPeerList() } +// GetConnections returns the networks list of open connections +func (n *IpfsNetwork) GetConnections() []conn.Conn { + return n.swarm.Connections() +} + // GetBandwidthTotals returns the total amount of bandwidth transferred func (n *IpfsNetwork) GetBandwidthTotals() (in uint64, out uint64) { return n.muxer.GetBandwidthTotals()